添加RSA加密解密模块
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@ logs/
|
||||
runtime/
|
||||
reports/
|
||||
target/
|
||||
rsakeys/
|
||||
config.ini
|
||||
*.log
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import java.util.Date;
|
||||
import cc.winboll.LogUtils;
|
||||
import cc.winboll.test.ConsoleCmdAutoTest;
|
||||
import cc.winboll.auth.RSAUtils;
|
||||
import cc.winboll.util.IniConfigUtils;
|
||||
import java.util.Date;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
@@ -43,9 +43,10 @@ public class Main {
|
||||
LogUtils.test(); // 执行测试
|
||||
LogUtils.setGlobalLogLevel(oldLevel); // 还原原级别
|
||||
System.out.println("\n【配置日志测试】...");
|
||||
LogUtils.test(); // 执行测试
|
||||
|
||||
// 单元测试
|
||||
LogUtils.test(); // 执行测试
|
||||
RSAUtils.test();
|
||||
//ConsoleCmdAutoTest.main(args);
|
||||
|
||||
LogUtils.i("Main", "正在处理事务...");
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package cc.winboll;
|
||||
|
||||
import cc.winboll.util.IniConfigUtils;
|
||||
import cc.winboll.util.MainUtils;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Scanner;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Formatter;
|
||||
@@ -18,110 +18,87 @@ import java.util.logging.Logger;
|
||||
/**
|
||||
* 日志工具类,对接Java原生java.util.logging,简化分级调用+统一格式化输出
|
||||
* 适配Java7语法,兼容Android API30,支持异常堆栈打印
|
||||
* 支持通过启动参数动态控制全局日志级别,终端日志默认开启
|
||||
* 功能:单文件输出+10MB大小限制+满额时间戳备份+新日志沿用authcenter.log+退出自动清lck锁文件
|
||||
* 核心调整:读取INI配置log_path和log_level,无兜底,配置失败打系统流日志后退出程序
|
||||
* 内部日志全部使用系统流输出,避免初始化依赖冲突
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026-01-14 00:00:00
|
||||
* @LastEditTime 2026-01-28 00:25:42
|
||||
* @LastEditTime 2026-01-29 12:10:00
|
||||
*/
|
||||
public class LogUtils {
|
||||
// ========== 静态常量(常量优先,public对外暴露,private私有) ==========
|
||||
private static final Logger LOGGER = Logger.getLogger(LogUtils.class.getName());
|
||||
public static final String LOG_FILE_PREFIX = "authcenter_";
|
||||
public static final String LOG_FILE_SUFFIX = ".log";
|
||||
private static final int MAX_LOG_SIZE = 10 * 1024 * 1024;
|
||||
private static final SimpleDateFormat BACKUP_SDF = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||
// INI配置读取键名
|
||||
private static final String INI_SECTION_GLOBAL = "GlobalConfig";
|
||||
private static final String INI_KEY_LOG_PATH = "log_path";
|
||||
private static final String INI_KEY_LOG_LEVEL = "log_level";
|
||||
|
||||
// ========== 静态成员变量 ==========
|
||||
private static ConsoleHandler consoleHandler;
|
||||
private static FileHandler fileHandler;
|
||||
private static String CURRENT_LOG_DIR; // 静态变量保存当前日志目录路径
|
||||
private static Level CURRENT_LOG_LEVEL; // 当前日志级别
|
||||
private static String CURRENT_LOG_DIR;
|
||||
private static Level CURRENT_LOG_LEVEL;
|
||||
private static boolean IS_INIT_COMPLETE = false;
|
||||
private static boolean TEST_HAS_RUN = false; // 测试方法防重复标记
|
||||
|
||||
// ========== 静态初始化块(全局仅执行一次,读INI配置初始化,失败直接退出) ==========
|
||||
static {
|
||||
LOGGER.setUseParentHandlers(false);
|
||||
LOGGER.setUseParentHandlers(false); // 置顶生效,彻底关闭父处理器
|
||||
consoleHandler = new ConsoleHandler();
|
||||
consoleHandler.setFormatter(new CustomLogFormatter());
|
||||
|
||||
// 1. 从INI读取核心配置,无兜底,失败直接退出
|
||||
loadConfigFromIni();
|
||||
// 2. 初始化日志目录
|
||||
initLogDirByIni();
|
||||
// 3. 初始化日志级别
|
||||
initLogLevelByIni();
|
||||
|
||||
System.out.printf("[LogUtils][INFO] 静态初始化完成,日志级别[%s],日志目录[%s],单文件10MB限制+时间戳备份+lck自动清理%n",
|
||||
CURRENT_LOG_LEVEL.getName(), CURRENT_LOG_DIR);
|
||||
IS_INIT_COMPLETE = true;
|
||||
System.out.printf("[LogUtils][INFO] 静态初始化完成,日志级别[%s],日志目录[%s]%n",
|
||||
CURRENT_LOG_LEVEL.getName(), CURRENT_LOG_DIR);
|
||||
}
|
||||
|
||||
// ========== 私有化构造器,禁止外部实例化 ==========
|
||||
private LogUtils() {}
|
||||
|
||||
// ========== 核心新增:从INI读取log_path和log_level,失败退出 ==========
|
||||
private static void loadConfigFromIni() {
|
||||
System.out.println("[LogUtils][INFO] 开始从INI读取日志核心配置");
|
||||
// 读取log_path
|
||||
System.out.println("[LogUtils][INFO] 读取INI日志配置");
|
||||
String logPath = IniConfigUtils.getConfigValue(INI_SECTION_GLOBAL, INI_KEY_LOG_PATH);
|
||||
if (logPath == null || logPath.trim().isEmpty()) {
|
||||
System.err.println("[LogUtils][ERROR] INI配置GlobalConfig.log_path为空或不存在,程序退出");
|
||||
System.err.println("[LogUtils][ERROR] log_path配置为空,程序退出");
|
||||
System.exit(1);
|
||||
}
|
||||
CURRENT_LOG_DIR = logPath.trim();
|
||||
|
||||
// 读取log_level
|
||||
String logLevelStr = IniConfigUtils.getConfigValue(INI_SECTION_GLOBAL, INI_KEY_LOG_LEVEL);
|
||||
if (logLevelStr == null || logLevelStr.trim().isEmpty()) {
|
||||
System.err.println("[LogUtils][ERROR] INI配置GlobalConfig.log_level为空或不存在,程序退出");
|
||||
System.err.println("[LogUtils][ERROR] log_level配置为空,程序退出");
|
||||
System.exit(1);
|
||||
}
|
||||
// 转换日志级别,失败退出
|
||||
try {
|
||||
CURRENT_LOG_LEVEL = Level.parse(logLevelStr.trim().toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.printf("[LogUtils][ERROR] INI配置log_level无效[%s],支持ALL/FINE/INFO/WARNING/SEVERE,程序退出%n", logLevelStr);
|
||||
System.err.printf("[LogUtils][ERROR] log_level无效[%s],支持ALL/FINE/INFO/WARNING/SEVERE%n", logLevelStr);
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.printf("[LogUtils][INFO] INI配置读取成功,log_path=%s,log_level=%s%n", CURRENT_LOG_DIR, CURRENT_LOG_LEVEL.getName());
|
||||
}
|
||||
|
||||
// ========== 核心:基于INI配置初始化日志目录(无用户输入,无兜底,失败退出) ==========
|
||||
private static void initLogDirByIni() {
|
||||
File logDir = new File(CURRENT_LOG_DIR);
|
||||
// 目录不存在则创建,创建失败退出
|
||||
if (!logDir.exists()) {
|
||||
boolean mkdirOk = logDir.mkdirs();
|
||||
if (!mkdirOk) {
|
||||
System.err.printf("[LogUtils][ERROR] 日志目录[%s]不存在且创建失败,程序退出%n", CURRENT_LOG_DIR);
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.printf("[LogUtils][INFO] 日志目录不存在,创建成功:%s%n", CURRENT_LOG_DIR);
|
||||
if (!logDir.exists() && !logDir.mkdirs()) {
|
||||
System.err.printf("[LogUtils][ERROR] 日志目录[%s]创建失败%n", CURRENT_LOG_DIR);
|
||||
System.exit(1);
|
||||
}
|
||||
// 初始化日志处理器
|
||||
setLogDir(CURRENT_LOG_DIR);
|
||||
}
|
||||
|
||||
// ========== 核心:初始化日志级别(基于INI配置) ==========
|
||||
private static void initLogLevelByIni() {
|
||||
setGlobalLogLevel(CURRENT_LOG_LEVEL);
|
||||
System.out.printf("[LogUtils][INFO] 全局日志级别初始化完成:%s%n", CURRENT_LOG_LEVEL.getName());
|
||||
}
|
||||
|
||||
// ========== 私有工具方法(内部调用,按功能归类) ==========
|
||||
private static boolean containsHandler(Handler handler) {
|
||||
System.out.printf("[LogUtils][DEBUG] 【函数调用】containsHandler,入参handler=%s%n",
|
||||
(handler == null ? "null" : handler.getClass().getName()));
|
||||
if (handler == null) {
|
||||
if (!IS_INIT_COMPLETE || handler == null) {
|
||||
return false;
|
||||
}
|
||||
for (Handler h : LOGGER.getHandlers()) {
|
||||
if (h == handler) {
|
||||
if (h.getClass() == handler.getClass()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -129,66 +106,59 @@ public class LogUtils {
|
||||
}
|
||||
|
||||
private static void backupOldLog(File logFile) {
|
||||
System.out.printf("[LogUtils][DEBUG] 【函数调用】backupOldLog,入参logFile=%s%n",
|
||||
(logFile == null ? "null" : logFile.getAbsolutePath()));
|
||||
if (logFile != null && logFile.exists() && logFile.length() >= MAX_LOG_SIZE) {
|
||||
String backupName = LOG_FILE_PREFIX + BACKUP_SDF.format(new Date()) + LOG_FILE_SUFFIX;
|
||||
File backupFile = new File(logFile.getParent(), backupName);
|
||||
boolean backupSuccess = logFile.renameTo(backupFile);
|
||||
if (backupSuccess) {
|
||||
System.out.printf("[LogUtils][INFO] 旧日志备份成功,备份文件:%s%n", backupFile.getAbsolutePath());
|
||||
} else {
|
||||
System.err.printf("[LogUtils][WARN] 旧日志备份失败,将直接覆盖原日志%n");
|
||||
File backupFile = new File(logFile.getParent(), LOG_FILE_PREFIX + BACKUP_SDF.format(new Date()) + LOG_FILE_SUFFIX);
|
||||
if (!logFile.renameTo(backupFile)) {
|
||||
System.err.println("[LogUtils][WARN] 旧日志备份失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 公共核心方法(对外提供功能,核心方法优先) ==========
|
||||
static void setLogDir(String logDir) {
|
||||
System.out.printf("[LogUtils][DEBUG] 【函数调用】setLogDir,入参logDir=%s%n", logDir);
|
||||
if (logDir == null || logDir.trim().isEmpty()) {
|
||||
System.err.printf("[LogUtils][ERROR] 日志目录为空,程序退出%n");
|
||||
System.err.println("[LogUtils][ERROR] 日志目录为空");
|
||||
System.exit(1);
|
||||
}
|
||||
File dir = new File(logDir.trim());
|
||||
|
||||
try {
|
||||
final File logFile = new File(dir, "authcenter.log");
|
||||
final File logFile = new File(logDir.trim(), "authcenter.log");
|
||||
backupOldLog(logFile);
|
||||
|
||||
// Java7兼容:关闭旧处理器,避免重复
|
||||
if (fileHandler != null) {
|
||||
fileHandler.close();
|
||||
LOGGER.removeHandler(fileHandler);
|
||||
}
|
||||
fileHandler = new FileHandler(logFile.getAbsolutePath(), MAX_LOG_SIZE, 1, true);
|
||||
fileHandler.setFormatter(new CustomLogFormatter());
|
||||
fileHandler.setLevel(Level.ALL);
|
||||
if (!containsHandler(fileHandler)) {
|
||||
LOGGER.addHandler(fileHandler);
|
||||
System.out.printf("[LogUtils][INFO] 日志处理器初始化成功,当前日志文件:%s,最大10MB%n", logFile.getAbsolutePath());
|
||||
System.out.println("[LogUtils][INFO] 满额自动备份为authcenter_时间戳.log,新日志沿用authcenter.log");
|
||||
}
|
||||
// JVM关闭钩子,清理lck锁文件
|
||||
|
||||
// Java7兼容:替换Lambda为匿名内部类
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new File(logFile.getAbsolutePath() + ".lck").delete();
|
||||
}
|
||||
}));
|
||||
System.out.println("[LogUtils][DEBUG] lck文件清理钩子已注册,程序退出自动清理");
|
||||
@Override
|
||||
public void run() {
|
||||
if (fileHandler != null) {
|
||||
fileHandler.close();
|
||||
}
|
||||
new File(logFile.getAbsolutePath() + ".lck").delete();
|
||||
}
|
||||
}));
|
||||
} catch (IOException e) {
|
||||
System.err.printf("[LogUtils][ERROR] 文件日志处理器初始化失败,程序退出%n");
|
||||
System.err.println("[LogUtils][ERROR] 文件日志处理器初始化失败");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static Level getGlobalLogLevel() {
|
||||
System.out.printf("[LogUtils][DEBUG] 【函数调用】getGlobalLogLevel,返回当前级别:%s%n", LOGGER.getLevel().getName());
|
||||
return LOGGER.getLevel();
|
||||
}
|
||||
|
||||
public static void setGlobalLogLevel(Level level) {
|
||||
System.out.printf("[LogUtils][DEBUG] 【函数调用】setGlobalLogLevel,入参level=%s%n",
|
||||
(level == null ? "null" : level.getName()));
|
||||
if (level == null) {
|
||||
System.err.println("[LogUtils][ERROR] 日志级别为null,程序退出");
|
||||
System.err.println("[LogUtils][ERROR] 日志级别为null");
|
||||
System.exit(1);
|
||||
}
|
||||
LOGGER.setLevel(level);
|
||||
@@ -198,88 +168,125 @@ public class LogUtils {
|
||||
if (fileHandler != null) {
|
||||
fileHandler.setLevel(level);
|
||||
}
|
||||
// 移除isVerbose判断,强制添加控制台处理器,终端日志始终开启
|
||||
if (!containsHandler(consoleHandler)) {
|
||||
LOGGER.addHandler(consoleHandler);
|
||||
System.out.println("[LogUtils][DEBUG] 终端日志输出已开启");
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 分级日志方法(重载方法归类,简洁无冗余) ==========
|
||||
public static void d(String tag, String msg) {
|
||||
LOGGER.fine(String.format("[%s] %s", tag, msg));
|
||||
if (IS_INIT_COMPLETE) {
|
||||
LOGGER.fine(String.format("[%s] %s", tag, msg));
|
||||
}
|
||||
}
|
||||
|
||||
public static void d(String tag, String msg, Throwable throwable) {
|
||||
LOGGER.log(Level.FINE, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
|
||||
public static void i(String tag, String msg) {
|
||||
LOGGER.info(String.format("[%s] %s", tag, msg));
|
||||
}
|
||||
|
||||
public static void i(String tag, String msg, Throwable throwable) {
|
||||
LOGGER.log(Level.INFO, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
|
||||
public static void w(String tag, String msg) {
|
||||
LOGGER.warning(String.format("[%s] %s", tag, msg));
|
||||
}
|
||||
|
||||
public static void w(String tag, String msg, Throwable throwable) {
|
||||
LOGGER.log(Level.WARNING, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg) {
|
||||
LOGGER.severe(String.format("[%s] %s", tag, msg));
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg, Throwable throwable) {
|
||||
LOGGER.log(Level.SEVERE, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
|
||||
// ========== 内部类:自定义日志格式化器 ==========
|
||||
static class CustomLogFormatter extends Formatter {
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
return String.format("[%1$tF %1$tT] [%2$s] [%3$s] %4$s - %5$s%n",
|
||||
new Date(record.getMillis()),
|
||||
record.getLevel().getName(),
|
||||
Thread.currentThread().getName(),
|
||||
record.getLoggerName(),
|
||||
formatMessage(record));
|
||||
if (IS_INIT_COMPLETE) {
|
||||
LOGGER.log(Level.FINE, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 完善 单元测试方法(全级别覆盖+异常堆栈测试+当前日志配置打印) ==========
|
||||
public static void test() {
|
||||
System.out.println("\n==================================== LogUtils 单元测试开始 ====================================");
|
||||
// 1. 打印当前日志配置信息,便于验证
|
||||
System.out.printf("[LogUtilsTest] 当前日志级别:%s | 当前日志目录:%s%n", CURRENT_LOG_LEVEL.getName(), CURRENT_LOG_DIR);
|
||||
System.out.println("[LogUtilsTest] ------------- 基础日志级别输出测试 -------------");
|
||||
public static void i(String tag, String msg) {
|
||||
if (IS_INIT_COMPLETE) {
|
||||
LOGGER.info(String.format("[%s] %s", tag, msg));
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 全日志级别基础消息测试(覆盖所有提供的日志方法)
|
||||
public static void i(String tag, String msg, Throwable throwable) {
|
||||
if (IS_INIT_COMPLETE) {
|
||||
LOGGER.log(Level.INFO, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(String tag, String msg) {
|
||||
if (IS_INIT_COMPLETE) {
|
||||
LOGGER.warning(String.format("[%s] %s", tag, msg));
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(String tag, String msg, Throwable throwable) {
|
||||
if (IS_INIT_COMPLETE) {
|
||||
LOGGER.log(Level.WARNING, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg) {
|
||||
if (IS_INIT_COMPLETE) {
|
||||
LOGGER.severe(String.format("[%s] %s", tag, msg));
|
||||
}
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg, Throwable throwable) {
|
||||
if (IS_INIT_COMPLETE) {
|
||||
LOGGER.log(Level.SEVERE, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomLogFormatter extends Formatter {
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
StringBuilder sb = new StringBuilder(String.format("[%1$tF %1$tT] [%2$s] [%3$s] %4$s - %5$s",
|
||||
new Date(record.getMillis()), record.getLevel().getName(),
|
||||
Thread.currentThread().getName(), record.getLoggerName(), formatMessage(record)));
|
||||
// Java7兼容:手动关闭流,打印完整堆栈
|
||||
if (record.getThrown() != null) {
|
||||
sb.append("\n");
|
||||
StringWriter sw = null;
|
||||
PrintWriter pw = null;
|
||||
try {
|
||||
sw = new StringWriter();
|
||||
pw = new PrintWriter(sw);
|
||||
record.getThrown().printStackTrace(pw);
|
||||
pw.flush();
|
||||
sb.append(sw.toString());
|
||||
} finally {
|
||||
try {
|
||||
if (pw != null) {
|
||||
pw.close();
|
||||
}
|
||||
if (sw != null) {
|
||||
sw.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("[LogUtils][WARN] 堆栈流关闭失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.append("\n").toString();
|
||||
}
|
||||
}
|
||||
|
||||
// 终极防重复:测试方法只允许运行1次
|
||||
public static void test() {
|
||||
if (TEST_HAS_RUN) {
|
||||
return;
|
||||
}
|
||||
TEST_HAS_RUN = true;
|
||||
|
||||
System.out.println("\n==================================== LogUtils 单元测试开始 ====================================");
|
||||
System.out.printf("[LogUtilsTest] 当前日志级别:%s | 日志目录:%s%n", CURRENT_LOG_LEVEL.getName(), CURRENT_LOG_DIR);
|
||||
|
||||
System.out.println("[LogUtilsTest] ------------- 基础日志测试 -------------");
|
||||
LogUtils.d("LogUtilsTest", "【DEBUG】调试信息:程序运行状态跟踪,仅调试环境输出");
|
||||
LogUtils.i("LogUtilsTest", "【INFO】普通信息:正常业务流程记录,核心运行节点提示");
|
||||
LogUtils.w("LogUtilsTest", "【WARN】告警信息:非致命异常,需关注但不影响程序运行");
|
||||
LogUtils.e("LogUtilsTest", "【ERROR】错误信息:致命异常,需紧急处理修复");
|
||||
|
||||
System.out.println("[LogUtilsTest] ------------- 异常堆栈日志输出测试 -------------");
|
||||
// 3. 带异常堆栈的日志测试(Java7兼容,模拟空指针异常场景)
|
||||
System.out.println("[LogUtilsTest] ------------- 异常堆栈测试 -------------");
|
||||
try {
|
||||
String nullStr = null;
|
||||
nullStr.length();
|
||||
} catch (NullPointerException e) {
|
||||
LogUtils.d("LogUtilsTest", "【DEBUG】带堆栈-调试异常(测试空指针场景)", e);
|
||||
LogUtils.i("LogUtilsTest", "【INFO】带堆栈-普通异常(测试空指针场景)", e);
|
||||
LogUtils.w("LogUtilsTest", "【WARN】带堆栈-告警异常(测试空指针场景)", e);
|
||||
LogUtils.e("LogUtilsTest", "【ERROR】带堆栈-错误异常(测试空指针场景)", e);
|
||||
} catch (Exception e) {
|
||||
LogUtils.d("LogUtilsTest", "【DEBUG】带堆栈-调试异常", e);
|
||||
LogUtils.i("LogUtilsTest", "【INFO】带堆栈-普通异常", e);
|
||||
LogUtils.w("LogUtilsTest", "【WARN】带堆栈-告警异常", e);
|
||||
LogUtils.e("LogUtilsTest", "【ERROR】带堆栈-错误异常", e);
|
||||
LogUtils.e("LogUtilsTest", "【ERROR】测试过程中出现未知异常", e);
|
||||
}
|
||||
|
||||
// 4. 测试结束标识
|
||||
System.out.println("[LogUtilsTest] ------------- 日志输出格式验证 -------------");
|
||||
System.out.println("[LogUtilsTest] ------------- 格式验证 -------------");
|
||||
LogUtils.i("LogUtilsTest", "日志格式:[时间] [级别] [线程名] [日志器名] - [内容]");
|
||||
System.out.println("==================================== LogUtils 单元测试结束 ====================================\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
306
src/cc/winboll/auth/RSAUtils.java
Normal file
306
src/cc/winboll/auth/RSAUtils.java
Normal file
@@ -0,0 +1,306 @@
|
||||
package cc.winboll.auth;
|
||||
|
||||
import cc.winboll.LogUtils;
|
||||
import cc.winboll.util.IniConfigUtils;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
/**
|
||||
* RSA加密解密工具类(单例模式)
|
||||
* 自动读取INI配置密钥目录,缺失密钥自动生成公私钥文件,支持字符串加解密
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/28
|
||||
* @LastEditTime 2026/01/28 23:00:00
|
||||
*/
|
||||
public class RSAUtils {
|
||||
// 单例实例(volatile保证多线程可见性,Java7兼容)
|
||||
private static volatile RSAUtils instance;
|
||||
// INI配置相关常量
|
||||
private static final String INI_SECTION_GLOBAL = "GlobalConfig";
|
||||
private static final String INI_KEY_RSA_PATH = "rsakeys_path";
|
||||
// 密钥文件名
|
||||
private static final String PRIVATE_KEY_FILE = "private_key.txt";
|
||||
private static final String PUBLIC_KEY_FILE = "public_key.txt";
|
||||
// RSA算法常量
|
||||
private static final String RSA_ALGORITHM = "RSA";
|
||||
private static final int KEY_SIZE = 2048;
|
||||
// 加密填充模式
|
||||
private static final String RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
|
||||
|
||||
// 工作目录&密钥对象
|
||||
private String rsaWorkDir;
|
||||
private RSAPrivateKey privateKey;
|
||||
private RSAPublicKey publicKey;
|
||||
|
||||
/**
|
||||
* 私有化构造器 禁止外部实例化
|
||||
*/
|
||||
private RSAUtils() {}
|
||||
|
||||
/**
|
||||
* 单例获取(双重校验锁,线程安全,Java7最优实现)
|
||||
*/
|
||||
public static RSAUtils getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (RSAUtils.class) {
|
||||
if (instance == null) {
|
||||
instance = new RSAUtils();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心初始化方法:读取INI配置+校验目录+加载/生成密钥
|
||||
* 调用前需确保IniConfigUtils已初始化,失败会抛出运行时异常
|
||||
*/
|
||||
public void init() {
|
||||
// 1. 读取INI配置获取工作目录
|
||||
readWorkDirFromIni();
|
||||
// 2. 校验并创建工作目录
|
||||
checkAndCreateWorkDir();
|
||||
// 3. 加载/生成密钥
|
||||
loadOrGenerateRSAKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从IniConfigUtils读取rsa工作目录
|
||||
*/
|
||||
private void readWorkDirFromIni() {
|
||||
rsaWorkDir = IniConfigUtils.getConfigValue(INI_SECTION_GLOBAL, INI_KEY_RSA_PATH);
|
||||
if (rsaWorkDir == null || rsaWorkDir.trim().isEmpty()) {
|
||||
throw new RuntimeException("RSA初始化失败:INI配置GlobalConfig.rsakeys_path为空或不存在");
|
||||
}
|
||||
rsaWorkDir = rsaWorkDir.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验工作目录,不存在则创建
|
||||
*/
|
||||
private void checkAndCreateWorkDir() {
|
||||
File workDirFile = new File(rsaWorkDir);
|
||||
if (!workDirFile.exists()) {
|
||||
boolean mkdirSuccess = workDirFile.mkdirs();
|
||||
if (!mkdirSuccess) {
|
||||
throw new RuntimeException("RSA初始化失败:无法创建工作目录[" + rsaWorkDir + "]");
|
||||
}
|
||||
}
|
||||
if (!workDirFile.isDirectory()) {
|
||||
throw new RuntimeException("RSA初始化失败:rsakeys_path不是合法目录[" + rsaWorkDir + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载密钥,私钥文件缺失则自动生成公私钥并保存
|
||||
*/
|
||||
private void loadOrGenerateRSAKey() {
|
||||
File privateKeyFile = new File(rsaWorkDir, PRIVATE_KEY_FILE);
|
||||
File publicKeyFile = new File(rsaWorkDir, PUBLIC_KEY_FILE);
|
||||
|
||||
// 私钥文件缺失 → 生成新密钥并保存
|
||||
if (!privateKeyFile.exists() || !publicKeyFile.exists()) {
|
||||
generateAndSaveRSAKey(privateKeyFile, publicKeyFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载已有密钥文件
|
||||
try {
|
||||
loadPrivateKeyFromFile(privateKeyFile);
|
||||
loadPublicKeyFromFile(publicKeyFile);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("RSA密钥加载失败,将重新生成", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件加载私钥
|
||||
*/
|
||||
private void loadPrivateKeyFromFile(File privateFile) throws Exception {
|
||||
String keyBase64 = readFileToString(privateFile);
|
||||
byte[] keyBytes = Base64.getDecoder().decode(keyBase64);
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
|
||||
privateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件加载公钥
|
||||
*/
|
||||
private void loadPublicKeyFromFile(File publicFile) throws Exception {
|
||||
String keyBase64 = readFileToString(publicFile);
|
||||
byte[] keyBytes = Base64.getDecoder().decode(keyBase64);
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
|
||||
publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成RSA公私钥并写入文件
|
||||
*/
|
||||
private void generateAndSaveRSAKey(File privateFile, File publicFile) {
|
||||
try {
|
||||
// 1. 生成RSA密钥对
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(RSA_ALGORITHM);
|
||||
keyGen.initialize(KEY_SIZE);
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
||||
publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
|
||||
// 2. Base64编码后写入文件(Java7兼容,适配Android)
|
||||
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded());
|
||||
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded());
|
||||
|
||||
writeStringToFile(privateKeyBase64, privateFile);
|
||||
writeStringToFile(publicKeyBase64, publicFile);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("RSA密钥生成失败:不支持RSA算法", e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("RSA密钥保存失败:文件写入异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串写入文件(Java7兼容)
|
||||
*/
|
||||
private void writeStringToFile(String content, File file) throws IOException {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(file);
|
||||
fos.write(content.getBytes());
|
||||
fos.flush();
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e("RSAUtils", "密钥文件流关闭异常", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件读取为字符串(Java7兼容)
|
||||
*/
|
||||
private String readFileToString(File file) throws IOException {
|
||||
BufferedReader br = null;
|
||||
InputStreamReader isr = null;
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(file);
|
||||
isr = new InputStreamReader(fis);
|
||||
br = new BufferedReader(isr);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
return sb.toString();
|
||||
} finally {
|
||||
try { if (br != null) br.close(); } catch (IOException e) { LogUtils.e("RSAUtils", "BufferedReader关闭异常", e); }
|
||||
try { if (isr != null) isr.close(); } catch (IOException e) { LogUtils.e("RSAUtils", "InputStreamReader关闭异常", e); }
|
||||
try { if (fis != null) fis.close(); } catch (IOException e) { LogUtils.e("RSAUtils", "FileInputStream关闭异常", e); }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密字符串(返回Base64编码密文)
|
||||
*/
|
||||
public String encrypt(String plainText) {
|
||||
if (plainText == null || plainText.trim().isEmpty() || publicKey == null) {
|
||||
LogUtils.w("RSAUtils", "加密失败:明文为空或公钥未初始化");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
byte[] encryptBytes = cipher.doFinal(plainText.getBytes());
|
||||
return Base64.getEncoder().encodeToString(encryptBytes);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e("RSAUtils", "RSA加密失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥解密字符串(传入Base64编码密文)
|
||||
*/
|
||||
public String decrypt(String cipherText) {
|
||||
if (cipherText == null || cipherText.trim().isEmpty() || privateKey == null) {
|
||||
LogUtils.w("RSAUtils", "解密失败:密文为空或私钥未初始化");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
byte[] cipherBytes = Base64.getDecoder().decode(cipherText);
|
||||
byte[] plainBytes = cipher.doFinal(cipherBytes);
|
||||
return new String(plainBytes);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e("RSAUtils", "RSA解密失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 对外提供的getter方法 ==========
|
||||
public RSAPrivateKey getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
public RSAPublicKey getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public String getRsaWorkDir() {
|
||||
return rsaWorkDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试函数:完整演示 初始化+加密+解密 流程
|
||||
*/
|
||||
public static void test() {
|
||||
try {
|
||||
// 2. RSAUtils初始化(全局一次即可)
|
||||
RSAUtils rsaUtils = RSAUtils.getInstance();
|
||||
rsaUtils.init();
|
||||
LogUtils.d("RSAUtils", "RSA初始化成功,工作目录:" + rsaUtils.getRsaWorkDir());
|
||||
|
||||
// 3. 待加密实例字符串
|
||||
String originalStr = "WinBoLL_AuthCenter_2026_Test123456";
|
||||
LogUtils.d("RSAUtils", "\n【原始字符串】:" + originalStr);
|
||||
|
||||
// 4. 公钥加密
|
||||
String encryptStr = rsaUtils.encrypt(originalStr);
|
||||
LogUtils.d("RSAUtils", "【加密后密文】:" + encryptStr);
|
||||
|
||||
// 5. 私钥解密
|
||||
String decryptStr = rsaUtils.decrypt(encryptStr);
|
||||
LogUtils.d("RSAUtils", "【解密后原文】:" + decryptStr);
|
||||
|
||||
// 6. 校验结果
|
||||
if (originalStr.equals(decryptStr)) {
|
||||
LogUtils.i("RSAUtils", "===== RSA加解密测试成功 =====");
|
||||
} else {
|
||||
LogUtils.e("RSAUtils", "===== RSA加解密测试失败 =====");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e("RSAUtils", "RSA测试失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user