diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java b/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java index a02f6e9a..3753485a 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java @@ -14,6 +14,9 @@ import cn.dev33.satoken.error.SaErrorCode; import cn.dev33.satoken.exception.SaTokenException; import cn.dev33.satoken.json.SaJsonTemplate; import cn.dev33.satoken.json.SaJsonTemplateDefaultImpl; +import cn.dev33.satoken.log.SaLog; +import cn.dev33.satoken.log.SaLogForConsole; +import cn.dev33.satoken.log.input.SaLogInput; import cn.dev33.satoken.same.SaSameTemplate; import cn.dev33.satoken.sign.SaSignTemplate; import cn.dev33.satoken.sign.SaSignTemplateDefaultImpl; @@ -37,18 +40,27 @@ public class SaManager { */ public volatile static SaTokenConfig config; public static void setConfig(SaTokenConfig config) { - SaManager.config = config; + setConfigMethod(config); + + // 打印 banner if(config.getIsPrint()) { SaFoxUtil.printSaToken(); } + + // ## 发送日志 + SaManager.getLogInput().registerConfig(config); + // 调用一次StpUtil中的方法,保证其可以尽早的初始化 StpLogic StpUtil.getLoginType(); } + private static void setConfigMethod(SaTokenConfig config) { + SaManager.config = config; + } public static SaTokenConfig getConfig() { if (config == null) { synchronized (SaManager.class) { if (config == null) { - setConfig(SaTokenConfigFactory.createConfig()); + setConfigMethod(SaTokenConfigFactory.createConfig()); } } } @@ -60,6 +72,10 @@ public class SaManager { */ private volatile static SaTokenDao saTokenDao; public static void setSaTokenDao(SaTokenDao saTokenDao) { + setSaTokenDaoMethod(saTokenDao); + SaManager.getLogInput().registerComponent("SaTokenDao", saTokenDao); + } + private static void setSaTokenDaoMethod(SaTokenDao saTokenDao) { if((SaManager.saTokenDao instanceof SaTokenDaoDefaultImpl)) { ((SaTokenDaoDefaultImpl)SaManager.saTokenDao).endRefreshThread(); } @@ -69,7 +85,7 @@ public class SaManager { if (saTokenDao == null) { synchronized (SaManager.class) { if (saTokenDao == null) { - setSaTokenDao(new SaTokenDaoDefaultImpl()); + setSaTokenDaoMethod(new SaTokenDaoDefaultImpl()); } } } @@ -82,12 +98,13 @@ public class SaManager { private volatile static StpInterface stpInterface; public static void setStpInterface(StpInterface stpInterface) { SaManager.stpInterface = stpInterface; + SaManager.getLogInput().registerComponent("StpInterface", stpInterface); } public static StpInterface getStpInterface() { if (stpInterface == null) { synchronized (SaManager.class) { if (stpInterface == null) { - setStpInterface(new StpInterfaceDefaultImpl()); + SaManager.stpInterface = new StpInterfaceDefaultImpl(); } } } @@ -100,6 +117,7 @@ public class SaManager { private volatile static SaTokenContext saTokenContext; public static void setSaTokenContext(SaTokenContext saTokenContext) { SaManager.saTokenContext = saTokenContext; + SaManager.getLogInput().registerComponent("SaTokenContext", saTokenContext); } public static SaTokenContext getSaTokenContext() { return saTokenContext; @@ -109,11 +127,12 @@ public class SaManager { * 二级Context */ private volatile static SaTokenSecondContext saTokenSecondContext; - public static SaTokenSecondContext getSaTokenSecondContext() { - return saTokenSecondContext; - } public static void setSaTokenSecondContext(SaTokenSecondContext saTokenSecondContext) { SaManager.saTokenSecondContext = saTokenSecondContext; + SaManager.getLogInput().registerComponent("SaTokenSecondContext", saTokenSecondContext); + } + public static SaTokenSecondContext getSaTokenSecondContext() { + return saTokenSecondContext; } /** @@ -146,12 +165,13 @@ public class SaManager { private volatile static SaTempInterface saTemp; public static void setSaTemp(SaTempInterface saTemp) { SaManager.saTemp = saTemp; + SaManager.getLogInput().registerComponent("SaTempInterface", saTemp); } public static SaTempInterface getSaTemp() { if (saTemp == null) { synchronized (SaManager.class) { if (saTemp == null) { - setSaTemp(new SaTempDefaultImpl()); + SaManager.saTemp = new SaTempDefaultImpl(); } } } @@ -164,12 +184,13 @@ public class SaManager { private volatile static SaJsonTemplate saJsonTemplate; public static void setSaJsonTemplate(SaJsonTemplate saJsonTemplate) { SaManager.saJsonTemplate = saJsonTemplate; + SaManager.getLogInput().registerComponent("SaJsonTemplate", saJsonTemplate); } public static SaJsonTemplate getSaJsonTemplate() { if (saJsonTemplate == null) { synchronized (SaManager.class) { if (saJsonTemplate == null) { - setSaJsonTemplate(new SaJsonTemplateDefaultImpl()); + SaManager.saJsonTemplate = new SaJsonTemplateDefaultImpl(); } } } @@ -182,12 +203,13 @@ public class SaManager { private volatile static SaSignTemplate saSignTemplate; public static void setSaSignTemplate(SaSignTemplate saSignTemplate) { SaManager.saSignTemplate = saSignTemplate; + SaManager.getLogInput().registerComponent("SaSignTemplate", saSignTemplate); } public static SaSignTemplate getSaSignTemplate() { if (saSignTemplate == null) { synchronized (SaManager.class) { if (saSignTemplate == null) { - setSaSignTemplate(new SaSignTemplateDefaultImpl()); + SaManager.saSignTemplate = new SaSignTemplateDefaultImpl(); } } } @@ -200,18 +222,50 @@ public class SaManager { private volatile static SaSameTemplate saSameTemplate; public static void setSaSameTemplate(SaSameTemplate saSameTemplate) { SaManager.saSameTemplate = saSameTemplate; + SaManager.getLogInput().registerComponent("SaSameTemplate", saSameTemplate); } public static SaSameTemplate getSaSameTemplate() { if (saSameTemplate == null) { synchronized (SaManager.class) { if (saSameTemplate == null) { - setSaSameTemplate(new SaSameTemplate()); + SaManager.saSameTemplate = new SaSameTemplate(); } } } return saSameTemplate; } + /** + * 日志接收器 + */ + private volatile static SaLogInput logInput; + public static void setLogInput(SaLogInput logInput) { + SaManager.logInput = logInput; + SaManager.getLogInput().registerComponent("SaLogInput", logInput); + } + public static SaLogInput getLogInput() { + if (logInput == null) { + synchronized (SaManager.class) { + if (logInput == null) { + SaManager.logInput = new SaLogInput(); + } + } + } + return logInput; + } + + /** + * 日志输出器 + */ + public volatile static SaLog log = new SaLogForConsole(); + public static void setLog(SaLog log) { + SaManager.log = log; + SaManager.getLogInput().registerComponent("SaLog", log); + } + public static SaLog getLog() { + return SaManager.log; + } + /** * StpLogic集合, 记录框架所有成功初始化的StpLogic */ diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java index 6ecd1ea6..be77d923 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java @@ -2,6 +2,8 @@ package cn.dev33.satoken.config; import java.io.Serializable; +import cn.dev33.satoken.util.SaFoxUtil; + /** * Sa-Token 配置类 Model *
@@ -70,6 +72,13 @@ public class SaTokenConfig implements Serializable {
/** 是否打印操作日志 */
private Boolean isLog = false;
+ /** 日志等级(trace、debug、info、warn、error、fatal) */
+ private String logLevel = "trace";
+
+ /** 日志等级 int 值(1=trace、2=debug、3=info、4=warn、5=error、6=fatal) */
+ private int logLevelInt = 1;
+
+
/**
* jwt秘钥 (只有集成 jwt 模块时此参数才会生效)
*/
@@ -374,6 +383,40 @@ public class SaTokenConfig implements Serializable {
return this;
}
+ /**
+ * @return 日志等级(trace、debug、info、warn、error、fatal)
+ */
+ public String getLogLevel() {
+ return logLevel;
+ }
+
+ /**
+ * @param logLevel 日志等级(trace、debug、info、warn、error、fatal)
+ * @return 对象自身
+ */
+ public SaTokenConfig setLogLevel(String logLevel) {
+ this.logLevel = logLevel;
+ this.logLevelInt = SaFoxUtil.translateLogLevelToInt(logLevel);
+ return this;
+ }
+
+ /**
+ * @return 日志等级 int 值(1=trace、2=debug、3=info、4=warn、5=error、6=fatal)
+ */
+ public int getLogLevelInt() {
+ return logLevelInt;
+ }
+
+ /**
+ * @param logLevelInt 日志等级 int 值(1=trace、2=debug、3=info、4=warn、5=error、6=fatal)
+ * @return 对象自身
+ */
+ public SaTokenConfig setLogLeveInt(int logLevelInt) {
+ this.logLevelInt = logLevelInt;
+ this.logLevel = SaFoxUtil.translateLogLevelToString(logLevelInt);
+ return this;
+ }
+
/**
* @return jwt秘钥 (只有集成 jwt 模块时此参数才会生效)
*/
@@ -490,6 +533,8 @@ public class SaTokenConfig implements Serializable {
+ ", tokenPrefix=" + tokenPrefix
+ ", isPrint=" + isPrint
+ ", isLog=" + isLog
+ + ", logLevel=" + logLevel
+ + ", logLevelInt=" + logLevelInt
+ ", jwtSecretKey=" + jwtSecretKey
+ ", basic=" + basic
+ ", currDomain=" + currDomain
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenEventCenter.java b/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenEventCenter.java
index c81e5f90..9bfc7bae 100644
--- a/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenEventCenter.java
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenEventCenter.java
@@ -21,7 +21,7 @@ public class SaTokenEventCenter {
static {
// 默认添加控制台日志侦听器
- listenerList.add(new SaTokenListenerForConsolePrint());
+ listenerList.add(new SaTokenListenerForLog());
}
/**
@@ -198,7 +198,32 @@ public class SaTokenEventCenter {
listener.doUntieDisable(loginType, loginId, service);
}
}
-
+
+ /**
+ * 每次打开二级认证时触发
+ * @param loginType 账号类别
+ * @param tokenValue token值
+ * @param service 指定服务
+ * @param safeTime 认证时间,单位:秒
+ */
+ public static void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
+ for (SaTokenListener listener : listenerList) {
+ listener.doOpenSafe(loginType, tokenValue, service, safeTime);
+ }
+ }
+
+ /**
+ * 每次关闭二级认证时触发
+ * @param loginType 账号类别
+ * @param service 指定服务
+ * @param tokenValue token值
+ */
+ public static void doCloseSafe(String loginType, String tokenValue, String service) {
+ for (SaTokenListener listener : listenerList) {
+ listener.doCloseSafe(loginType, tokenValue, service);
+ }
+ }
+
/**
* 每次创建Session时触发
* @param id SessionId
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListener.java b/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListener.java
index 1ef18fe7..839d3d57 100644
--- a/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListener.java
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListener.java
@@ -60,7 +60,24 @@ public interface SaTokenListener {
* @param service 指定服务
*/
public void doUntieDisable(String loginType, Object loginId, String service);
-
+
+ /**
+ * 每次打开二级认证时触发
+ * @param loginType 账号类别
+ * @param tokenValue token值
+ * @param service 指定服务
+ * @param safeTime 认证时间,单位:秒
+ */
+ public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime);
+
+ /**
+ * 每次关闭二级认证时触发
+ * @param loginType 账号类别
+ * @param tokenValue token值
+ * @param service 指定服务
+ */
+ public void doCloseSafe(String loginType, String tokenValue, String service);
+
/**
* 每次创建Session时触发
* @param id SessionId
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForConsolePrint.java b/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForConsolePrint.java
deleted file mode 100644
index 4a7362a3..00000000
--- a/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForConsolePrint.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package cn.dev33.satoken.listener;
-
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-
-import cn.dev33.satoken.SaManager;
-import cn.dev33.satoken.stp.SaLoginModel;
-import cn.dev33.satoken.util.SaFoxUtil;
-
-/**
- * Sa-Token 侦听器实现:控制台 log 打印
- * @author kong
- *
- */
-public class SaTokenListenerForConsolePrint implements SaTokenListener {
-
- /**
- * 每次登录时触发
- */
- @Override
- public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
- println("账号[" + loginId + "]登录成功");
- }
-
- /**
- * 每次注销时触发
- */
- @Override
- public void doLogout(String loginType, Object loginId, String tokenValue) {
- println("账号[" + loginId + "]注销成功 (Token=" + tokenValue + ")");
- }
-
- /**
- * 每次被踢下线时触发
- */
- @Override
- public void doKickout(String loginType, Object loginId, String tokenValue) {
- println("账号[" + loginId + "]被踢下线 (Token=" + tokenValue + ")");
- }
-
- /**
- * 每次被顶下线时触发
- */
- @Override
- public void doReplaced(String loginType, Object loginId, String tokenValue) {
- println("账号[" + loginId + "]被顶下线 (Token=" + tokenValue + ")");
- }
-
- /**
- * 每次被封禁时触发
- */
- @Override
- public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
- Instant instant = Instant.ofEpochMilli(System.currentTimeMillis() + disableTime * 1000);
- ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());
- println("账号[" + loginId + "] " + service + " 服务被封禁,封禁等级=" + level + " (解封时间: " + SaFoxUtil.formatDate(zonedDateTime) + ")");
- }
-
- /**
- * 每次被解封时触发
- */
- @Override
- public void doUntieDisable(String loginType, Object loginId, String service) {
- println("账号[" + loginId + "] " + service + " 服务被解除封禁");
- }
-
- /**
- * 每次创建Session时触发
- */
- @Override
- public void doCreateSession(String id) {
- println("Session[" + id + "]创建成功");
- }
-
- /**
- * 每次注销Session时触发
- */
- @Override
- public void doLogoutSession(String id) {
- println("Session[" + id + "]注销成功");
- }
-
- /**
- * 每次Token续期时触发
- */
- @Override
- public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
- println("帐号[" + loginId + "],Token=" + tokenValue + " 续期timeout成功!");
- }
-
- /**
- * 日志输出的前缀
- */
- public static final String LOG_PREFIX = "SaLog -->: ";
-
- /**
- * 打印指定字符串
- * @param str 字符串
- */
- public void println(String str) {
- if(SaManager.getConfig().getIsLog()) {
- System.out.println(LOG_PREFIX + str);
- }
- }
-
-}
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForLog.java b/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForLog.java
new file mode 100644
index 00000000..75afa853
--- /dev/null
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForLog.java
@@ -0,0 +1,103 @@
+package cn.dev33.satoken.listener;
+
+import cn.dev33.satoken.SaManager;
+import cn.dev33.satoken.stp.SaLoginModel;
+
+/**
+ * Sa-Token 侦听器实现:Log 打印
+ *
+ * @author kong
+ * @since 2022-11-2
+ */
+public class SaTokenListenerForLog implements SaTokenListener {
+
+ /**
+ * 每次登录时触发
+ */
+ @Override
+ public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
+ SaManager.getLogInput().doLogin(loginType, loginId, tokenValue, loginModel);
+ }
+
+ /**
+ * 每次注销时触发
+ */
+ @Override
+ public void doLogout(String loginType, Object loginId, String tokenValue) {
+ SaManager.getLogInput().doLogout(loginType, loginId, tokenValue);
+ }
+
+ /**
+ * 每次被踢下线时触发
+ */
+ @Override
+ public void doKickout(String loginType, Object loginId, String tokenValue) {
+ SaManager.getLogInput().doKickout(loginType, loginId, tokenValue);
+ }
+
+ /**
+ * 每次被顶下线时触发
+ */
+ @Override
+ public void doReplaced(String loginType, Object loginId, String tokenValue) {
+ SaManager.getLogInput().doReplaced(loginType, loginId, tokenValue);
+ }
+
+ /**
+ * 每次被封禁时触发
+ */
+ @Override
+ public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
+ SaManager.getLogInput().doDisable(loginType, loginId, service, level, disableTime);
+ }
+
+ /**
+ * 每次被解封时触发
+ */
+ @Override
+ public void doUntieDisable(String loginType, Object loginId, String service) {
+ SaManager.getLogInput().doUntieDisable(loginType, loginId, service);
+ }
+
+ /**
+ * 每次打开二级认证时触发
+ */
+ @Override
+ public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
+ SaManager.getLogInput().doOpenSafe(loginType, tokenValue, service, safeTime);
+ }
+
+ /**
+ * 每次关闭二级认证时触发
+ */
+ @Override
+ public void doCloseSafe(String loginType, String tokenValue, String service) {
+ SaManager.getLogInput().doCloseSafe(loginType, tokenValue, service);
+ }
+
+ /**
+ * 每次创建Session时触发
+ */
+ @Override
+ public void doCreateSession(String id) {
+ SaManager.getLogInput().doCreateSession(id);
+ }
+
+ /**
+ * 每次注销Session时触发
+ */
+ @Override
+ public void doLogoutSession(String id) {
+ SaManager.getLogInput().doLogoutSession(id);
+ }
+
+ /**
+ * 每次Token续期时触发
+ */
+ @Override
+ public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
+ SaManager.getLogInput().doRenewTimeout(tokenValue, loginId, timeout);
+ }
+
+
+}
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForSimple.java b/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForSimple.java
index 78eba257..21cb87b4 100644
--- a/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForSimple.java
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForSimple.java
@@ -40,6 +40,16 @@ public class SaTokenListenerForSimple implements SaTokenListener {
@Override
public void doUntieDisable(String loginType, Object loginId, String service) {
+ }
+
+ @Override
+ public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
+
+ }
+
+ @Override
+ public void doCloseSafe(String loginType, String tokenValue, String service) {
+
}
@Override
@@ -57,4 +67,5 @@ public class SaTokenListenerForSimple implements SaTokenListener {
}
+
}
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/log/SaLog.java b/sa-token-core/src/main/java/cn/dev33/satoken/log/SaLog.java
new file mode 100644
index 00000000..769bcef8
--- /dev/null
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/log/SaLog.java
@@ -0,0 +1,53 @@
+package cn.dev33.satoken.log;
+
+/**
+ * Sa-Token 日志输出接口
+ *
+ * @author kong
+ * @since 2022-11-1
+ */
+public interface SaLog {
+
+ /**
+ * 输出 trace 日志
+ * @param str 日志内容
+ * @param args 参数列表
+ */
+ public void trace(String str, Object ...args);
+
+ /**
+ * 输出 debug 日志
+ * @param str 日志内容
+ * @param args 参数列表
+ */
+ public void debug(String str, Object ...args);
+
+ /**
+ * 输出 info 日志
+ * @param str 日志内容
+ * @param args 参数列表
+ */
+ public void info(String str, Object ...args);
+
+ /**
+ * 输出 warn 日志
+ * @param str 日志内容
+ * @param args 参数列表
+ */
+ public void warn(String str, Object ...args);
+
+ /**
+ * 输出 error 日志
+ * @param str 日志内容
+ * @param args 参数列表
+ */
+ public void error(String str, Object ...args);
+
+ /**
+ * 输出 fatal 日志
+ * @param str 日志内容
+ * @param args 参数列表
+ */
+ public void fatal(String str, Object ...args);
+
+}
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/log/SaLogForConsole.java b/sa-token-core/src/main/java/cn/dev33/satoken/log/SaLogForConsole.java
new file mode 100644
index 00000000..837a92e9
--- /dev/null
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/log/SaLogForConsole.java
@@ -0,0 +1,73 @@
+package cn.dev33.satoken.log;
+
+import cn.dev33.satoken.SaManager;
+import cn.dev33.satoken.config.SaTokenConfig;
+import cn.dev33.satoken.util.StrFormatter;
+
+/**
+ * Sa-Token 日志实现类 [控制台打印]
+ *
+ * @author kong
+ * @since 2022-11-1
+ */
+public class SaLogForConsole implements SaLog {
+
+ /**
+ * 日志输出的前缀
+ */
+ public static String LOG_PREFIX = "SaLog -->: ";
+
+ /**
+ * 日志等级
+ */
+ public static final int trace = 1;
+ public static final int debug = 2;
+ public static final int info = 3;
+ public static final int warn = 4;
+ public static final int error = 5;
+ public static final int fatal = 6;
+
+ @Override
+ public void trace(String str, Object... args) {
+ println(trace, str, args);
+ }
+
+ @Override
+ public void debug(String str, Object... args) {
+ println(debug, str, args);
+ }
+
+ @Override
+ public void info(String str, Object... args) {
+ println(info, str, args);
+ }
+
+ @Override
+ public void warn(String str, Object... args) {
+ println(warn, str, args);
+ }
+
+ @Override
+ public void error(String str, Object... args) {
+ println(error, str, args);
+ }
+
+ @Override
+ public void fatal(String str, Object... args) {
+ println(fatal, str, args);
+ }
+
+ /**
+ * 打印日志到控制台
+ * @param level 日志等级
+ * @param str 字符串
+ * @param args 参数列表
+ */
+ public void println(int level, String str, Object... args) {
+ SaTokenConfig config = SaManager.getConfig();
+ if(config.getIsLog() && level >= config.getLogLevelInt()) {
+ System.out.println(LOG_PREFIX + StrFormatter.format(str, args));
+ }
+ }
+
+}
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/log/input/SaLogInput.java b/sa-token-core/src/main/java/cn/dev33/satoken/log/input/SaLogInput.java
new file mode 100644
index 00000000..87acded0
--- /dev/null
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/log/input/SaLogInput.java
@@ -0,0 +1,160 @@
+package cn.dev33.satoken.log.input;
+
+import static cn.dev33.satoken.SaManager.log;
+
+import cn.dev33.satoken.config.SaTokenConfig;
+import cn.dev33.satoken.stp.SaLoginModel;
+import cn.dev33.satoken.stp.StpLogic;
+import cn.dev33.satoken.util.SaFoxUtil;
+
+/**
+ * Sa-Token 日志接受器
+ *
+ * @author kong
+ * @since 2022-11-1
+ */
+public class SaLogInput {
+
+ /**
+ * 账号登录
+ * @param loginType 账号类别
+ * @param loginId 账号id
+ * @param tokenValue 本次登录产生的 token 值
+ * @param loginModel 登录参数
+ */
+ public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
+ log.info("账号 {} 登录成功 (loginType={}), 会话凭证Token={}", loginId, loginType, tokenValue);
+ }
+
+ /**
+ * 每次注销时触发
+ * @param loginType 账号类别
+ * @param loginId 账号id
+ * @param tokenValue token值
+ */
+ public void doLogout(String loginType, Object loginId, String tokenValue) {
+ log.info("账号 {} 注销登录 (loginType={}), 会话凭证Token={}", loginId, loginType, tokenValue);
+ }
+
+ /**
+ * 每次被踢下线时触发
+ * @param loginType 账号类别
+ * @param loginId 账号id
+ * @param tokenValue token值
+ */
+ public void doKickout(String loginType, Object loginId, String tokenValue) {
+ log.info("账号 {} 被踢下线 (loginType={}), 会话凭证Token={}", loginId, loginType, tokenValue);
+ }
+
+ /**
+ * 每次被顶下线时触发
+ * @param loginType 账号类别
+ * @param loginId 账号id
+ * @param tokenValue token值
+ */
+ public void doReplaced(String loginType, Object loginId, String tokenValue) {
+ log.info("账号 {} 被顶下线 (loginType={}), 会话凭证Token={}", loginId, loginType, tokenValue);
+ }
+
+ /**
+ * 每次被封禁时触发
+ * @param loginType 账号类别
+ * @param loginId 账号id
+ * @param service 指定服务
+ * @param level 封禁等级
+ * @param disableTime 封禁时长,单位: 秒
+ */
+ public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
+ log.info("账号 {} [{}服务] 被封禁 (loginType={}), 封禁等级={}, 解封时间为 {}", loginId, loginType, service, level, SaFoxUtil.formatAfterDate(disableTime * 1000));
+ }
+
+ /**
+ * 每次被解封时触发
+ * @param loginType 账号类别
+ * @param loginId 账号id
+ * @param service 指定服务
+ */
+ public void doUntieDisable(String loginType, Object loginId, String service) {
+ log.info("账号 {} [{}服务] 解封成功 (loginType={})", loginId, service, loginType);
+ }
+
+ /**
+ * 每次打开二级认证时触发
+ * @param loginType 账号类别
+ * @param tokenValue token值
+ * @param service 指定服务
+ * @param safeTime 认证时间,单位:秒
+ */
+ public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
+ log.info("Token 二级认证成功, 业务标识={}, 有效期={}秒, Token值={}", service, safeTime, tokenValue);
+ }
+
+ /**
+ * 每次关闭二级认证时触发
+ * @param loginType 账号类别
+ * @param tokenValue token值
+ * @param service 指定服务
+ */
+ public void doCloseSafe(String loginType, String tokenValue, String service) {
+ log.info("Token 二级认证关闭, 业务标识={}, Token值={}", service, tokenValue);
+ }
+
+ /**
+ * 每次创建Session时触发
+ * @param id SessionId
+ */
+ public void doCreateSession(String id) {
+ log.info("SaSession [{}] 创建成功", id);
+ }
+
+ /**
+ * 每次注销Session时触发
+ * @param id SessionId
+ */
+ public void doLogoutSession(String id) {
+ log.info("SaSession [{}] 注销成功", id);
+ }
+
+ /**
+ * 每次Token续期时触发
+ *
+ * @param tokenValue token 值
+ * @param loginId 账号id
+ * @param timeout 续期时间
+ */
+ public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
+ log.info("Token 续期成功, {} 秒后到期, 帐号={}, Token值={} ", timeout, loginId, tokenValue);
+ }
+
+
+ /**
+ * 全局组件载入
+ * @param comtName 组件名称
+ * @param comtObj 组件对象
+ */
+ public void registerComponent(String comtName, Object comtObj) {
+ String canonicalName = comtObj == null ? null : comtObj.getClass().getCanonicalName();
+ log.info("全局组件 {} 载入成功: {}", comtName, canonicalName);
+ }
+
+ /**
+ * StpLogic 对象替换
+ * @param stpLogic /
+ */
+ public void replaceStpLogic(StpLogic stpLogic) {
+ if(stpLogic != null) {
+ log.info("会话组件 StpLogic(type={}) 重置成功: {}", stpLogic.getLoginType(), stpLogic.getClass());
+ }
+ }
+
+ /**
+ * 载入全局配置
+ * @param stpLogic /
+ */
+ public void registerConfig(SaTokenConfig config) {
+ if(config != null) {
+ log.info("全局配置 {} ", config);
+ }
+ }
+
+}
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java
index f2687bb6..e3f07f47 100644
--- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java
@@ -375,7 +375,7 @@ public class StpLogic {
// 写入 [token-last-activity]
setLastActivityToNow(tokenValue);
- // $$ 发布事件:账号xxx 登录成功
+ // $$ 发布事件:账号 xxx 登录成功
SaTokenEventCenter.doLogin(loginType, id, tokenValue, loginModel);
// 检查此账号会话数量是否超出最大值
@@ -551,7 +551,7 @@ public class StpLogic {
// $$ 发布事件:某某Token注销下线了
SaTokenEventCenter.doLogout(loginType, loginId, tokenValue);
-
+
// 4. 清理User-Session上的token签名 & 尝试注销User-Session
SaSession session = getSessionByLoginId(loginId, false);
if(session != null) {
@@ -2018,8 +2018,13 @@ public class StpLogic {
public void openSafe(String service, long safeTime) {
// 开启二级认证前必须处于登录状态
checkLogin();
+
// 写入key
- getSaTokenDao().set(splicingKeySafe(getTokenValueNotNull(), service), SaTokenConsts.SAFE_AUTH_SAVE_VALUE, safeTime);
+ String tokenValue = getTokenValueNotNull();
+ getSaTokenDao().set(splicingKeySafe(tokenValue, service), SaTokenConsts.SAFE_AUTH_SAVE_VALUE, safeTime);
+
+ // $$ 发布事件
+ SaTokenEventCenter.doOpenSafe(loginType, tokenValue, service, safeTime);
}
/**
@@ -2118,6 +2123,9 @@ public class StpLogic {
// 删除 key
getSaTokenDao().delete(splicingKeySafe(tokenValue, service));
+
+ // $$ 发布事件
+ SaTokenEventCenter.doCloseSafe(loginType, tokenValue, service);
}
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java
index d787b6d4..2008ce0a 100644
--- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java
@@ -36,6 +36,7 @@ public class StpUtil {
* 重置 StpLogic 对象
*
+ * 本工具类 copy 自 Hutool:
+ * https://github.com/dromara/hutool/blob/v5-master/hutool-core/src/main/java/cn/hutool/core/text/StrFormatter.java
+ *
1、更改此账户的 StpLogic 对象
*
2、put 到全局 StpLogic 集合中
+ *
3、发送日志
*
* @param newStpLogic /
*/
@@ -46,6 +47,9 @@ public class StpUtil {
// 添加到全局 StpLogic 集合中
// 以便可以通过 SaManager.getStpLogic(type) 的方式来全局获取到这个 StpLogic
SaManager.putStpLogic(newStpLogic);
+
+ // ## 发送日志
+ SaManager.getLogInput().replaceStpLogic(stpLogic);
}
/**
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java
index a9bfe20f..0d4e8e42 100644
--- a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java
@@ -4,9 +4,12 @@ import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -84,7 +87,16 @@ public class SaFoxUtil {
public static boolean isNotEmpty(Object str) {
return isEmpty(str) == false;
}
-
+
+ /**
+ * 指定数组是否为null或者空数组
+ * @param array /
+ * @return /
+ */
+ public static
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") =》 this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") =》 this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") =》 this is \a for b
+ *
+ * @param strPattern 字符串模板
+ * @param argArray 参数列表
+ * @return 结果
+ */
+ public static String format(String strPattern, Object... argArray) {
+ return formatWith(strPattern, EMPTY_JSON, argArray);
+ }
+
+ /**
+ * 格式化字符串
+ * 此方法只是简单将指定占位符 按照顺序替换为参数
+ * 如果想输出占位符使用 \\转义即可,如果想输出占位符之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "{}", "a", "b") =》 this is a for b
+ * 转义{}: format("this is \\{} for {}", "{}", "a", "b") =》 this is {} for a
+ * 转义\: format("this is \\\\{} for {}", "{}", "a", "b") =》 this is \a for b
+ *
+ * @param strPattern 字符串模板
+ * @param placeHolder 占位符,例如{}
+ * @param argArray 参数列表
+ * @return 结果
+ * @since 5.7.14
+ */
+ public static String formatWith(String strPattern, String placeHolder, Object... argArray) {
+ if (SaFoxUtil.isEmpty(strPattern) || SaFoxUtil.isEmpty(placeHolder) || SaFoxUtil.isEmpty(argArray)) {
+ return strPattern;
+ }
+ final int strPatternLength = strPattern.length();
+ final int placeHolderLength = placeHolder.length();
+
+ // 初始化定义好的长度以获得更好的性能
+ final StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
+
+ int handledPosition = 0;// 记录已经处理到的位置
+ int delimIndex;// 占位符所在位置
+ for (int argIndex = 0; argIndex < argArray.length; argIndex++) {
+ delimIndex = strPattern.indexOf(placeHolder, handledPosition);
+ if (delimIndex == -1) {// 剩余部分无占位符
+ if (handledPosition == 0) { // 不带占位符的模板直接返回
+ return strPattern;
+ }
+ // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
+ sbuf.append(strPattern, handledPosition, strPatternLength);
+ return sbuf.toString();
+ }
+
+ // 转义符
+ if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) {// 转义符
+ if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) {// 双转义符
+ // 转义符之前还有一个转义符,占位符依旧有效
+ sbuf.append(strPattern, handledPosition, delimIndex - 1);
+ sbuf.append(String.valueOf(argArray[argIndex]));
+ handledPosition = delimIndex + placeHolderLength;
+ } else {
+ // 占位符被转义
+ argIndex--;
+ sbuf.append(strPattern, handledPosition, delimIndex - 1);
+ sbuf.append(placeHolder.charAt(0));
+ handledPosition = delimIndex + 1;
+ }
+ } else {// 正常占位符
+ sbuf.append(strPattern, handledPosition, delimIndex);
+ sbuf.append(String.valueOf(argArray[argIndex]));
+ handledPosition = delimIndex + placeHolderLength;
+ }
+ }
+
+ // 加入最后一个占位符后所有的字符
+ sbuf.append(strPattern, handledPosition, strPatternLength);
+
+ return sbuf.toString();
+ }
+
+}
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/up/DisableController.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/up/DisableController.java
index 5006a0c8..cd017e9a 100644
--- a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/up/DisableController.java
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/up/DisableController.java
@@ -22,6 +22,8 @@ public class DisableController {
2、注销登录 ---- http://localhost:8081/disable/logout
3、禁用账号 ---- http://localhost:8081/disable/disable?userId=10001
4、再次访问登录接口,登录失败 ---- http://localhost:8081/disable/login?userId=10001
+ 5、解封账号 ---- http://localhost:8081/disable/untieDisable?userId=10001
+ 6、再次访问登录接口,登录成功 ---- http://localhost:8081/disable/login?userId=10001
*/
// 会话登录接口 ---- http://localhost:8081/disable/login?userId=10001
@@ -41,7 +43,7 @@ public class DisableController {
return SaResult.ok("账号退出成功");
}
- // 封禁指定账号 ---- http://localhost:8081/disable/disable
+ // 封禁指定账号 ---- http://localhost:8081/disable/disable?userId=10001
@RequestMapping("disable")
public SaResult disable(long userId) {
/*
@@ -53,4 +55,11 @@ public class DisableController {
return SaResult.ok("账号 " + userId + " 封禁成功");
}
+ // 解封指定账号 ---- http://localhost:8081/disable/untieDisable?userId=10001
+ @RequestMapping("untieDisable")
+ public SaResult untieDisable(long userId) {
+ StpUtil.untieDisable(userId);
+ return SaResult.ok("账号 " + userId + " 解封成功");
+ }
+
}
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/up/SafeAuthController.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/up/SafeAuthController.java
index e8bad9b0..34caa912 100644
--- a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/up/SafeAuthController.java
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/up/SafeAuthController.java
@@ -61,6 +61,13 @@ public class SafeAuthController {
return SaResult.error("二级认证失败");
}
+ // 手动关闭二级认证 ---- http://localhost:8081/safe/closeSafe
+ @RequestMapping("closeSafe")
+ public SaResult closeSafe() {
+ StpUtil.closeSafe();
+ return SaResult.ok();
+ }
+
// ------------------ 指定业务类型进行二级认证
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/MySaTokenListener.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/MySaTokenListener.java
index 3dd4f66f..dd4bd66d 100644
--- a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/MySaTokenListener.java
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/MySaTokenListener.java
@@ -47,7 +47,19 @@ public class MySaTokenListener implements SaTokenListener {
public void doUntieDisable(String loginType, Object loginId, String service) {
System.out.println("---------- 自定义侦听器实现 doUntieDisable");
}
+
+ /** 每次打开二级认证时触发 */
+ @Override
+ public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
+ System.out.println("---------- 自定义侦听器实现 doOpenSafe");
+ }
+ /** 每次关闭二级认证时触发 */
+ @Override
+ public void doCloseSafe(String loginType, String tokenValue, String service) {
+ System.out.println("---------- 自定义侦听器实现 doCloseSafe");
+ }
+
/** 每次创建Session时触发 */
@Override
public void doCreateSession(String id) {
@@ -65,5 +77,6 @@ public class MySaTokenListener implements SaTokenListener {
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
System.out.println("---------- 自定义侦听器实现 doRenewTimeout");
}
-
+
+
}
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/SaTokenConfigure.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/SaTokenConfigure.java
index 26da4900..9ef97a41 100644
--- a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/SaTokenConfigure.java
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/SaTokenConfigure.java
@@ -31,6 +31,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器打开注解鉴权功能
registry.addInterceptor(new SaInterceptor(handle -> {
+ // SaManager.getLog().debug("----- 请求path={} 提交token={}", SaHolder.getRequest().getRequestPath(), StpUtil.getTokenValue());
// 指定一条 match 规则
SaRouter
@@ -75,6 +76,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
// 认证函数: 每次请求执行
.setAuth(obj -> {
// System.out.println("---------- sa全局认证 " + SaHolder.getRequest().getRequestPath());
+ // SaManager.getLog().debug("----- 请求path={} 提交token={}", SaHolder.getRequest().getRequestPath(), StpUtil.getTokenValue());
// 权限校验 -- 不同模块认证不同权限
// 这里你可以写和拦截器鉴权同样的代码,不同点在于:
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/StpUserUtil.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/StpUserUtil.java
index 752b0715..3dcb0d32 100644
--- a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/StpUserUtil.java
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/StpUserUtil.java
@@ -42,6 +42,7 @@ public class StpUserUtil {
* 重置 StpLogic 对象
*
1、更改此账户的 StpLogic 对象
*
2、put 到全局 StpLogic 集合中
+ *
3、发送日志
*
* @param newStpLogic /
*/
@@ -52,6 +53,9 @@ public class StpUserUtil {
// 添加到全局 StpLogic 集合中
// 以便可以通过 SaManager.getStpLogic(type) 的方式来全局获取到这个 StpLogic
SaManager.putStpLogic(newStpLogic);
+
+ // ## 发送日志
+ SaManager.getLogInput().replaceStpLogic(stpLogic);
}
/**
diff --git a/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/satoken/StpUserUtil.java b/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/satoken/StpUserUtil.java
index 752b0715..3dcb0d32 100644
--- a/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/satoken/StpUserUtil.java
+++ b/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/satoken/StpUserUtil.java
@@ -42,6 +42,7 @@ public class StpUserUtil {
* 重置 StpLogic 对象
*
1、更改此账户的 StpLogic 对象
*
2、put 到全局 StpLogic 集合中
+ *
3、发送日志
*
* @param newStpLogic /
*/
@@ -52,6 +53,9 @@ public class StpUserUtil {
// 添加到全局 StpLogic 集合中
// 以便可以通过 SaManager.getStpLogic(type) 的方式来全局获取到这个 StpLogic
SaManager.putStpLogic(newStpLogic);
+
+ // ## 发送日志
+ SaManager.getLogInput().replaceStpLogic(stpLogic);
}
/**
diff --git a/sa-token-doc/up/global-listener.md b/sa-token-doc/up/global-listener.md
index 7564c9b0..639d4a2f 100644
--- a/sa-token-doc/up/global-listener.md
+++ b/sa-token-doc/up/global-listener.md
@@ -67,6 +67,18 @@ public class MySaTokenListener implements SaTokenListener {
System.out.println("---------- 自定义侦听器实现 doUntieDisable");
}
+ /** 每次二级认证时触发 */
+ @Override
+ public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
+ System.out.println("---------- 自定义侦听器实现 doOpenSafe");
+ }
+
+ /** 每次退出二级认证时触发 */
+ @Override
+ public void doCloseSafe(String loginType, String tokenValue, String service) {
+ System.out.println("---------- 自定义侦听器实现 doCloseSafe");
+ }
+
/** 每次创建Session时触发 */
@Override
public void doCreateSession(String id) {