Compare commits

..

39 Commits

Author SHA1 Message Date
click33
2ddfe6e50d v1.33.0 更新日志 2022-11-16 21:22:20 +08:00
click33
bd509ed75f 修复 sa-token-demo-sso3-client-nosdk 项目中单点注销 url 配置错误的问题 2022-11-16 21:19:15 +08:00
click33
b8de417d16 增加常见报错解答 2022-11-16 21:13:02 +08:00
click33
6a65b63f7e v1.33.0 更新 2022-11-16 19:52:42 +08:00
click33
5155743fb0 新增全局log模块的文档 2022-11-16 18:18:17 +08:00
click33
d2568577ba 修复工具类随机数重复的问题 2022-11-16 18:18:03 +08:00
click33
1f99a2efba Merge branch 'dev' of github.com:dromara/sa-token into dev 2022-11-16 17:44:47 +08:00
孔明
bdcaecd5bc !195 sa-token-solon-plugin:调整适配代码,允许没有sa-token配置内容。
Merge pull request !195 from 西东/dev
2022-11-16 08:06:31 +00:00
noear
59eaed193b sa-token-solon-plugin:调整适配代码,允许没有sa-token配置内容。 2022-11-16 15:38:55 +08:00
noear
c2ad8bf347 sa-token-solon-plugin:调整适配代码,允许没有sa-token配置内容(给个默认的配置实例)。 2022-11-16 15:24:22 +08:00
noear
637781415a sa-token-solon-plugin:调整适配代码,允许没有sa-token配置内容(给个默认的配置实例)。 2022-11-16 15:20:04 +08:00
孔明
234b9d4089 !192 update sa-token-doc/use/config.md.
Merge pull request !192 from AppleOfGray/N/A
2022-11-16 02:06:07 +00:00
孔明
4a418d048b !193 sa-token-solon-plugin:调整示例代码关于异常的处理
Merge pull request !193 from 西东/dev
2022-11-16 02:05:53 +00:00
noear
1e2abfaa15 sa-token-solon-plugin:调整示例代码关于异常的处理 2022-11-15 22:46:22 +08:00
AppleOfGray
1da64da031 update sa-token-doc/use/config.md.
void类型无法返回...
2022-11-15 08:41:35 +00:00
孔明
f79564220e update sa-token-doc/fun/issue-template.md.
Signed-off-by: 孔明 <2393584716@qq.com>
2022-11-15 04:31:37 +00:00
孔明
674aa8fdce !191 sa-token-solon-plugin:升级 solon 为 1.10.13
Merge pull request !191 from 西东/dev
2022-11-14 11:12:12 +00:00
noear
062af081f7 sa-token-solon-plugin:升级 solon 为 1.10.13 2022-11-14 16:46:24 +08:00
click33
5de3060efd Merge pull request #332 from gongjixiaobai/patch-1
Update sso-home-jump.md
2022-11-09 10:33:36 +08:00
gongjixiaobai
f0431e3a54 Update sso-home-jump.md
未登录应该为!
2022-11-09 10:16:08 +08:00
click33
6b2060e447 修复部分错别处 2022-11-04 17:01:46 +08:00
click33
699670627e 优化文档 2022-11-04 16:51:11 +08:00
click33
27dbb4842e 修改订阅 Bean 的方式 2022-11-03 14:23:37 +08:00
click33
b5691141ce 修改订阅 Bean 的方式 2022-11-03 14:15:41 +08:00
click33
0869d322db 修正注释 2022-11-03 14:04:32 +08:00
孔明
1c86312ed7 !190 sa-token-solon-plugin:添另 SaLog 适配支持
Merge pull request !190 from 西东/dev
2022-11-03 05:51:39 +00:00
noear
d2f0167e28 sa-token-solon-plugin:添另 SaLog 适配支持 2022-11-03 13:50:46 +08:00
click33
8f01bacd6f Merge branch 'dev' of https://gitee.com/dromara/sa-token into dev 2022-11-03 13:44:46 +08:00
click33
6dc9be8cd7 移除 SaLogInput 组件 2022-11-03 13:44:33 +08:00
孔明
ac2351fd96 !189 修改SaCookie中Set-Cookie的sameSite错误问题
Merge pull request !189 from muyuanqiang/dev
2022-11-03 03:03:42 +00:00
click33
2abd152218 全局日志模块的自动注入 2022-11-02 19:13:34 +08:00
click33
8be77d70b5 全局日志模块 2022-11-02 18:50:15 +08:00
muyuanqiang7
e22233576a fix(SaCookie): 修改SaCookie中Set-Cookie的sameSite错误问题 2022-11-02 18:41:04 +08:00
click33
cf8cd29a82 整理常见问题 2022-10-31 23:53:05 +08:00
click33
d2665036a2 重构异常状态码机制 2022-10-31 02:00:38 +08:00
click33
f21b6d8b07 优化文档样式 2022-10-29 12:15:57 +08:00
click33
838f64b06b 去除无关代码 2022-10-28 21:22:27 +08:00
click33
e5dc9165d1 新增常见问题排查 2022-10-28 18:09:44 +08:00
click33
944e7af8f3 完善赞助者名单 2022-10-28 12:16:52 +08:00
131 changed files with 2326 additions and 593 deletions

View File

@@ -1,7 +1,7 @@
<p align="center">
<img alt="logo" src="https://sa-token.cc/logo.png" width="150" height="150">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.32.0</h1>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.33.0</h1>
<h4 align="center">一个轻量级 Java 权限认证框架,让鉴权变得简单、优雅!</h4>
<p align="center">
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg?theme=gvp"></a>
@@ -184,7 +184,7 @@ Sa-OAuth2 模块分为四种授权模式,解决不同场景下的授权需求
## 交流群
QQ交流群614714762 [点击加入](https://jq.qq.com/?_wv=1027&k=F96A2mrl)
QQ交流群707350988 [点击加入](https://jq.qq.com/?_wv=1027&k=tqbzHT2D)
微信交流群:

View File

@@ -38,7 +38,7 @@
<!-- 一些属性 -->
<properties>
<revision>1.32.0</revision>
<revision>1.33.0</revision>
<jdk.version>1.8</jdk.version>
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>

View File

@@ -10,9 +10,13 @@ import cn.dev33.satoken.context.SaTokenContextDefaultImpl;
import cn.dev33.satoken.context.second.SaTokenSecondContext;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl;
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.listener.SaTokenEventCenter;
import cn.dev33.satoken.log.SaLog;
import cn.dev33.satoken.log.SaLogForConsole;
import cn.dev33.satoken.same.SaSameTemplate;
import cn.dev33.satoken.sign.SaSignTemplate;
import cn.dev33.satoken.sign.SaSignTemplateDefaultImpl;
@@ -36,18 +40,27 @@ public class SaManager {
*/
public volatile static SaTokenConfig config;
public static void setConfig(SaTokenConfig config) {
SaManager.config = config;
if(config.getIsPrint()) {
setConfigMethod(config);
// 打印 banner
if(config !=null && config.getIsPrint()) {
SaFoxUtil.printSaToken();
}
// $$ 全局事件
SaTokenEventCenter.doSetConfig(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());
}
}
}
@@ -59,6 +72,10 @@ public class SaManager {
*/
private volatile static SaTokenDao saTokenDao;
public static void setSaTokenDao(SaTokenDao saTokenDao) {
setSaTokenDaoMethod(saTokenDao);
SaTokenEventCenter.doRegisterComponent("SaTokenDao", saTokenDao);
}
private static void setSaTokenDaoMethod(SaTokenDao saTokenDao) {
if((SaManager.saTokenDao instanceof SaTokenDaoDefaultImpl)) {
((SaTokenDaoDefaultImpl)SaManager.saTokenDao).endRefreshThread();
}
@@ -68,7 +85,7 @@ public class SaManager {
if (saTokenDao == null) {
synchronized (SaManager.class) {
if (saTokenDao == null) {
setSaTokenDao(new SaTokenDaoDefaultImpl());
setSaTokenDaoMethod(new SaTokenDaoDefaultImpl());
}
}
}
@@ -81,12 +98,13 @@ public class SaManager {
private volatile static StpInterface stpInterface;
public static void setStpInterface(StpInterface stpInterface) {
SaManager.stpInterface = stpInterface;
SaTokenEventCenter.doRegisterComponent("StpInterface", stpInterface);
}
public static StpInterface getStpInterface() {
if (stpInterface == null) {
synchronized (SaManager.class) {
if (stpInterface == null) {
setStpInterface(new StpInterfaceDefaultImpl());
SaManager.stpInterface = new StpInterfaceDefaultImpl();
}
}
}
@@ -99,6 +117,7 @@ public class SaManager {
private volatile static SaTokenContext saTokenContext;
public static void setSaTokenContext(SaTokenContext saTokenContext) {
SaManager.saTokenContext = saTokenContext;
SaTokenEventCenter.doRegisterComponent("SaTokenContext", saTokenContext);
}
public static SaTokenContext getSaTokenContext() {
return saTokenContext;
@@ -108,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;
SaTokenEventCenter.doRegisterComponent("SaTokenSecondContext", saTokenSecondContext);
}
public static SaTokenSecondContext getSaTokenSecondContext() {
return saTokenSecondContext;
}
/**
@@ -145,12 +165,13 @@ public class SaManager {
private volatile static SaTempInterface saTemp;
public static void setSaTemp(SaTempInterface saTemp) {
SaManager.saTemp = saTemp;
SaTokenEventCenter.doRegisterComponent("SaTempInterface", saTemp);
}
public static SaTempInterface getSaTemp() {
if (saTemp == null) {
synchronized (SaManager.class) {
if (saTemp == null) {
setSaTemp(new SaTempDefaultImpl());
SaManager.saTemp = new SaTempDefaultImpl();
}
}
}
@@ -163,12 +184,13 @@ public class SaManager {
private volatile static SaJsonTemplate saJsonTemplate;
public static void setSaJsonTemplate(SaJsonTemplate saJsonTemplate) {
SaManager.saJsonTemplate = saJsonTemplate;
SaTokenEventCenter.doRegisterComponent("SaJsonTemplate", saJsonTemplate);
}
public static SaJsonTemplate getSaJsonTemplate() {
if (saJsonTemplate == null) {
synchronized (SaManager.class) {
if (saJsonTemplate == null) {
setSaJsonTemplate(new SaJsonTemplateDefaultImpl());
SaManager.saJsonTemplate = new SaJsonTemplateDefaultImpl();
}
}
}
@@ -181,12 +203,13 @@ public class SaManager {
private volatile static SaSignTemplate saSignTemplate;
public static void setSaSignTemplate(SaSignTemplate saSignTemplate) {
SaManager.saSignTemplate = saSignTemplate;
SaTokenEventCenter.doRegisterComponent("SaSignTemplate", saSignTemplate);
}
public static SaSignTemplate getSaSignTemplate() {
if (saSignTemplate == null) {
synchronized (SaManager.class) {
if (saSignTemplate == null) {
setSaSignTemplate(new SaSignTemplateDefaultImpl());
SaManager.saSignTemplate = new SaSignTemplateDefaultImpl();
}
}
}
@@ -199,17 +222,30 @@ public class SaManager {
private volatile static SaSameTemplate saSameTemplate;
public static void setSaSameTemplate(SaSameTemplate saSameTemplate) {
SaManager.saSameTemplate = saSameTemplate;
SaTokenEventCenter.doRegisterComponent("SaSameTemplate", saSameTemplate);
}
public static SaSameTemplate getSaSameTemplate() {
if (saSameTemplate == null) {
synchronized (SaManager.class) {
if (saSameTemplate == null) {
setSaSameTemplate(new SaSameTemplate());
SaManager.saSameTemplate = new SaSameTemplate();
}
}
}
return saSameTemplate;
}
/**
* 日志输出器
*/
public volatile static SaLog log = new SaLogForConsole();
public static void setLog(SaLog log) {
SaManager.log = log;
SaTokenEventCenter.doRegisterComponent("SaLog", log);
}
public static SaLog getLog() {
return SaManager.log;
}
/**
* StpLogic集合, 记录框架所有成功初始化的StpLogic
@@ -269,7 +305,7 @@ public class SaManager {
* (1) 从main方法里调用一次
* (2) 在自定义StpUtil类加上类似 @Component 的注解让容器启动时扫描到自动初始化
*/
throw new SaTokenException("未能获取对应StpLogictype="+ loginType);
throw new SaTokenException("未能获取对应StpLogictype="+ loginType).setCode(SaErrorCode.CODE_10002);
}
}

View File

@@ -2,6 +2,7 @@ package cn.dev33.satoken.basic;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.NotBasicAuthException;
import cn.dev33.satoken.secure.SaBase64Util;
import cn.dev33.satoken.util.SaFoxUtil;
@@ -24,7 +25,7 @@ public class SaBasicTemplate {
*/
public void throwNotBasicAuthException(String realm) {
SaHolder.getResponse().setStatus(401).setHeader("WWW-Authenticate", "Basic Realm=" + realm);
throw new NotBasicAuthException();
throw new NotBasicAuthException().setCode(SaErrorCode.CODE_10311);
}
/**

View File

@@ -2,6 +2,8 @@ package cn.dev33.satoken.config;
import java.io.Serializable;
import cn.dev33.satoken.util.SaFoxUtil;
/**
* Sa-Token 配置类 Model
* <p>
@@ -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

View File

@@ -7,6 +7,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.util.SaFoxUtil;
/**
@@ -15,7 +17,7 @@ import cn.dev33.satoken.util.SaFoxUtil;
* 用于手动读取配置文件初始化 SaTokenConfig 对象只有在非IOC环境下你才会用到此类
*
* @author kong
*
* @since 2022-10-30
*/
public class SaTokenConfigFactory {
@@ -69,7 +71,7 @@ public class SaTokenConfigFactory {
map.put(key, prop.getProperty(key));
}
} catch (IOException e) {
throw new RuntimeException("配置文件(" + propertiesPath + ")加载失败", e);
throw new SaTokenException("配置文件(" + propertiesPath + ")加载失败", e).setCode(SaErrorCode.CODE_10021);
}
return map;
}
@@ -110,7 +112,7 @@ public class SaTokenConfigFactory {
field.setAccessible(true);
field.set(obj, valueConvert);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("属性赋值出错:" + field.getName(), e);
throw new SaTokenException("属性赋值出错:" + field.getName(), e).setCode(SaErrorCode.CODE_10022);
}
}
return obj;

View File

@@ -1,9 +1,10 @@
package cn.dev33.satoken.context;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.InvalidContextException;
/**
* Sa-Token 上下文处理器 [默认实现类]
@@ -26,14 +27,14 @@ public class SaTokenContextDefaultImpl implements SaTokenContext {
/**
* 默认的错误提示语
*/
public static final String ERROR_MESSAGE = "初始化任何有效上下文处理器";
public static final String ERROR_MESSAGE = "能获取有效上下文处理器";
/**
* 获取当前请求的 [Request] 对象
*/
@Override
public SaRequest getRequest() {
throw new SaTokenException(ERROR_MESSAGE);
throw new InvalidContextException(ERROR_MESSAGE).setCode(SaErrorCode.CODE_10001);
}
/**
@@ -41,7 +42,7 @@ public class SaTokenContextDefaultImpl implements SaTokenContext {
*/
@Override
public SaResponse getResponse() {
throw new SaTokenException(ERROR_MESSAGE);
throw new InvalidContextException(ERROR_MESSAGE).setCode(SaErrorCode.CODE_10001);
}
/**
@@ -49,7 +50,7 @@ public class SaTokenContextDefaultImpl implements SaTokenContext {
*/
@Override
public SaStorage getStorage() {
throw new SaTokenException(ERROR_MESSAGE);
throw new InvalidContextException(ERROR_MESSAGE).setCode(SaErrorCode.CODE_10001);
}
/**
@@ -57,8 +58,7 @@ public class SaTokenContextDefaultImpl implements SaTokenContext {
*/
@Override
public boolean matchPath(String pattern, String path) {
throw new SaTokenException(ERROR_MESSAGE);
throw new InvalidContextException(ERROR_MESSAGE).setCode(SaErrorCode.CODE_10001);
}
}

View File

@@ -3,7 +3,8 @@ package cn.dev33.satoken.context;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.InvalidContextException;
/**
* Sa-Token 上下文处理器 [ThreadLocal版本] ---- 对象存储器
@@ -51,7 +52,7 @@ public class SaTokenContextForThreadLocalStorage {
public static Box getBoxNotNull() {
Box box = boxThreadLocal.get();
if(box == null) {
throw new SaTokenException("成功初始化上下文");
throw new InvalidContextException("能获取有效的上下文").setCode(SaErrorCode.CODE_10002);
}
return box;
};

View File

@@ -5,6 +5,7 @@ import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.util.SaFoxUtil;
@@ -233,10 +234,10 @@ public class SaCookie {
this.builde();
if(SaFoxUtil.isEmpty(name)) {
throw new SaTokenException("name不能为空");
throw new SaTokenException("name不能为空").setCode(SaErrorCode.CODE_12002);
}
if(value != null && value.indexOf(";") > -1) {
throw new SaTokenException("无效Value" + value);
throw new SaTokenException("无效Value" + value).setCode(SaErrorCode.CODE_12003);
}
// Set-Cookie: name=value; Max-Age=100000; Expires=Tue, 05-Oct-2021 20:28:17 GMT; Domain=localhost; Path=/; Secure; HttpOnly; SameSite=Lax
@@ -254,10 +255,10 @@ public class SaCookie {
}
sb.append("; Expires=" + expires);
}
if(SaFoxUtil.isEmpty(domain) == false) {
if(!SaFoxUtil.isEmpty(domain)) {
sb.append("; Domain=" + domain);
}
if(SaFoxUtil.isEmpty(path) == false) {
if(!SaFoxUtil.isEmpty(path)) {
sb.append("; Path=" + path);
}
if(secure) {
@@ -266,8 +267,8 @@ public class SaCookie {
if(httpOnly) {
sb.append("; HttpOnly");
}
if(SaFoxUtil.isEmpty(sameSite) == false) {
sb.append("; sameSite=" + sameSite);
if(!SaFoxUtil.isEmpty(sameSite)) {
sb.append("; SameSite=" + sameSite);
}
return sb.toString();

View File

@@ -1,5 +1,6 @@
package cn.dev33.satoken.context.model;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.util.SaFoxUtil;
@@ -65,7 +66,7 @@ public interface SaRequest {
public default String getParamNotNull(String name) {
String paramValue = getParam(name);
if(SaFoxUtil.isEmpty(paramValue)) {
throw new SaTokenException("缺少参数:" + name);
throw new SaTokenException("缺少参数:" + name).setCode(SaErrorCode.CODE_12001);
}
return paramValue;
}
@@ -139,8 +140,6 @@ public interface SaRequest {
* @param path 转发地址
* @return 任意值
*/
public default Object forward(String path) {
throw new SaTokenException("No implementation");
}
public Object forward(String path);
}

View File

@@ -0,0 +1,158 @@
package cn.dev33.satoken.error;
/**
* 定义所有异常细分状态码
*
* @author kong
* @since: 2022-10-30
*/
public interface SaErrorCode {
/** 代表这个异常在抛出时未指定异常细分状态码 */
public static final int CODE_UNDEFINED = -1;
// ------------
/** 未能获取有效的上下文处理器 */
public static final int CODE_10001 = 10001;
/** 未能获取有效的上下文 */
public static final int CODE_10002 = 10002;
/** JSON 转换器未实现 */
public static final int CODE_10003 = 10003;
/** 未能从全局 StpLogic 集合中找到对应 type 的 StpLogic */
public static final int CODE_10011 = 10011;
/** 指定的配置文件加载失败 */
public static final int CODE_10021 = 10021;
/** 配置文件属性无法正常读取 */
public static final int CODE_10022 = 10022;
/** 重置的侦听器集合不可以为空 */
public static final int CODE_10031 = 10031;
/** 注册的侦听器不可以为空 */
public static final int CODE_10032 = 10032;
// 1030x core模块
/** 提供的 Same-Token 是无效的 */
public static final int CODE_10301 = 10301;
/** 表示未能通过 Http Basic 认证校验 */
public static final int CODE_10311 = 10311;
/** 提供的 HttpMethod 是无效的 */
public static final int CODE_10321 = 10321;
// 1100x StpLogic
/** 未能读取到有效Token */
public static final int CODE_11001 = 11001;
/** 登录时的账号id值为空 */
public static final int CODE_11002 = 11002;
/** 更改 Token 指向的 账号Id 时账号Id值为空 */
public static final int CODE_11003 = 11003;
/** 未能读取到有效Token */
public static final int CODE_11011 = 11011;
/** Token无效 */
public static final int CODE_11012 = 11012;
/** Token已过期 */
public static final int CODE_11013 = 11013;
/** Token已被顶下线 */
public static final int CODE_11014 = 11014;
/** Token已被踢下线 */
public static final int CODE_11015 = 11015;
/** Token已临时过期 */
public static final int CODE_11016 = 11016;
/** 在未集成 sa-token-jwt 插件时调用 getExtra() 抛出异常 */
public static final int CODE_11031 = 11031;
/** 缺少指定的角色 */
public static final int CODE_11041 = 11041;
/** 缺少指定的权限 */
public static final int CODE_11051 = 11051;
/** 当前账号未通过服务封禁校验 */
public static final int CODE_11061 = 11061;
/** 提供要解禁的账号无效 */
public static final int CODE_11062 = 11062;
/** 提供要解禁的服务无效 */
public static final int CODE_11063 = 11063;
/** 提供要解禁的等级无效 */
public static final int CODE_11064 = 11064;
/** 二级认证校验未通过 */
public static final int CODE_11071 = 11071;
// ------------
/** 请求中缺少指定的参数 */
public static final int CODE_12001 = 12001;
/** 构建 Cookie 时缺少 name 参数 */
public static final int CODE_12002 = 12002;
/** 构建 Cookie 时缺少 value 参数 */
public static final int CODE_12003 = 12003;
// ------------
/** Base64 编码异常 */
public static final int CODE_12101 = 12101;
/** Base64 解码异常 */
public static final int CODE_12102 = 12102;
/** URL 编码异常 */
public static final int CODE_12103 = 12103;
/** URL 解码异常 */
public static final int CODE_12104 = 12104;
/** md5 加密异常 */
public static final int CODE_12111 = 12111;
/** sha1 加密异常 */
public static final int CODE_12112 = 12112;
/** sha256 加密异常 */
public static final int CODE_12113 = 12113;
/** AES 加密异常 */
public static final int CODE_12114 = 12114;
/** AES 解密异常 */
public static final int CODE_12115 = 12115;
/** RSA 公钥加密异常 */
public static final int CODE_12116 = 12116;
/** RSA 私钥加密异常 */
public static final int CODE_12117 = 12117;
/** RSA 公钥解密异常 */
public static final int CODE_12118 = 12118;
/** RSA 私钥解密异常 */
public static final int CODE_12119 = 12119;
}

View File

@@ -0,0 +1,24 @@
package cn.dev33.satoken.exception;
/**
* 一个异常:代表未能获取有效的上下文
*
* @author kong
* @since 2022-10-29
*/
public class InvalidContextException extends SaTokenException {
/**
* 序列化版本号
*/
private static final long serialVersionUID = 6806129545290130144L;
/**
* 一个异常:代表未能获取有效的上下文
* @param message 异常描述
*/
public InvalidContextException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,25 @@
package cn.dev33.satoken.exception;
/**
* 一个异常:代表组件或方法未被提供有效的实现
*
* @author kong
* @since 2022-10-30
*/
public class NotImplException extends SaTokenException {
/**
* 序列化版本号
*/
private static final long serialVersionUID = 6806129545290130144L;
/**
* 一个异常:代表组件或方法未被提供有效的实现
* @param message 异常描述
*/
public NotImplException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,24 @@
package cn.dev33.satoken.exception;
/**
* 一个异常:代表不是 Web 上下文
*
* @author kong
* @since 2022-10-29
*/
public class NotWebContextException extends SaTokenException {
/**
* 序列化版本号
*/
private static final long serialVersionUID = 6806129545290130144L;
/**
* 一个异常:代表不是 Web 上下文
* @param message 异常描述
*/
public NotWebContextException(String message) {
super(message);
}
}

View File

@@ -1,14 +0,0 @@
package cn.dev33.satoken.exception;
/**
* 定义所有异常细分状态码
*
* @author kong
* @since: 2022-4-25
*/
public class SaExceptionCode {
/** 代表这个异常在抛出时未指定异常细分状态码 */
public static final int CODE_UNDEFINED = -1;
}

View File

@@ -1,5 +1,6 @@
package cn.dev33.satoken.exception;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.util.SaFoxUtil;
/**
@@ -19,7 +20,7 @@ public class SaTokenException extends RuntimeException {
/**
* 异常细分状态码
*/
private int code = SaExceptionCode.CODE_UNDEFINED;
private int code = SaErrorCode.CODE_UNDEFINED;
/**
* 构建一个异常
@@ -93,8 +94,9 @@ public class SaTokenException extends RuntimeException {
* 如果flag==true则抛出message异常
* @param flag 标记
* @param message 异常信息
* @param code 异常细分状态码
*/
public static void throwBy(boolean flag, String message) {
public static void throwBy(boolean flag, String message, int code) {
if(flag) {
throw new SaTokenException(message);
}
@@ -104,10 +106,11 @@ public class SaTokenException extends RuntimeException {
* 如果value==null或者isEmpty则抛出message异常
* @param value 值
* @param message 异常信息
* @param code 异常细分状态码
*/
public static void throwByNull(Object value, String message) {
public static void throwByNull(Object value, String message, int code) {
if(SaFoxUtil.isEmpty(value)) {
throw new SaTokenException(message);
throw new SaTokenException(message).setCode(code);
}
}

View File

@@ -2,7 +2,8 @@ package cn.dev33.satoken.json;
import java.util.Map;
import cn.dev33.satoken.exception.ApiDisabledException;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.NotImplException;
/**
* JSON 相关操作接口
@@ -19,7 +20,7 @@ public class SaJsonTemplateDefaultImpl implements SaJsonTemplate {
*/
@Override
public String toJsonString(Object obj) {
throw new ApiDisabledException(ERROR_MESSAGE);
throw new NotImplException(ERROR_MESSAGE).setCode(SaErrorCode.CODE_10003);
}
/**
@@ -27,7 +28,7 @@ public class SaJsonTemplateDefaultImpl implements SaJsonTemplate {
*/
@Override
public Map<String, Object> parseJsonToMap(String jsonStr) {
throw new ApiDisabledException(ERROR_MESSAGE);
throw new NotImplException(ERROR_MESSAGE).setCode(SaErrorCode.CODE_10003);
};
}

View File

@@ -3,8 +3,11 @@ package cn.dev33.satoken.listener;
import java.util.ArrayList;
import java.util.List;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpLogic;
/**
* Sa-Token 事件中心 事件发布器
@@ -20,7 +23,7 @@ public class SaTokenEventCenter {
static {
// 默认添加控制台日志侦听器
listenerList.add(new SaTokenListenerForConsolePrint());
listenerList.add(new SaTokenListenerForLog());
}
/**
@@ -37,7 +40,7 @@ public class SaTokenEventCenter {
*/
public static void setListenerList(List<SaTokenListener> listenerList) {
if(listenerList == null) {
throw new SaTokenException("重置的侦听器集合不可以为空");
throw new SaTokenException("重置的侦听器集合不可以为空").setCode(SaErrorCode.CODE_10031);
}
SaTokenEventCenter.listenerList = listenerList;
}
@@ -48,7 +51,7 @@ public class SaTokenEventCenter {
*/
public static void registerListener(SaTokenListener listener) {
if(listener == null) {
throw new SaTokenException("注册的侦听器不可以为空");
throw new SaTokenException("注册的侦听器不可以为空").setCode(SaErrorCode.CODE_10032);
}
listenerList.add(listener);
}
@@ -59,11 +62,11 @@ public class SaTokenEventCenter {
*/
public static void registerListenerList(List<SaTokenListener> listenerList) {
if(listenerList == null) {
throw new SaTokenException("注册的侦听器不可以为空");
throw new SaTokenException("注册的侦听器集合不可以为空").setCode(SaErrorCode.CODE_10031);
}
for (SaTokenListener listener : listenerList) {
if(listener == null) {
throw new SaTokenException("注册的侦听器不可以为空");
throw new SaTokenException("注册的侦听器不可以为空").setCode(SaErrorCode.CODE_10032);
}
}
SaTokenEventCenter.listenerList.addAll(listenerList);
@@ -197,7 +200,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
@@ -231,4 +259,36 @@ public class SaTokenEventCenter {
}
}
/**
* 全局组件载入
* @param comtName 组件名称
* @param comtObj 组件对象
*/
public static void doRegisterComponent(String comtName, Object comtObj) {
for (SaTokenListener listener : listenerList) {
listener.doRegisterComponent(comtName, comtObj);
}
}
/**
* StpLogic 对象替换
* @param stpLogic /
*/
public static void doSetStpLogic(StpLogic stpLogic) {
for (SaTokenListener listener : listenerList) {
listener.doSetStpLogic(stpLogic);
}
}
/**
* 载入全局配置
* @param config /
*/
public static void doSetConfig(SaTokenConfig config) {
for (SaTokenListener listener : listenerList) {
listener.doSetConfig(config);
}
}
}

View File

@@ -1,6 +1,8 @@
package cn.dev33.satoken.listener;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpLogic;
/**
* Sa-Token 侦听器
@@ -60,7 +62,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
@@ -81,5 +100,24 @@ public interface SaTokenListener {
* @param timeout 续期时间
*/
public void doRenewTimeout(String tokenValue, Object loginId, long timeout);
/**
* 全局组件载入
* @param comtName 组件名称
* @param comtObj 组件对象
*/
public default void doRegisterComponent(String comtName, Object comtObj) {}
/**
* StpLogic 对象替换
* @param stpLogic /
*/
public default void doSetStpLogic(StpLogic stpLogic) {}
/**
* 载入全局配置
* @param config /
*/
public default void doSetConfig(SaTokenConfig config) {}
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,140 @@
package cn.dev33.satoken.listener;
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 侦听器实现Log 打印
*
* @author kong
* @since 2022-11-2
*/
public class SaTokenListenerForLog implements SaTokenListener {
/**
* 每次登录时触发
*/
@Override
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
log.info("账号 {} 登录成功 (loginType={}), 会话凭证Token={}", loginId, loginType, tokenValue);
}
/**
* 每次注销时触发
*/
@Override
public void doLogout(String loginType, Object loginId, String tokenValue) {
log.info("账号 {} 注销登录 (loginType={}), 会话凭证Token={}", loginId, loginType, tokenValue);
}
/**
* 每次被踢下线时触发
*/
@Override
public void doKickout(String loginType, Object loginId, String tokenValue) {
log.info("账号 {} 被踢下线 (loginType={}), 会话凭证Token={}", loginId, loginType, tokenValue);
}
/**
* 每次被顶下线时触发
*/
@Override
public void doReplaced(String loginType, Object loginId, String tokenValue) {
log.info("账号 {} 被顶下线 (loginType={}), 会话凭证Token={}", loginId, loginType, tokenValue);
}
/**
* 每次被封禁时触发
*/
@Override
public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
log.info("账号 {} [{}服务] 被封禁 (loginType={}), 封禁等级={}, 解封时间为 {}", loginId, loginType, service, level, SaFoxUtil.formatAfterDate(disableTime * 1000));
}
/**
* 每次被解封时触发
*/
@Override
public void doUntieDisable(String loginType, Object loginId, String service) {
log.info("账号 {} [{}服务] 解封成功 (loginType={})", loginId, service, loginType);
}
/**
* 每次打开二级认证时触发
*/
@Override
public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
log.info("Token 二级认证成功, 业务标识={}, 有效期={}秒, Token值={}", service, safeTime, tokenValue);
}
/**
* 每次关闭二级认证时触发
*/
@Override
public void doCloseSafe(String loginType, String tokenValue, String service) {
log.info("Token 二级认证关闭, 业务标识={}, Token值={}", service, tokenValue);
}
/**
* 每次创建Session时触发
*/
@Override
public void doCreateSession(String id) {
log.info("SaSession [{}] 创建成功", id);
}
/**
* 每次注销Session时触发
*/
@Override
public void doLogoutSession(String id) {
log.info("SaSession [{}] 注销成功", id);
}
/**
* 每次Token续期时触发
*/
@Override
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
log.info("Token 续期成功, {} 秒后到期, 帐号={}, Token值={} ", timeout, loginId, tokenValue);
}
/**
* 全局组件载入
* @param comtName 组件名称
* @param comtObj 组件对象
*/
@Override
public void doRegisterComponent(String comtName, Object comtObj) {
String canonicalName = comtObj == null ? null : comtObj.getClass().getCanonicalName();
log.info("全局组件 {} 载入成功: {}", comtName, canonicalName);
}
/**
* StpLogic 对象替换
* @param stpLogic /
*/
@Override
public void doSetStpLogic(StpLogic stpLogic) {
if(stpLogic != null) {
log.info("会话组件 StpLogic(type={}) 重置成功: {}", stpLogic.getLoginType(), stpLogic.getClass());
}
}
/**
* 载入全局配置
* @param config /
*/
@Override
public void doSetConfig(SaTokenConfig config) {
if(config != null) {
log.info("全局配置 {} ", config);
}
}
}

View File

@@ -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 {
}
}

View File

@@ -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);
}

View File

@@ -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));
}
}
}

View File

@@ -3,6 +3,7 @@ package cn.dev33.satoken.router;
import java.util.HashMap;
import java.util.Map;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.SaTokenException;
/**
@@ -37,11 +38,11 @@ public enum SaHttpMethod {
*/
public static SaHttpMethod toEnum(String method) {
if(method == null) {
throw new SaTokenException("无效Method" + method);
throw new SaTokenException("无效Method" + method).setCode(SaErrorCode.CODE_10321);
}
SaHttpMethod reqMethod = map.get(method.toUpperCase());
if(reqMethod == null) {
throw new SaTokenException("无效Method" + method);
throw new SaTokenException("无效Method" + method).setCode(SaErrorCode.CODE_10321);
}
return reqMethod;
}

View File

@@ -2,6 +2,7 @@ package cn.dev33.satoken.same;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.SameTokenInvalidException;
import cn.dev33.satoken.util.SaFoxUtil;
@@ -57,7 +58,7 @@ public class SaSameTemplate {
public void checkToken(String token) {
if(isValid(token) == false) {
token = (token == null ? "" : token);
throw new SameTokenInvalidException("无效Same-Token" + token);
throw new SameTokenInvalidException("无效Same-Token" + token).setCode(SaErrorCode.CODE_10301);
}
}

View File

@@ -3,6 +3,9 @@ package cn.dev33.satoken.secure;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.SaTokenException;
/**
* Sa-Token Base64工具类
* @author kong
@@ -40,7 +43,7 @@ public class SaBase64Util {
try {
return encoder.encodeToString(text.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12101);
}
}
@@ -53,7 +56,7 @@ public class SaBase64Util {
try {
return new String(decoder.decode(base64Text), "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12101);
}
}

View File

@@ -1,6 +1,5 @@
package cn.dev33.satoken.secure;
import java.security.InvalidParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -23,6 +22,7 @@ import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.SaTokenException;
/**
@@ -70,7 +70,7 @@ public class SaSecureUtil {
}
return new String(strA);
} catch (Exception e) {
throw new SaTokenException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12111);
}
}
@@ -98,7 +98,7 @@ public class SaSecureUtil {
}
return new String(chs);
} catch (Exception e) {
throw new SaTokenException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12112);
}
}
@@ -127,7 +127,7 @@ public class SaSecureUtil {
return builder.toString();
} catch (Exception e) {
throw new SaTokenException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12113);
}
}
@@ -164,7 +164,7 @@ public class SaSecureUtil {
byte[] result = cipher.doFinal(byteContent);
return encoder.encodeToString(result);
} catch (Exception e) {
throw new SaTokenException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12114);
}
}
@@ -181,7 +181,7 @@ public class SaSecureUtil {
byte[] result = cipher.doFinal(decoder.decode(text));
return new String(result, "utf-8");
} catch (Exception e) {
throw new SaTokenException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12115);
}
}
@@ -220,15 +220,9 @@ public class SaSecureUtil {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
KeyPair keyPair;
try {
keyPairGenerator.initialize(KEY_SIZE,
new SecureRandom(UUID.randomUUID().toString().replaceAll("-", "").getBytes()));
keyPair = keyPairGenerator.generateKeyPair();
} catch (InvalidParameterException e) {
throw e;
} catch (NullPointerException e) {
throw e;
}
keyPairGenerator.initialize(KEY_SIZE,
new SecureRandom(UUID.randomUUID().toString().replaceAll("-", "").getBytes()));
keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
@@ -261,7 +255,7 @@ public class SaSecureUtil {
}
return stringBuffer.toString();
} catch (Exception e) {
throw new SaTokenException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12116);
}
}
@@ -286,7 +280,7 @@ public class SaSecureUtil {
}
return stringBuffer.toString();
} catch (Exception e) {
throw new SaTokenException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12117);
}
}
@@ -313,7 +307,7 @@ public class SaSecureUtil {
}
return stringBuffer.toString();
} catch (Exception e) {
throw new SaTokenException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12118);
}
}
@@ -339,7 +333,7 @@ public class SaSecureUtil {
}
return stringBuffer.toString();
} catch (Exception e) {
throw new SaTokenException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12119);
}
}

View File

@@ -20,6 +20,7 @@ import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.ApiDisabledException;
import cn.dev33.satoken.exception.DisableServiceException;
import cn.dev33.satoken.exception.NotLoginException;
@@ -255,7 +256,7 @@ public class StpLogic {
public String getTokenValueNotNull(){
String tokenValue = getTokenValue();
if(SaFoxUtil.isEmpty(tokenValue)) {
throw new SaTokenException("未能读取到有效Token");
throw new SaTokenException("未能读取到有效Token").setCode(SaErrorCode.CODE_11001);
}
return tokenValue;
}
@@ -351,7 +352,7 @@ public class StpLogic {
public String createLoginSession(Object id, SaLoginModel loginModel) {
// ------ 前置检查
SaTokenException.throwByNull(id, "账号id不能为空");
SaTokenException.throwByNull(id, "账号id不能为空", SaErrorCode.CODE_11002);
// ------ 1、初始化 loginModel
SaTokenConfig config = getConfig();
@@ -374,7 +375,7 @@ public class StpLogic {
// 写入 [token-last-activity]
setLastActivityToNow(tokenValue);
// $$ 发布事件账号xxx 登录成功
// $$ 发布事件:账号 xxx 登录成功
SaTokenEventCenter.doLogin(loginType, id, tokenValue, loginModel);
// 检查此账号会话数量是否超出最大值
@@ -550,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) {
@@ -677,24 +678,24 @@ public class StpLogic {
// 如果获取不到token则抛出: 无token
String tokenValue = getTokenValue();
if(tokenValue == null) {
throw NotLoginException.newInstance(loginType, NotLoginException.NOT_TOKEN);
throw NotLoginException.newInstance(loginType, NotLoginException.NOT_TOKEN).setCode(SaErrorCode.CODE_11011);
}
// 查找此token对应loginId, 如果找不到则抛出无效token
String loginId = getLoginIdNotHandle(tokenValue);
if(loginId == null) {
throw NotLoginException.newInstance(loginType, NotLoginException.INVALID_TOKEN, tokenValue);
throw NotLoginException.newInstance(loginType, NotLoginException.INVALID_TOKEN, tokenValue).setCode(SaErrorCode.CODE_11012);
}
// 如果是已经过期,则抛出:已经过期
if(loginId.equals(NotLoginException.TOKEN_TIMEOUT)) {
throw NotLoginException.newInstance(loginType, NotLoginException.TOKEN_TIMEOUT, tokenValue);
throw NotLoginException.newInstance(loginType, NotLoginException.TOKEN_TIMEOUT, tokenValue).setCode(SaErrorCode.CODE_11013);
}
// 如果是已经被顶替下去了, 则抛出:已被顶下线
if(loginId.equals(NotLoginException.BE_REPLACED)) {
throw NotLoginException.newInstance(loginType, NotLoginException.BE_REPLACED, tokenValue);
throw NotLoginException.newInstance(loginType, NotLoginException.BE_REPLACED, tokenValue).setCode(SaErrorCode.CODE_11014);
}
// 如果是已经被踢下线了, 则抛出:已被踢下线
if(loginId.equals(NotLoginException.KICK_OUT)) {
throw NotLoginException.newInstance(loginType, NotLoginException.KICK_OUT, tokenValue);
throw NotLoginException.newInstance(loginType, NotLoginException.KICK_OUT, tokenValue).setCode(SaErrorCode.CODE_11015);
}
// 检查是否已经 [临时过期]
checkActivityTimeout(tokenValue);
@@ -817,7 +818,7 @@ public class StpLogic {
* @return 对应的扩展数据
*/
public Object getExtra(String key) {
throw new ApiDisabledException();
throw new ApiDisabledException().setCode(SaErrorCode.CODE_11031);
}
/**
@@ -827,7 +828,7 @@ public class StpLogic {
* @return 对应的扩展数据
*/
public Object getExtra(String tokenValue, String key) {
throw new ApiDisabledException();
throw new ApiDisabledException().setCode(SaErrorCode.CODE_11031);
}
@@ -853,7 +854,7 @@ public class StpLogic {
* @param loginId 新的账号Id值
*/
public void updateTokenToIdMapping(String tokenValue, Object loginId) {
SaTokenException.throwBy(SaFoxUtil.isEmpty(loginId), "LoginId 不能为空");
SaTokenException.throwBy(SaFoxUtil.isEmpty(loginId), "LoginId 不能为空", SaErrorCode.CODE_11003);
getSaTokenDao().update(splicingKeyTokenValue(tokenValue), loginId.toString());
}
/**
@@ -1096,7 +1097,7 @@ public class StpLogic {
}
// -2 代表已过期,抛出异常
if(timeout == SaTokenDao.NOT_VALUE_EXPIRE) {
throw NotLoginException.newInstance(loginType, NotLoginException.TOKEN_TIMEOUT, tokenValue);
throw NotLoginException.newInstance(loginType, NotLoginException.TOKEN_TIMEOUT, tokenValue).setCode(SaErrorCode.CODE_11016);
}
// --- 至此,验证已通过
@@ -1353,7 +1354,7 @@ public class StpLogic {
*/
public void checkRole(String role) {
if(hasRole(role) == false) {
throw new NotRoleException(role, this.loginType);
throw new NotRoleException(role, this.loginType).setCode(SaErrorCode.CODE_11041);
}
}
@@ -1366,7 +1367,7 @@ public class StpLogic {
List<String> roleList = getRoleList(loginId);
for (String role : roleArray) {
if(!hasElement(roleList, role)) {
throw new NotRoleException(role, this.loginType);
throw new NotRoleException(role, this.loginType).setCode(SaErrorCode.CODE_11041);
}
}
}
@@ -1385,7 +1386,7 @@ public class StpLogic {
}
}
if(roleArray.length > 0) {
throw new NotRoleException(roleArray[0], this.loginType);
throw new NotRoleException(roleArray[0], this.loginType).setCode(SaErrorCode.CODE_11041);
}
}
@@ -1466,7 +1467,7 @@ public class StpLogic {
*/
public void checkPermission(String permission) {
if(hasPermission(permission) == false) {
throw new NotPermissionException(permission, this.loginType);
throw new NotPermissionException(permission, this.loginType).setCode(SaErrorCode.CODE_11051);
}
}
@@ -1479,7 +1480,7 @@ public class StpLogic {
List<String> permissionList = getPermissionList(loginId);
for (String permission : permissionArray) {
if(!hasElement(permissionList, permission)) {
throw new NotPermissionException(permission, this.loginType);
throw new NotPermissionException(permission, this.loginType).setCode(SaErrorCode.CODE_11051);
}
}
}
@@ -1498,7 +1499,7 @@ public class StpLogic {
}
}
if(permissionArray.length > 0) {
throw new NotPermissionException(permissionArray[0], this.loginType);
throw new NotPermissionException(permissionArray[0], this.loginType).setCode(SaErrorCode.CODE_11051);
}
}
@@ -1808,10 +1809,10 @@ public class StpLogic {
public void untieDisable(Object loginId, String... services) {
// 空值检查
if(SaFoxUtil.isEmpty(loginId)) {
throw new SaTokenException("请提供要解禁的账号");
throw new SaTokenException("请提供要解禁的账号").setCode(SaErrorCode.CODE_11062);
}
if(services == null || services.length == 0) {
throw new SaTokenException("请提供要解禁的服务");
throw new SaTokenException("请提供要解禁的服务").setCode(SaErrorCode.CODE_11063);
}
for (String service : services) {
@@ -1846,13 +1847,13 @@ public class StpLogic {
public void disableLevel(Object loginId, String service, int level, long time) {
// 空值检查
if(SaFoxUtil.isEmpty(loginId)) {
throw new SaTokenException("请提供要封禁的账号");
throw new SaTokenException("请提供要封禁的账号").setCode(SaErrorCode.CODE_11062);
}
if(SaFoxUtil.isEmpty(service)) {
throw new SaTokenException("请提供要封禁的服务");
throw new SaTokenException("请提供要封禁的服务").setCode(SaErrorCode.CODE_11063);
}
if(level < SaTokenConsts.MIN_DISABLE_LEVEL) {
throw new SaTokenException("封禁等级不可以小于最小值:" + SaTokenConsts.MIN_DISABLE_LEVEL);
throw new SaTokenException("封禁等级不可以小于最小值:" + SaTokenConsts.MIN_DISABLE_LEVEL).setCode(SaErrorCode.CODE_11064);
}
// 标注为已被封禁
@@ -1917,7 +1918,7 @@ public class StpLogic {
// s2. 检测被封禁的等级是否达到指定级别
Integer disableLevel = SaFoxUtil.getValueByType(value, int.class);
if(disableLevel >= level) {
throw new DisableServiceException(loginType, loginId, service, disableLevel, level, getDisableTime(loginId, service));
throw new DisableServiceException(loginType, loginId, service, disableLevel, level, getDisableTime(loginId, service)).setCode(SaErrorCode.CODE_11061);
}
}
@@ -2017,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);
}
/**
@@ -2069,7 +2075,7 @@ public class StpLogic {
public void checkSafe(String service) {
String tokenValue = getTokenValue();
if (isSafe(tokenValue, service) == false) {
throw new NotSafeException(loginType, tokenValue, service);
throw new NotSafeException(loginType, tokenValue, service).setCode(SaErrorCode.CODE_11071);
}
}
@@ -2117,6 +2123,9 @@ public class StpLogic {
// 删除 key
getSaTokenDao().delete(splicingKeySafe(tokenValue, service));
// $$ 发布事件
SaTokenEventCenter.doCloseSafe(loginType, tokenValue, service);
}

View File

@@ -4,6 +4,7 @@ import java.util.List;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.fun.SaFunction;
import cn.dev33.satoken.listener.SaTokenEventCenter;
import cn.dev33.satoken.session.SaSession;
/**
@@ -36,6 +37,7 @@ public class StpUtil {
* 重置 StpLogic 对象
* <br> 1、更改此账户的 StpLogic 对象
* <br> 2、put 到全局 StpLogic 集合中
* <br> 3、发送日志
*
* @param newStpLogic /
*/
@@ -46,6 +48,9 @@ public class StpUtil {
// 添加到全局 StpLogic 集合中
// 以便可以通过 SaManager.getStpLogic(type) 的方式来全局获取到这个 StpLogic
SaManager.putStpLogic(newStpLogic);
// $$ 全局事件
SaTokenEventCenter.doSetStpLogic(stpLogic);
}
/**

View File

@@ -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;
@@ -15,6 +18,7 @@ import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Pattern;
import cn.dev33.satoken.error.SaErrorCode;
import cn.dev33.satoken.exception.SaTokenException;
/**
@@ -24,10 +28,6 @@ import cn.dev33.satoken.exception.SaTokenException;
*
*/
public class SaFoxUtil {
/**
* 可以减少线程争用的随机对象
*/
private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
private SaFoxUtil() {
}
@@ -60,7 +60,7 @@ public class SaFoxUtil {
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = RANDOM.nextInt(62);
int number = ThreadLocalRandom.current().nextInt(62);
sb.append(str.charAt(number));
}
return sb.toString();
@@ -83,7 +83,17 @@ public class SaFoxUtil {
public static boolean isNotEmpty(Object str) {
return isEmpty(str) == false;
}
/**
* 指定数组是否为null或者空数组
* @param <T> /
* @param array /
* @return /
*/
public static <T> boolean isEmpty(T[] array) {
return array == null || array.length == 0;
}
/**
* 比较两个对象是否相等
* @param a 第一个对象
@@ -100,7 +110,7 @@ public class SaFoxUtil {
* @return 随机字符串
*/
public static String getMarking28() {
return System.currentTimeMillis() + "" + RANDOM.nextInt(Integer.MAX_VALUE);
return System.currentTimeMillis() + "" + ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
}
/**
@@ -120,7 +130,18 @@ public class SaFoxUtil {
public static String formatDate(ZonedDateTime zonedDateTime) {
return zonedDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
/**
* 指定毫秒后的时间(格式化 yyyy-MM-dd HH:mm:ss
* @param ms 指定毫秒后
* @return 格式化后的时间
*/
public static String formatAfterDate(long ms) {
Instant instant = Instant.ofEpochMilli(System.currentTimeMillis() + ms);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());
return formatDate(zonedDateTime);
}
/**
* 从集合里查询数据
*
@@ -451,7 +472,7 @@ public class SaFoxUtil {
try {
return URLEncoder.encode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new SaTokenException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12103);
}
}
@@ -464,7 +485,7 @@ public class SaFoxUtil {
try {
return URLDecoder.decode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new SaTokenException(e);
throw new SaTokenException(e).setCode(SaErrorCode.CODE_12104);
}
}
@@ -518,7 +539,7 @@ public class SaFoxUtil {
}
/**
* Array 转 String按照逗号切割
* Array 转 String按照逗号连接
* @param arr 数组
* @return 字符串
*/
@@ -551,7 +572,31 @@ public class SaFoxUtil {
return list;
}
public static List<String> logLevelList = Arrays.asList("", "trace", "debug", "info", "warn", "error", "fatal");
/**
* 将日志等级从 String 格式转化为 int 格式
* @param level /
* @return /
*/
public static int translateLogLevelToInt(String level) {
int levelInt = logLevelList.indexOf(level);
if(levelInt <= 0 || levelInt >= logLevelList.size()) {
levelInt = 1;
}
return levelInt;
}
/**
* 将日志等级从 String 格式转化为 int 格式
* @param level /
* @return /
*/
public static String translateLogLevelToString(int level) {
if(level <= 0 || level >= logLevelList.size()) {
level = 1;
}
return logLevelList.get(level);
}
}

View File

@@ -15,7 +15,7 @@ public class SaTokenConsts {
/**
* Sa-Token 当前版本号
*/
public static final String VERSION_NO = "v1.32.0";
public static final String VERSION_NO = "v1.33.0";
/**
* Sa-Token 开源地址 Gitee

View File

@@ -0,0 +1,103 @@
package cn.dev33.satoken.util;
/**
* 字符串格式化工具
* <p>
* 本工具类 copy 自 Hutool
* https://github.com/dromara/hutool/blob/v5-master/hutool-core/src/main/java/cn/hutool/core/text/StrFormatter.java
* </p>
*
* @author Looly
*/
public class StrFormatter {
/**
* 占位符
*/
public static String EMPTY_JSON = "{}";
public static char C_BACKSLASH = '\\';
/**
* 格式化字符串<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
* 例:<br>
* 通常使用format("this is {} for {}", "a", "b") =》 this is a for b<br>
* 转义{} format("this is \\{} for {}", "a", "b") =》 this is \{} for a<br>
* 转义\ format("this is \\\\{} for {}", "a", "b") =》 this is \a for b<br>
*
* @param strPattern 字符串模板
* @param argArray 参数列表
* @return 结果
*/
public static String format(String strPattern, Object... argArray) {
return formatWith(strPattern, EMPTY_JSON, argArray);
}
/**
* 格式化字符串<br>
* 此方法只是简单将指定占位符 按照顺序替换为参数<br>
* 如果想输出占位符使用 \\转义即可,如果想输出占位符之前的 \ 使用双转义符 \\\\ 即可<br>
* 例:<br>
* 通常使用format("this is {} for {}", "{}", "a", "b") =》 this is a for b<br>
* 转义{} format("this is \\{} for {}", "{}", "a", "b") =》 this is {} for a<br>
* 转义\ format("this is \\\\{} for {}", "{}", "a", "b") =》 this is \a for b<br>
*
* @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();
}
}

View File

@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -3,6 +3,8 @@ package com.pj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import cn.dev33.satoken.SaManager;
/**
* Sa-Token 示例
* @author kong
@@ -13,6 +15,7 @@ public class SaTokenCaseApplication {
public static void main(String[] args) {
SpringApplication.run(SaTokenCaseApplication.class, args);
System.out.println("\n启动成功Sa-Token配置如下" + SaManager.getConfig());
}
}

View File

@@ -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 + " 解封成功");
}
}

View File

@@ -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();
}
// ------------------ 指定业务类型进行二级认证

View File

@@ -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");
}
}

View File

@@ -0,0 +1,49 @@
package com.pj.satoken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.dev33.satoken.log.SaLog;
/**
* 将 Sa-Token log 信息转接到 Slf4j
*
* @author kong
* @since 2022-11-2
*/
//@Component
public class SaLogForSlf4j implements SaLog {
Logger log = LoggerFactory.getLogger(SaLogForSlf4j.class);
@Override
public void trace(String str, Object... args) {
log.trace(str, args);
}
@Override
public void debug(String str, Object... args) {
log.debug(str, args);
}
@Override
public void info(String str, Object... args) {
log.info(str, args);
}
@Override
public void warn(String str, Object... args) {
log.trace(str, args);
}
@Override
public void error(String str, Object... args) {
log.error(str, args);
}
@Override
public void fatal(String str, Object... args) {
log.error(str, args);
}
}

View File

@@ -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());
// 权限校验 -- 不同模块认证不同权限
// 这里你可以写和拦截器鉴权同样的代码,不同点在于:

View File

@@ -6,6 +6,7 @@ import org.springframework.stereotype.Component;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.fun.SaFunction;
import cn.dev33.satoken.listener.SaTokenEventCenter;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.SaTokenInfo;
@@ -42,6 +43,7 @@ public class StpUserUtil {
* 重置 StpLogic 对象
* <br> 1、更改此账户的 StpLogic 对象
* <br> 2、put 到全局 StpLogic 集合中
* <br> 3、发送日志
*
* @param newStpLogic /
*/
@@ -52,6 +54,9 @@ public class StpUserUtil {
// 添加到全局 StpLogic 集合中
// 以便可以通过 SaManager.getStpLogic(type) 的方式来全局获取到这个 StpLogic
SaManager.putStpLogic(newStpLogic);
// $$ 全局事件
SaTokenEventCenter.doSetStpLogic(stpLogic);
}
/**

View File

@@ -16,7 +16,7 @@
<properties>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -16,7 +16,7 @@
<properties>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -27,7 +27,7 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<lombok.version>1.18.10</lombok.version>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -17,7 +17,7 @@
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<!-- 定义 Sa-Token 版本号 -->
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -17,7 +17,7 @@
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<!-- 定义 Sa-Token 版本号 -->
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -9,8 +9,8 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<solon.version>1.10.9</solon.version>
<sa-token.version>1.33.0</sa-token.version>
<solon.version>1.10.13</solon.version>
</properties>
<dependencies>
@@ -21,6 +21,12 @@
<artifactId>solon-web</artifactId>
<version>${solon.version}</version>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon.logging</artifactId>
<version>${solon.version}</version>
</dependency>
<!-- Sa-Token 权限认证, 在线文档https://sa-token.cc/ -->
<dependency>

View File

@@ -1,17 +1,19 @@
package com.pj;
import cn.dev33.satoken.SaManager;
import org.noear.solon.Solon;
import cn.dev33.satoken.SaManager;
/**
* sa-token整合 solon 示例
* @author noear
*
*/
public class SaTokenDemoApp {
public static void main(String[] args) {
Solon.start(SaTokenDemoApp.class, args);
System.out.println("\n启动成功sa-token配置如下" + SaManager.getConfig());
System.out.println("\n启动成功Sa-Token配置如下" + SaManager.getConfig());
}
}

View File

@@ -0,0 +1,51 @@
package com.pj.satoken;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.log.SaLog;
import cn.dev33.satoken.log.SaLogForConsole;
import cn.dev33.satoken.util.StrFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 将 Sa-Token log 信息转接到 slf4j 接口
*
* @author noear 2022/11/14 created
*/
//@Component
public class SaLogForSlf4j extends SaLogForConsole implements SaLog {
static final Logger log = LoggerFactory.getLogger(SaLogForSlf4j.class);
/**
* 打印日志到控制台
*
* @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()) {
switch (level) {
case trace:
log.trace(LOG_PREFIX + StrFormatter.format(str, args));
break;
case debug:
log.debug(LOG_PREFIX + StrFormatter.format(str, args));
break;
case info:
log.info(LOG_PREFIX + StrFormatter.format(str, args));
break;
case warn:
log.warn(LOG_PREFIX + StrFormatter.format(str, args));
break;
case error:
case fatal:
log.error(LOG_PREFIX + StrFormatter.format(str, args));
break;
}
}
}
}

View File

@@ -0,0 +1,51 @@
package com.pj.satoken;
import org.noear.solon.core.util.LogUtil;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.log.SaLog;
import cn.dev33.satoken.log.SaLogForConsole;
import cn.dev33.satoken.util.StrFormatter;
/**
* 将 Sa-Token log 信息转接到 Solon
*
* @author kong
* @since 2022-11-2
*/
//@Component
public class SaLogForSolon extends SaLogForConsole implements SaLog {
/**
* 打印日志到控制台
*
* @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()) {
switch (level) {
case trace:
LogUtil.global().trace(LOG_PREFIX + StrFormatter.format(str, args));
break;
case debug:
LogUtil.global().debug(LOG_PREFIX + StrFormatter.format(str, args));
break;
case info:
LogUtil.global().info(LOG_PREFIX + StrFormatter.format(str, args));
break;
case warn:
LogUtil.global().warn(LOG_PREFIX + StrFormatter.format(str, args));
break;
case error:
case fatal:
LogUtil.global().error(LOG_PREFIX + StrFormatter.format(str, args));
break;
}
}
}
}

View File

@@ -1,11 +1,13 @@
package com.pj.test;
import cn.dev33.satoken.exception.*;
import com.pj.util.AjaxJson;
import cn.dev33.satoken.exception.*;
import org.noear.solon.annotation.Component;
import org.noear.solon.core.event.EventListener;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Filter;
import org.noear.solon.core.handle.FilterChain;
/**
@@ -14,13 +16,12 @@ import org.noear.solon.core.handle.Context;
* @author noear
*/
@Component
public class GlobalException implements EventListener<Throwable> {
public class GlobalExceptionFilter implements Filter {
@Override
public void onEvent(Throwable e) {
Context c = Context.current();
if (c != null) {
public void doFilter(Context ctx, FilterChain chain) throws Throwable {
try {
chain.doFilter(ctx);
} catch (SaTokenException e) {
// 不同异常返回不同状态码
AjaxJson aj = null;
if (e instanceof NotLoginException) { // 如果是未登录异常
@@ -39,7 +40,7 @@ public class GlobalException implements EventListener<Throwable> {
aj = AjaxJson.getError(e.getMessage());
}
c.result = aj;
ctx.render(aj);
}
}
}

View File

@@ -17,7 +17,7 @@ sa-token:
# token风格
token-style: uuid
# 是否输出操作日志
is-log: false
is-log: true
solon:
# redis配置

View File

@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -40,7 +40,7 @@ public class SsoRequestUtil {
/**
* 单点注销地址
*/
public static String sloUrl = "http://sa-sso-server.com:9000/sso/logout";
public static String sloUrl = "http://sa-sso-server.com:9000/sso/signout";
/**
* 接口调用秘钥

View File

@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -0,0 +1,49 @@
package com.pj.satoken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.dev33.satoken.log.SaLog;
/**
* 将 Sa-Token log 信息转接到 Slf4j
*
* @author kong
* @since 2022-11-2
*/
//@Component
public class SaLogForSlf4j implements SaLog {
Logger log = LoggerFactory.getLogger(SaLogForSlf4j.class);
@Override
public void trace(String str, Object... args) {
log.trace(str, args);
}
@Override
public void debug(String str, Object... args) {
log.debug(str, args);
}
@Override
public void info(String str, Object... args) {
log.info(str, args);
}
@Override
public void warn(String str, Object... args) {
log.trace(str, args);
}
@Override
public void error(String str, Object... args) {
log.error(str, args);
}
@Override
public void fatal(String str, Object... args) {
log.error(str, args);
}
}

View File

@@ -42,8 +42,9 @@ public class SaTokenConfigure implements WebMvcConfigurer {
// 认证函数: 每次请求执行
.setAuth(obj -> {
// System.out.println("---------- sa全局认证 " + SaHolder.getRequest().getRequestPath());
// 输出 API 请求日志,方便调试代码
// SaManager.getLog().debug("----- 请求path={} 提交token={}", SaHolder.getRequest().getRequestPath(), StpUtil.getTokenValue());
})
// 异常处理函数:每次认证函数发生异常时执行此函数

View File

@@ -6,6 +6,7 @@ import org.springframework.stereotype.Component;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.fun.SaFunction;
import cn.dev33.satoken.listener.SaTokenEventCenter;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.SaTokenInfo;
@@ -42,6 +43,7 @@ public class StpUserUtil {
* 重置 StpLogic 对象
* <br> 1、更改此账户的 StpLogic 对象
* <br> 2、put 到全局 StpLogic 集合中
* <br> 3、发送日志
*
* @param newStpLogic /
*/
@@ -52,6 +54,9 @@ public class StpUserUtil {
// 添加到全局 StpLogic 集合中
// 以便可以通过 SaManager.getStpLogic(type) 的方式来全局获取到这个 StpLogic
SaManager.putStpLogic(newStpLogic);
// $$ 全局事件
SaTokenEventCenter.doSetStpLogic(stpLogic);
}
/**

View File

@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.32.0</sa-token.version>
<sa-token.version>1.33.0</sa-token.version>
</properties>
<dependencies>

View File

@@ -12,7 +12,7 @@
<description>Sa-Token Dependencies</description>
<properties>
<revision>1.32.0</revision>
<revision>1.33.0</revision>
<!-- 统一定义依赖版本号 -->
<springboot.version>2.5.12</springboot.version>
@@ -21,9 +21,8 @@
<jackson-datatype-jsr310.version>2.11.2</jackson-datatype-jsr310.version>
<servlet-api.version>3.1.0</servlet-api.version>
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
<solon.version>1.10.9</solon.version>
<solon-test.version>1.9.1</solon-test.version>
<noear-redisx.version>1.4.3</noear-redisx.version>
<solon.version>1.10.13</solon.version>
<noear-redisx.version>1.4.4</noear-redisx.version>
<jfinal.version>4.9.17</jfinal.version>
<jboot.version>3.14.4</jboot.version>
<commons-pool2.version>2.5.0</commons-pool2.version>
@@ -154,7 +153,7 @@
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-test</artifactId>
<version>${solon-test.version}</version>
<version>${solon.version}</version>
</dependency>
<!-- redis pool -->

View File

@@ -1,7 +1,7 @@
<p align="center">
<img alt="logo" src="https://sa-token.cc/logo.png" width="150" height="150">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.32.0</h1>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.33.0</h1>
<h5 align="center">一个轻量级 Java 权限认证框架,让鉴权变得简单、优雅!</h5>
<p align="center" class="badge-box">
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg?theme=gvp"></a>
@@ -136,7 +136,7 @@ Sa-Token 目前主要五大功能模块:登录认证、权限认证、单点
## 交流群
QQ交流群614714762 [点击加入](https://jq.qq.com/?_wv=1027&k=F96A2mrl)
QQ交流群707350988 [点击加入](https://jq.qq.com/?_wv=1027&k=tqbzHT2D)
微信交流群:

View File

@@ -88,11 +88,13 @@
- [推荐公众号](/more/tj-gzh)
- [加入讨论群](/more/join-group)
- [赞助 Sa-Token](/more/sa-token-donate)
- [需求提交](/more/demand-commit)
- **附录**
- [常见问题排查](/more/common-questions)
- [框架名词解释](/more/noun-intro)
- [Sa-Token功能结构图](/fun/auth-flow)
- [全局 Log 输出](/fun/log)
- [未登录场景值详解](/fun/not-login-scene)
- [Token有效期详解](/fun/token-timeout)
- [Session模型详解](/fun/session-model)

View File

@@ -18,7 +18,7 @@
<div class="logo-box">
<img src="logo.png" title="logo" />
<h1 class="logo-text">Sa-Token</h1>
<sub>v1.32.0</sub>
<sub>v1.33.0</sub>
</div>
</a>
</div>
@@ -26,37 +26,9 @@
<div class="sear-box p-none" tabindex="-1" >
<!-- 加载中…… -->
</div>
<div class="zk-box">
<a class="wzi" href="javascript:;">
<!-- <span>背景 </span> -->
<img class="theme-btn" src="static/icon/theme.svg">
<!-- <span class="zk-icon"></span> -->
</a>
<div class="zk-context theme-box">
<div>
<div style="height: 5px;"></div>
<span style="background-color: #FFFFFF;"></span>
<span style="background-color: #f5f5f5;"></span>
<span style="background-color: #F1FAFA;"></span>
<span style="background-color: #f5f5d5;"></span>
<span style="background-color: #d5f5f5;"></span>
<span style="background-color: #f5e5f5;"></span>
<span style="background-color: #E8E8FF;"></span>
<span style="background-color: #f0f9eb;"></span>
<span style="background-color: #ebe5dd;"></span>
<span style="background-color: #e8f4ff;"></span>
<!-- <span style="background-color: #F0DAD2;"></span> -->
<!-- <span style="background-color: #f5d5d5;"></span> -->
<!-- <span style="background-color: #FFFFE0;"></span> -->
<!-- <span style="background-color: #eeeeee;"></span> -->
<!-- <span style="background-color: #f5fafe;"></span> -->
</div>
</div>
</div>
<select class="select-version p-none" onchange="location.href=this.value">
<option value="doc.html">最新版</option>
<option value="v/v1.32.0/doc.html">v1.32.0</option>
<option value="v/v1.31.0/doc.html">v1.31.0</option>
<option value="v/v1.30.0/doc/index.html">v1.30.0</option>
<option value="v/v1.29.0/doc/index.html">v1.29.0</option>
@@ -92,10 +64,40 @@
<option value="v/v1.0.0/doc/index.html">v1.0.0</option>
<option value="/">首页</option>
</select>
<div class="zk-box p-none">
<a class="wzi" href="javascript:;">
<!-- <span>背景 </span> -->
<img class="theme-btn" src="static/icon/theme.svg">
<!-- <span class="zk-icon"></span> -->
</a>
<div class="zk-context theme-box">
<div>
<div style="height: 5px;"></div>
<span style="background-color: #FFFFFF;"></span>
<span style="background-color: #f5f5f5;"></span>
<span style="background-color: #F1FAFA;"></span>
<span style="background-color: #f5f5d5;"></span>
<span style="background-color: #d5f5f5;"></span>
<span style="background-color: #f5e5f5;"></span>
<span style="background-color: #E8E8FF;"></span>
<span style="background-color: #f0f9eb;"></span>
<span style="background-color: #ebe5dd;"></span>
<span style="background-color: #e8f4ff;"></span>
<!-- <span style="background-color: #F0DAD2;"></span> -->
<!-- <span style="background-color: #f5d5d5;"></span> -->
<!-- <span style="background-color: #FFFFE0;"></span> -->
<!-- <span style="background-color: #eeeeee;"></span> -->
<!-- <span style="background-color: #f5fafe;"></span> -->
</div>
</div>
</div>
<a class="wzi" href="index.html">首页</a>
<a class="wzi" href="doc.html">文档</a>
<a class="p-none wzi" href="#/more/link">案例</a>
<a class="p-none wzi" href="#/more/blog">博客</a>
<a class="p-none wzi" href="#/more/demand-commit">需求提交</a>
<!-- <a class="p-none wzi" href="#/more/blog">博客</a> -->
<a class="p-none wzi" href="#/more/sa-token-donate">赞助</a>
<a class="p-none wzi" href="#/sso/sso-pro">🔥 SSO商业版</a>
<div class="zk-box">
@@ -109,11 +111,12 @@
<a href="#/more/update-log">更新日志</a>
<a href="#/more/common-questions">常见报错</a>
<a href="#/more/tj-gzh">推荐公众号</a>
<a href="#/more/blog">相关博客</a>
<div class="zk-fengexian"></div>
<!-- <a href="http://sa-app.dev33.cn/wall.html?name=sa-token" target="_blank">需求墙</a> -->
<a href="#/fun/sa-token-test">在线考试</a>
<a href="#/fun/issue-template">在线提问</a>
<a href="https://wj.qq.com/s2/10852322/0d8b/" target="_blank">需求提交</a>
<!-- <a href="https://wj.qq.com/s2/10852322/0d8b/" target="_blank">需求提交</a> -->
<a href="https://wj.qq.com/s2/8475114/2f6a/" target="_blank">问卷调查</a>
</div>
</div>
@@ -147,7 +150,7 @@
<!-- -->
<script src="./static/docsify-plugin.js"></script>
<script>
var saTokenTopVersion = '1.32.0'; // Sa-Token最新版本
var saTokenTopVersion = '1.33.0'; // Sa-Token最新版本
var name = '<img style="width: 60px; height: 60px; vertical-align: middle;" src="logo.png" alt="logo" /> ';
name += '<b style="font-size: 28px; vertical-align: middle;">Sa-Token</b> <sub>v' + saTokenTopVersion + '</sub>';
window.$docsify = {

View File

@@ -53,39 +53,176 @@ SaToken 中的所有异常都是继承于 `SaTokenException` 的,也就是说
### 异常细分状态码-参照表
!> 目前仅对 sso 插件和 jwt 插件做了异常状态码细分,后续版本升级会支持更多模块
!> 部分插件因异常抛出点较少,暂未做状态码细分处理
**sa-token-code 核心包**
#### sa-token-code 核心包
| code码值 | 含义 |
| :-------- | :-------- |
| -1 | 代表这个异常在抛出时未指定异常细分状态码 |
| 10001 | 未能获取有效的上下文处理器 |
| 10002 | 未能获取有效的上下文 |
| 10003 | JSON 转换器未实现 |
| 10011 | 未能从全局 StpLogic 集合中找到对应 type 的 StpLogic |
| 10021 | 指定的配置文件加载失败 |
| 10022 | 配置文件属性无法正常读取 |
| 10031 | 重置的侦听器集合不可以为空 |
| 10032 | 注册的侦听器不可以为空 |
| 10301 | 提供的 Same-Token 是无效的 |
| 10311 | 表示未能通过 Http Basic 认证校验 |
| 10321 | 提供的 HttpMethod 是无效的 |
| 11001 | 未能读取到有效Token |
| 11002 | 登录时的账号id值为空 |
| 11003 | 更改 Token 指向的 账号Id 时账号Id值为空 |
| 11011 | 未能读取到有效Token |
| 11012 | Token无效 |
| 11013 | Token已过期 |
| 11014 | Token已被顶下线 |
| 11015 | Token已被踢下线 |
| 11016 | Token已临时过期 |
| 11031 | 在未集成 sa-token-jwt 插件时调用 getExtra() 抛出异常 |
| 11041 | 缺少指定的角色 |
| 11051 | 缺少指定的权限 |
| 11061 | 当前账号未通过服务封禁校验 |
| 11062 | 提供要解禁的账号无效 |
| 11063 | 提供要解禁的服务无效 |
| 11064 | 提供要解禁的等级无效 |
| 11071 | 二级认证校验未通过 |
| 12001 | 请求中缺少指定的参数 |
| 12002 | 构建 Cookie 时缺少 name 参数 |
| 12003 | 构建 Cookie 时缺少 value 参数 |
| 12101 | Base64 编码异常 |
| 12102 | Base64 解码异常 |
| 12103 | URL 编码异常 |
| 12104 | URL 解码异常 |
| 12111 | md5 加密异常 |
| 12112 | sha1 加密异常 |
| 12113 | sha256 加密异常 |
| 12114 | AES 加密异常 |
| 12115 | AES 解密异常 |
| 12116 | RSA 公钥加密异常 |
| 12117 | RSA 私钥加密异常 |
| 12118 | RSA 公钥解密异常 |
| 12119 | RSA 私钥解密异常 |
**sa-token-sso 单点登录相关:**
#### sa-token-servlet
| code码值 | 含义 |
| :-------- | :-------- |
| 20001 | `redirect` 重定向 url 是一个无效地址 |
| 20002 | `redirect` 重定向 url 不在 allowUrl 允许的范围内 |
| 20003 | 接口调用方提供的 `secretkey` 秘钥无效 |
| 20004 | 提供的 `ticket` 是无效的 |
| 20005 | 在模式三下sso-client 调用 sso-server 端 校验ticket接口 时,得到的响应是校验失败 |
| 20006 | 在模式三下sso-client 调用 sso-server 端 单点注销接口 时,得到的响应是注销失败 |
| 20007 | http 请求调用 提供的 `timestamp` 与当前时间的差距超出允许的范围 |
| 20008 | http 请求调用 提供的 `sign` 无效 |
| 20009 | 本地系统没有配置 `secretkey` 字段 |
| 20001 | 转发失败 |
| 20002 | 重定向失败 |
**sa-token-jwt 插件相关:**
#### sa-token-spring-boot-starter
| code码值 | 含义 |
| :-------- | :-------- |
| 40101 | 对 jwt 字符串解析失败 |
| 40102 | 此 jwt 的签名无效 |
| 40103 | 此 jwt 的 `loginType` 字段不符合预期 |
| 40104 | 此 jwt 已超时 |
| 20101 | 企图在非 Web 上下文获取 Request、Response 等对象 |
| 20103 | 对象转 JSON 字符串失败 |
| 20104 | JSON 字符串转 Map 失败 |
| 20105 | 默认的 Filter 异常处理函数 |
#### sa-token-reactor-spring-boot-starter
| code码值 | 含义 |
| :-------- | :-------- |
| 20203 | 对象转 JSON 字符串失败 |
| 20204 | JSON 字符串转 Map 失败 |
| 20205 | 默认的 Filter 异常处理函数 |
#### sa-token-solon-plugin
| code码值 | 含义 |
| :-------- | :-------- |
| 20301 | 默认的拦截器异常处理函数 |
| 20302 | 默认的 Filter 异常处理函数 |
#### sa-token-sso 单点登录相关:
| code码值 | 含义 |
| :-------- | :-------- |
| 30001 | `redirect` 重定向 url 是一个无效地址 |
| 30002 | `redirect` 重定向 url 不在 allowUrl 允许的范围内 |
| 30003 | 接口调用方提供的 `secretkey` 秘钥无效 |
| 30004 | 提供的 `ticket` 是无效的 |
| 30005 | 在模式三下sso-client 调用 sso-server 端 校验ticket接口 时,得到的响应是校验失败 |
| 30006 | 在模式三下sso-client 调用 sso-server 端 单点注销接口 时,得到的响应是注销失败 |
| 30007 | http 请求调用 提供的 `timestamp` 与当前时间的差距超出允许的范围 |
| 30008 | http 请求调用 提供的 `sign` 无效 |
| 30009 | 本地系统没有配置 `secretkey` 字段 |
| 30010 | 本地系统没有配置 http 请求处理器 |
#### sa-token-oauth2 相关:
| code码值 | 含义 |
| :-------- | :-------- |
| 30101 | client_id 不可为空 |
| 30102 | scope 不可为空 |
| 30103 | redirect_uri 不可为空 |
| 30104 | LoginId 不可为空 |
| 30105 | 无效client_id |
| 30106 | 无效access_token |
| 30107 | 无效 client_token |
| 30108 | Access-Token 不具备指定的 Scope |
| 30109 | Client-Token 不具备指定的 Scope |
| 30110 | 无效 code 码 |
| 30111 | 无效 Refresh-Token |
| 30112 | 请求的Scope暂未签约 |
| 30113 | 无效redirect_url |
| 30114 | 非法redirect_url |
| 30115 | 无效client_secret |
| 30116 | 请求的Scope暂未签约 |
| 30117 | 无效code |
| 30118 | 无效client_id |
| 30119 | 无效client_secret |
| 30120 | 无效redirect_uri |
| 30121 | 无效refresh_token |
| 30122 | 无效client_id |
| 30123 | 无效client_secret |
| 30124 | 无效client_id |
| 30125 | 无效response_type |
| 30131 | 暂未开放授权码模式 |
| 30132 | 暂未开放隐藏式模式 |
| 30133 | 暂未开放密码式模式 |
| 30134 | 暂未开放凭证式模式 |
#### sa-token-jwt 插件相关:
| code码值 | 含义 |
| :-------- | :-------- |
| 30201 | 对 jwt 字符串解析失败 |
| 30202 | 此 jwt 的签名无效 |
| 30203 | 此 jwt 的 `loginType` 字段不符合预期 |
| 30204 | 此 jwt 已超时 |
| 30205 | 没有配置jwt秘钥 |
| 30206 | 登录时提供的账号id为空 |
#### sa-token-temp-jwt 插件相关:
| code码值 | 含义 |
| :-------- | :-------- |
| 30301 | jwt 模式没有提供秘钥 |
| 30302 | jwt 模式不可以删除 Token |
| 30303 | Token已超时 |

View File

@@ -27,7 +27,7 @@
+ 得出以下结果:
+ 和文档上描述的不一致:
+ 其中第 xx 行的代码输出表现 和文档上描述的不一致:
+ 我的理解是:

89
sa-token-doc/fun/log.md Normal file
View File

@@ -0,0 +1,89 @@
# 参考:全局 Log 输出
---
### 打开全局日志输出
以下配置可以打开全局日志输出:
<!---------------------------- tabs:start ---------------------------->
<!------------- tab:yaml 风格 ------------->
``` yaml
sa-token:
# 是否输出操作日志
is-log: true
```
<!------------- tab:properties 风格 ------------->
``` properties
# 是否输出操作日志
sa-token.is-log=true
```
<!---------------------------- tabs:end ---------------------------->
此配置项打开之后,框架将会在账号登录、注销、二级认证 等关键性步骤打印日志,以方便项目开发调试。
框架默认将日志信息打印到控制台,如果需要将日志输出到其它地方,你可以重写 SaLog 对象,例如以下代码将会把日志转接到 Slf4j 下:
``` java
/**
* 将 Sa-Token log 信息转接到 Slf4j
*/
@Component
public class SaLogForSlf4j implements SaLog {
Logger log = LoggerFactory.getLogger(SaLogForSlf4j.class);
@Override
public void trace(String str, Object... args) {
log.trace(str, args);
}
@Override
public void debug(String str, Object... args) {
log.debug(str, args);
}
@Override
public void info(String str, Object... args) {
log.info(str, args);
}
@Override
public void warn(String str, Object... args) {
log.trace(str, args);
}
@Override
public void error(String str, Object... args) {
log.error(str, args);
}
@Override
public void fatal(String str, Object... args) {
log.error(str, args);
}
}
```
重新启动项目,观察日志打印变化。
### 增加API访问日志
手动增加 API 请求日志信息,这将非常有助于你调试代码,例如:
``` java
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
.addInclude("/**")
.addExclude("/favicon.ico")
.setAuth(obj -> {
// 输出 API 请求日志,方便调试代码
SaManager.getLog().debug("----- 请求path={} 提交token={}", SaHolder.getRequest().getRequestPath(), StpUtil.getTokenValue());
// 其它校验代码...
})
// 异常处理函数:每次认证函数发生异常时执行此函数
.setError(e -> {
System.out.println("---------- sa全局异常 ");
return SaResult.error(e.getMessage());
})
;
}
```

View File

@@ -27,10 +27,40 @@
</a>
</div>
<nav class="nav-right">
<div class="zk-box p-none">
<a class="wzi" href="javascript:;">
<!-- <span>背景 </span> -->
<img class="theme-btn" src="static/icon/theme.svg">
<!-- <span class="zk-icon"></span> -->
</a>
<div class="zk-context theme-box">
<div>
<div style="height: 5px;"></div>
<span style="background-color: #FFFFFF;"></span>
<span style="background-color: #f5f5f5;"></span>
<span style="background-color: #F1FAFA;"></span>
<span style="background-color: #f5f5d5;"></span>
<span style="background-color: #d5f5f5;"></span>
<span style="background-color: #f5e5f5;"></span>
<span style="background-color: #E8E8FF;"></span>
<span style="background-color: #f0f9eb;"></span>
<span style="background-color: #ebe5dd;"></span>
<span style="background-color: #e8f4ff;"></span>
<!-- <span style="background-color: #F0DAD2;"></span> -->
<!-- <span style="background-color: #f5d5d5;"></span> -->
<!-- <span style="background-color: #FFFFE0;"></span> -->
<!-- <span style="background-color: #eeeeee;"></span> -->
<!-- <span style="background-color: #f5fafe;"></span> -->
</div>
</div>
</div>
<a class="wzi" href="index.html">首页</a>
<a class="wzi" href="doc.html">文档</a>
<a class="p-none wzi" href="doc.html#/more/link">案例</a>
<a class="p-none wzi" href="doc.html#/more/blog">博客</a>
<a class="p-none wzi" href="doc.html#/more/demand-commit">需求提交</a>
<!-- <a class="p-none wzi" href="doc.html#/more/blog">博客</a> -->
<a class="p-none wzi" href="doc.html#/more/sa-token-donate">赞助</a>
<a class="p-none wzi" href="doc.html#/sso/sso-pro">🔥 SSO商业版</a>
<div class="zk-box">
@@ -44,11 +74,12 @@
<a href="doc.html#/more/update-log">更新日志</a>
<a href="doc.html#/more/common-questions">常见报错</a>
<a href="doc.html#/more/tj-gzh">推荐公众号</a>
<a href="doc.html#/more/blog">相关博客</a>
<div class="zk-fengexian"></div>
<!-- <a href="http://sa-app.dev33.cn/wall.html?name=sa-token" target="_blank">需求墙</a> -->
<a href="doc.html#/fun/sa-token-test">在线考试</a>
<a href="doc.html#/fun/issue-template">在线提问</a>
<a href="https://wj.qq.com/s2/10852322/0d8b/" target="_blank">需求提交</a>
<!-- <a href="https://wj.qq.com/s2/10852322/0d8b/" target="_blank">需求提交</a> -->
<a href="https://wj.qq.com/s2/8475114/2f6a/" target="_blank">问卷调查</a>
</div>
</div>
@@ -71,7 +102,7 @@
<div class="main-box">
<div class="content-box">
<!-- <div class="fenge"></div> -->
<h1>Sa-Token<small>v1.32.0</small></h1>
<h1>Sa-Token<small>v1.33.0</small></h1>
<div class="sub-title">一个轻量级 java 权限认证框架,让鉴权变得简单、优雅!</div>
<div class="btn-box">
<a class="abtn" href="https://github.com/dromara/sa-token" target="_blank">GitHub</a>
@@ -148,7 +179,7 @@
<div class="s-fenge"></div>
<h2 class="s-title">优秀开源集成案例</h2>
<div class="feature-box s-case-box">
<!-- SpringBoot_v2 4.8k -->
<!-- SpringBoot_v2 5.1k -->
<div class="s-case">
<a href="https://gitee.com/bdj/SpringBoot_v2" target="_blank" class="s-case-link">
<img src="https://oss.dev33.cn/sa-token/case/case--springboot_v2.png">
@@ -157,7 +188,7 @@
<span class="s-author">开源oschina</span>
<p class="s-case-intro">努力打造 springboot 框架的极致细腻的脚手架,原生纯净。</p>
</div>
<!-- Snowy 3.7k -->
<!-- Snowy 4.2k -->
<div class="s-case">
<a href="https://gitee.com/xiaonuobase/snowy" target="_blank" class="s-case-link">
<img src="https://oss.dev33.cn/sa-token/case/case--snowy.png">
@@ -166,7 +197,7 @@
<span class="s-author"> 小诺开源技术 </span>
<p class="s-case-intro">国内首个国密前后分离快速开发平台基于Vue3、Antdv、SaToken</p>
</div>
<!-- RuoYi-Vue-Plus 2.4k -->
<!-- RuoYi-Vue-Plus 3.3k -->
<div class="s-case">
<a href="https://gitee.com/JavaLionLi/RuoYi-Vue-Plus" target="_blank" class="s-case-link">
<img src="https://oss.dev33.cn/sa-token/case/case--ruoyi-vue-plus.png">
@@ -175,25 +206,7 @@
<span class="s-author"> 疯狂的狮子Li </span>
<p class="s-case-intro">重写 RuoYi-Vue 所有功能,集成 Sa-Token、Mybatis-Plus、Hutool 定期同步</p>
</div>
<!-- Sa-Plus 875 -->
<div class="s-case">
<a href="https://gitee.com/click33/sa-plus" target="_blank" class="s-case-link">
<img src="https://oss.dev33.cn/sa-token/case/case--sa-plus.png">
</a>
<h3 class="s-case-title">Sa-Plus</h3>
<span class="s-author"> 孔明 </span>
<p class="s-case-intro">一个基于 SpringBoot 的快速开发框架,内置代码生成器</p>
</div>
<!-- EasyAdmin 628 -->
<div class="s-case">
<a href="https://gitee.com/lakernote/easy-admin" target="_blank" class="s-case-link">
<img src="https://oss.dev33.cn/sa-token/case/case--easy-admin.png">
</a>
<h3 class="s-case-title">EasyAdmin</h3>
<span class="s-author"> laker </span>
<p class="s-case-intro">轻量级的后台管理系统脚手架,内置代码生成器、权限管理、工作流引擎等</p>
</div>
<!-- RuoYi-Cloud-Plus 475 -->
<!-- RuoYi-Cloud-Plus 990 -->
<div class="s-case">
<a href="https://gitee.com/JavaLionLi/RuoYi-Cloud-Plus" target="_blank" class="s-case-link">
<img src="https://oss.dev33.cn/sa-token/case/case--ruoyi-cloud-plus.png">
@@ -202,7 +215,25 @@
<span class="s-author"> 疯狂的狮子Li </span>
<p class="s-case-intro">重写 RuoYi-Cloud 所有功能 整合 SpringCloudAlibaba、Dubbo3.0、Sa-Token</p>
</div>
<!-- dcy-fast 315 -->
<!-- Sa-Plus 960 -->
<div class="s-case">
<a href="https://gitee.com/click33/sa-plus" target="_blank" class="s-case-link">
<img src="https://oss.dev33.cn/sa-token/case/case--sa-plus.png">
</a>
<h3 class="s-case-title">Sa-Plus</h3>
<span class="s-author"> 孔明 </span>
<p class="s-case-intro">一个基于 SpringBoot 的快速开发框架,内置代码生成器</p>
</div>
<!-- EasyAdmin 785 -->
<div class="s-case">
<a href="https://gitee.com/lakernote/easy-admin" target="_blank" class="s-case-link">
<img src="https://oss.dev33.cn/sa-token/case/case--easy-admin.png">
</a>
<h3 class="s-case-title">EasyAdmin</h3>
<span class="s-author"> laker </span>
<p class="s-case-intro">轻量级的后台管理系统脚手架,内置代码生成器、权限管理、工作流引擎等</p>
</div>
<!-- dcy-fast 343 -->
<!-- <div class="s-case">
<a href="https://gitee.com/dcy421/dcy-fast" target="_blank" class="s-case-link">
<img src="https://oss.dev33.cn/sa-token/case/case--dcy-fast.png">
@@ -211,7 +242,7 @@
<span class="s-author"> 青年 </span>
<p class="s-case-intro">一个基于 SpringBoot + Sa-Token + Mybatis-Plus 的后台管理系统</p>
</div> -->
<!-- fhs 543 -->
<!-- fhs 585 -->
<!-- <div class="s-case">
<a href="https://gitee.com/fhs-opensource/fhs-framework" target="_blank" class="s-case-link">
<img src="https://oss.dev33.cn/sa-token/case/case--fhs.png">
@@ -420,7 +451,7 @@
<a href="https://www.herodotus.cn/" target="_blank">
<img src="https://oss.dev33.cn/sa-token/link/dante-cloud2.png" msg="Dante-Cloud 是一款企业级微服务架构和服务能力开发平台。">
</a>
<a href="http://www.mtruning.club:81/" target="_blank">
<a href="http://www.mtruning.club" target="_blank">
<img src="https://oss.dev33.cn/sa-token/link/go-view.png" msg="低代码数据可视化开发平台">
</a>
<a href="https://dromara.org/zh/projects/" target="_blank">
@@ -533,7 +564,7 @@
</style>
<div class="foot-box" id="foot">
<div class="s-width" style="text-align: center;">
Copyright ©2022 Sa-Token java权限认证 | sa-token.cc | <a href="https://beian.miit.gov.cn/" target="_blank">鲁ICP备18046274号-2</a>
Copyright ©2022 Sa-Token java权限认证 | sa-token.cc | <a href="https://beian.miit.gov.cn/" target="_blank">鲁ICP备18046274号-4</a>
</div>
</div>
</div>
@@ -612,45 +643,66 @@
});
})
// set 一下 img 的宽度
function setImgWidth(img) {
// 如果已经有了宽度参数,则不追加
if(img.src.indexOf('?') > -1) {
return;
}
// console.log(img.getAttribute('nf'));
if(img.getAttribute('nf') != null) {
return;
}
// 如果不是oss上的图片
if(img.src.indexOf('https://oss.dev33.cn') == -1 || img.src.endsWith('.svg')) {
return;
}
img.src = img.src + "?x-oss-process=image/resize,m_lfit,w_" + (img.width) + ",limit_0/auto-orient,0";
</script>
<!-- 修改背景颜色 -->
<script>
// 绑定修改背景色的按钮事件
$('.theme-box span').click(function() {
let bgColor = this.style.backgroundColor;
setBg(bgColor);
localStorage.setItem('bg-color-value', bgColor)
})
// 读取上次记录
let bgColor = localStorage.getItem('bg-color-value');
if(bgColor) {
setBg(bgColor);
}
// set 一遍 img 的尺寸,防止失真
function f5ImgSize() {
$('.com-box-f .com-box a img').each(function() {
// console.log(this.src, this.width);
// 未加载完毕时则等待其load之后再设置
if(this.complete == false) {
this.onload = function() {
setImgWidth(this);
}
return;
// 设置背景颜色
function setBg(bgColor) {
console.log('---- 背景颜色设定为:', bgColor);
// -------- 设置 body 背景
document.body.style.backgroundColor = bgColor;
// -------- 设置 header 头背景
// 如果是 16 进制,转 rgba
if(bgColor.indexOf('#') == 0) {
bgColor = hexToRgba(bgColor, 0.97);
}
// 如果是 rgb转 rgba
else if(bgColor.match(/\,/g).length == 2) {
bgColor = bgColor.replace(')', ' ,0.97)');
}
document.querySelector('.doc-header').style.backgroundColor = bgColor;
}
// 16进制 转 rgba
function hexToRgba(str, a){
a = a || 1;
var reg = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/
if(!reg.test(str)){return;}
let newStr = (str.toLowerCase()).replace(/\#/g,'')
let len = newStr.length;
if(len == 3){
let t = ''
for(var i=0;i<len;i++){
t += newStr.slice(i,i+1).concat(newStr.slice(i,i+1))
}
// 追加宽度参数
setImgWidth(this);
})
}
if(window.innerWidth > 1800) {
if(navigator.userAgent.indexOf('WebKit') > -1) {
// f5ImgSize();
newStr = t
}
let arr = []; //将字符串分隔,两个两个的分隔
for(var i =0;i<6;i=i+2){
let s = newStr.slice(i,i+2)
arr.push(parseInt("0x" + s))
}
return 'rgb(' + arr.join(",") + ', ' + a + ')';
}
</script>
<script type="text/javascript">

View File

@@ -74,15 +74,6 @@
- 可能1这个账号被 `StpUtil.kickout(loginId)` 方法强制踢下线了。
### Q集成 Redis 后,明明 Redis 中有值却还是提示无效Token
根据以往的处理经验,发生这种情况 90% 的概率是因为你找错了Redis代码连接的Redis和你用管理工具看到的Redis并不是同一个。
你可能会问:我看配置文件明明是同一个啊?
我的回答是:别光看配置文件,不一定准确,在启动时直接执行 `SaManager.getSaTokenDao().set("name", "value", 100000);`
随便写入一个值看看能不能根据你的预期写进这个Redis如果能的话才能证明`代码连接的Reids``你用管理工具看到的Redis` 是同一个,再进行下一步排查。
### Q加了注解进行鉴权认证不生效
1. 注解鉴权功能默认关闭两种方式任选其一进行打开注册注解拦截器、集成AOP模块参考[注解式鉴权](/use/at-check)
2. 在Spring环境中, 如果同时配置了`WebMvcConfigurer``WebMvcConfigurationSupport`时, 也会导致拦截器失效.
@@ -91,6 +82,7 @@
3. 如果以上步骤处理后仍然没有效果,加群说明一下复现步骤
### Q我加了拦截器鉴权但是好像没有什么效果请求没有被拦截住
- 可能1这个拦截器可能没有注册成功。
- 可能2你访问的请求没有进入这个拦截器。
@@ -105,7 +97,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("--------- flag 1");
registry.addInterceptor(new SaInterceptor(handle -> {
System.out.println("--------- flag 2");
System.out.println("--------- flag 2,请求进入了拦截器,访问的 path 是:" + SaHolder.getRequest().getRequestPath());
StpUtil.checkLogin(); // 登录校验,只有会话登录后才能通过这句代码
}))
.addPathPatterns("/user/**")
@@ -117,15 +109,18 @@ public class SaTokenConfigure implements WebMvcConfigurer {
在启动时 `flag 1` 被打印出来,才证明拦截器注册成功了,在访问请求时 `flag 2` 被打印出来,才证明请求进入了拦截器。
如果拦截器没有注册成功,则:
- 可能1SpringBoot 版本较高(`>= 2.6.0`),请尝试在启动类加上 `@EnableWebMvc` 注解再重新启动。
- 可能2`SaTokenConfigure` 配置类不在启动类的同包或者子包下,导致没有被 SpringBoot 扫描到。
- 可能3`SaTokenConfigure` 配置类在启动类的同包或者子包下,但启动类上加了 `@ComponentScan("com.xxx")` 注解,导致包扫描范围不正确,请将此注解删除或移动到其它配置类上。
- 可能4:项目属于 Maven 多模块项目,`SaTokenConfigure` 和启动类没有在一个模块,且启动类模块没有引入配置类的模块,导致加载不到。
<!-- - 可能1SpringBoot 版本较高(`>= 2.6.0`),请尝试在启动类加上 `@EnableWebMvc` 注解再重新启动。 -->
- 可能1`SaTokenConfigure` 配置类不在启动类的同包或者子包下,导致没有被 SpringBoot 扫描到。
- 可能2你的项目启动类上加了 `@ComponentScan("com.xxx")` 注解,导致包扫描范围不正确,请将此注解删除或移动到其它配置类上。
- 可能3:项目属于 Maven 多模块项目,`SaTokenConfigure` 和启动类没有在一个模块,且启动类模块没有引入配置类的模块,导致加载不到。
如果拦截器已经注册成功,但请求没有进入拦截器:
- 可能1你访问的 path没有被 `.addPathPatterns("/user/**")` 拦截住。
- 可能2你访问的 path被 `.excludePathPatterns("/xxx/xx")` 排除掉了
- 可能3你访问的是另一个项目请把当前项目停掉看看你的请求还能不能访问成功。
- 可能1你访问的 path没有被 `.addPathPatterns("/user/**")` 拦截住,或者被 `.excludePathPatterns("/xxx/xx")` 排除掉了
- 可能2你访问的是另一个项目,请把当前项目停掉,看看你的请求还能不能访问成功
如果请求进入拦截器也成功了,那可能是:
- 可能1前端访问时提交了会话 Token且这个 Token 是有效的,通过了拦截器的代码校验。
- 可能2你访问的 path和你预期不符仔细观察一下打印出来的 path 信息,和你的预期相符吗。
注:以上的排查步骤,对过滤器不生效的情形一样适用。
@@ -133,17 +128,60 @@ public class SaTokenConfigure implements WebMvcConfigurer {
### Q我使用拦截器鉴权时明明排除了某个路径却仍然被拦截了
- 可能1你的项目可能是跨域了先把跨域问题解决掉参考[解决跨域问题](/fun/cors-filter)
- 可能2你访问的接口可能是404了SpringBoot环境下如果访问接口404后会被转发到`/error`,然后被再次拦截。请确保你访问的 path 有对应的 Controller 承接!
- 可能3可能这里并没有拦截但是又被其他地方拦截了。请先把这个拦截器给注释掉看看还会不会拦截如果依然拦截那说明不是这个拦截器的锅请仔细查看一下控制台抛出的堆栈信息定位一下到底是哪行代码拦截住这个请求的。
- 可能4后端拦截的 path 未必是你前端访问的这个path建议先打印一下 path 信息,看看和你预想的是否一致,再做分析。
- 可能5你写了多个匹配规则请求只越过了第一个规则被其它规则拦下了例如以下代码
- 可能3可能拦截器这里并没有拦截,但是又被其他地方拦截了。请先把这个拦截器给注释掉,看看还会不会拦截,如果依然拦截,那说明不是这个拦截器的锅,请仔细查看一下控制台抛出的堆栈信息,定位一下到底是哪行代码拦截住这个请求的。
- 可能4后端拦截的 path 未必是你前端访问的这个path特别是经过网关转发后的path可能会有变化,建议先打印一下 path 信息,看看和你预想的是否一致,再做分析。
``` java
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaInterceptor(handle -> {
try {
System.out.println("-------- 前端访问path" + SaHolder.getRequest().getRequestPath());
StpUtil.checkLogin();
System.out.println("-------- 此 path 校验成功:" + SaHolder.getRequest().getRequestPath());
} catch (Exception e) {
System.out.println("-------- 此 path 校验失败:" + SaHolder.getRequest().getRequestPath());
throw e;
}
})).addPathPatterns("/**");
}
```
- 可能5可能你只提交了一个请求但是浏览器自动帮你提交了其它请求举个例子首次访问网站时浏览器一般会自动提交 `/favicon.ico`,所以**你需要找出是哪个path被拦截了**怎么找呢用【可能4】的代码来测试找。
- 可能6你的项目配置了 `context-path` 上下文地址,比如 `server.servlet.context-path=/shop`,注意这个地址是不需要加在拦截器上的:
``` java
// 这是错误示例,不需要把 context-path 上下文参数写在下面的 excludePathPatterns 地址上。
registry.addInterceptor(new SaInterceptor(hadnle -> StpUtil.checkLogin()))
.addPathPatterns("/**").excludePathPatterns("/shop/user/login");
// 这是正确示例,无论你的 context-path 上下文配置了什么样的值,下面的 excludePathPatterns 地址都不需要写上它
registry.addInterceptor(new SaInterceptor(hadnle -> StpUtil.checkLogin()))
.addPathPatterns("/**").excludePathPatterns("/user/login");
```
- 可能7你写了多个匹配规则请求越过了第一个规则但又被其它规则拦下来了例如以下代码
``` java
// 以下代码,当你未登录访问 `/user/doLogin` 时会被第1条规则越过然后被第2条拦下校验登录然后抛出异常`NotLoginExceptionxxx`
registry.addInterceptor(new SaInterceptor(handler -> {
SaRouter.match("/**").notMatch("/user/doLogin").check(r -> StpUtil.checkLogin()); // 第1个规则
SaRouter.match("/**").notMatch("/article/getList").check(r -> StpUtil.checkLogin()); // 第2个规则
SaRouter.match("/**").notMatch("/goods/getList").check(r -> StpUtil.checkLogin()); // 第3个规则
})).addPathPatterns("/**");
```
以上代码,当你未登录访问 `/user/doLogin` 时会被第1条规则越过然后被第2条拦下校验登录然后抛出异常`NotLoginExceptionxxx`
- 可能8你自定义的封装方法并没有按照你的预想情况执行
``` java
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaInterceptor(handle -> {
// 调用自定义的 excludePaths() 方法获取数据排除鉴权
SaRouter.match("/**").notMatch(excludePaths()).check(r -> StpUtil.checkLogin());
})).addPathPatterns("/**");
}
// 自定义查询排查鉴权的地址方法
public static List<String> excludePaths() {
List<String> list = ... // 从数据源查询...;
return list;
}
```
如上方法, `excludePaths()` 可能并不会像你预想的一样正确执行返回相应的值,请在 `.notMatch()` 处 `一律先硬编码写固定死值来测试`,这时就有两种情况:
- 情况1写固定死值时代码能正常执行了那说明你自定义的 `excludePaths()` 方法有问题,执行结果不正确。
- 情况2写固定也不行那说明不是 `excludePaths()` 的问题,那再从其它地方开始排查。
### Q我在配置文件中加了一些关于 Sa-Token 的配置,但是没有生效。
@@ -196,6 +234,48 @@ public class SaTokenApplication {
2、情况2是这个组件注入成功了但是还没到执行时机比如 `StpInterface` 组件,只有在鉴权时才会触发,如果你的代码仅仅是登录校验,就不会执行到这个组件。
### Q集成 Redis 后,明明 Redis 中有值却还是提示无效Token
根据以往的处理经验,发生这种情况 90% 的概率是因为你找错了Redis代码连接的Redis和你用管理工具看到的Redis并不是同一个。
你可能会问:我看配置文件明明是同一个啊?
我的回答是:别光看配置文件,不一定准确,在启动时直接执行 `SaManager.getSaTokenDao().set("name", "value", 100000);`
随便写入一个值看看能不能根据你的预期写进这个Redis如果能的话才能证明`代码连接的Reids` 和`你用管理工具看到的Redis` 是同一个,再进行下一步排查。
### Q报错无效Same-Tokenxxxxxxxxxxx
与之类似的的报错还有:
- SSO模式二时报错无效ticketxxxxxxxxxx
- OAuth2模块跨多个项目搭建Server时报错无效 Access-Tokenxxxxxx
- 微服务做分布式 Session 认证时,报错:无效 Tokenxxxxxxxxx
- 等等等等....
这些功能有个统一的特点,就是需要多个项目连接同一个 Redis 才能搭建成功,如果连接的不是同一个 Redis就会导致 Token / ticket 无法互相认证。
你可能会问:我看配置文件明明就是连接的同一个 Redis 啊?
别急,和上一个问题一样,**不要凭借肉眼检查下定论**,在你的两个服务之间,分别使用以下代码测试一下:
``` java
@SpringBootApplication
public class SspServerApplication {
public static void main(String[] args) {
SpringApplication.run(SspServerApplication.class, args);
// 写值测试:注意一定要用下列方法测试,不要用自己封装的 RedisUtil 之类的测试
SaManager.getSaTokenDao().set("name", "value", 100000);
}
}
```
如果都能根据你的预期写进同一个 Redis那才能证明两个服务确实连接的是同一个 Redis。
实际上在交流群中提问这些问题的同学90%的经过以上测试以后,都会发现两者连接的不是同一个 Reids原因大多是Redis配置没有生效、使用了 Alone-Redis 之类的……
如果你是剩下的 10%,那么继续排查:两边的 sa-token 配置是否完全一致,比如 token-name 配置不一致,也会导致数据无法相互认证。最好是把所有 sa-token 相关的配置都复制过去,试验一下看看。
### Q有时候我不加 Token 也可以通过鉴权,请问是怎么回事?
@@ -276,6 +356,38 @@ springboot 集成 satoken redis 后, 一旦 springboot 切换版本就有可能
### Q我实现了 StpInterface 接口,但是在登录时没有进入我的实现类代码?
不进入是正常现象, StpInterface 是鉴权接口,在执行鉴权代码时才会进入 StpInterface 实现类,登录认证时不会进入。
### Q启动时报错找不到 xx 类 xx 方法:
``` java
Caused by: java.lang.ClassNotFoundException: cn.dev33.satoken.same.SaSameTemplate
```
一般找不到类,或者找不到方法,都是版本冲突了,使用 Sa-Token 时一定要注意**版本对齐**,意思是所有和 Sa-Token 相关的依赖都需要版本一致。
比如说你如果一个依赖是 1.32.0,一个是 1.31.0,就会造成无法启动:
``` xml
<!-- 如下样例:一个是 `1.32.0`,一个是 `1.31.0` 版本没对齐,就会造成项目无法启动 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.32.0</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
<version>1.31.0</version>
</dependency>
```
请仔细排查你的 pom.xml 文件,是否有 Sa-Token 依赖没对齐,**请不要肉眼检查,用全局搜索 "sa-token" 关键词来找**,如果是多模块或者微服务项目,就整个项目搜索。
<!-- ---------------------------- 常见疑问 ----------------------------- -->

View File

@@ -0,0 +1,14 @@
# 需求提交
文档不清晰?功能不完善?脑袋里有好 idea都可以提
我们深知一个优秀的项目不能闭门造车,它需要海纳百川:[点我在线提交需求](https://wj.qq.com/s2/10852322/0d8b/)
<!-- 我们深知一个优秀的项目需要海纳百川,请把你的不满、吐槽、创意纷纷砸过来:[点我在线提交需求](https://wj.qq.com/s2/10852322/0d8b/) -->
我们将慎重对待每一位粉丝的珍贵意见 ❤️ ❤️ ❤️:
- 对框架新增特性功能且比较简单,会在第一时间进行开发。
- 对框架新增特性功能但比较复杂,会延后几个版本制定相应的计划后进行开发。
- 与框架设计理念不太相符,或超出权限认证范畴,将会视需求人数决定是否开发。

View File

@@ -6,10 +6,9 @@
### 1、加入QQ交流群
![QQ群](https://oss.dev33.cn/sa-token/qq-group-4.png ':size=180')
![QQ群](https://oss.dev33.cn/sa-token/qq-group-2.png ':size=180')
QQ交流群614714762 [点击加入](https://jq.qq.com/?_wv=1027&k=F96A2mrl)
QQ交流群707350988 [点击加入](https://jq.qq.com/?_wv=1027&k=tqbzHT2D)
### 2、加入微信交流群

View File

@@ -19,6 +19,10 @@ Sa-Token 采用 Apache-2.0 开源协议,**承诺框架本身与官网文档永
| 赞助人 | 赞助金额 | 留言 | 时间 |
| :-------- | :-------- | :-------- | :-------- |
| [laruui](https://gitee.com/laruui) | ¥ 10.0 | 感谢您的开源项目!加油 | 2022-10-28 |
| [就眠儀式](https://gitee.com/Jmysy) | ¥ 50.0 | 感谢您的开源项目! | 2022-10-26 |
| [王文博](https://gitee.com/rl520) | ¥ 20.0 | 感谢您的开源项目! | 2022-10-24 |
| [feyong](https://gitee.com/feyong) | ¥ 10.0 | 感谢您的开源项目! | 2022-10-18 |
| [xueshize](https://gitee.com/xueshize) | ¥ 20.0 | 感谢您的开源项目! | 2022-10-12 |
| [西东](https://gitee.com/noear_admin) | ¥ 99.0 | 感谢您的开源项目! | 2022-10-05 |
| [BlueRose](https://gitee.com/Bluerose_2) | ¥ 20.0 | 感谢您的付出,项目非常棒! | 2022-09-22 |

View File

@@ -1,5 +1,17 @@
# 更新日志
### 2022-11-16 @v1.33.0
- 重构:重构异常状态码机制。 **[重要]**
- 重构:重构 sa-token-sso 模块异常码改为 300 开头sa-token-jwt 异常码改为 302 开头。 **[不向下兼容]**
- 新增:新增全局 Log 模块。 **[重要]**
- 重构:`SaTokenListenerForConsolePrint` 改名 `SaTokenListenerForLog`**[不向下兼容]**
- 修复:修复多线程下 `SaFoxUtil.getRandomString()` 随机数重复问题。
- 修复:修复 sa-token-demo-sso3-client-nosdk 项目中单点注销 url 配置错误的问题
- 文档:文档优化。
### 2022-10-28 @v1.32.0
- 修复:修复 sa-token-dao-redis-fastjson 插件多余序列化 `timeout` 字段的问题。
- 修复:修复 sa-token-dao-redis-fastjson 插件 `session.getModel` 无法反序列化实体类的问题。

View File

@@ -42,7 +42,7 @@ public class HomeController {
@RequestMapping("/home")
public Object index() {
// 如果未登录,则先去登录
if(StpUtil.isLogin()) {
if(!StpUtil.isLogin()) {
return SaHolder.getResponse().redirect("/sso/auth");
}

View File

@@ -26,9 +26,15 @@ SSO 集成常见问题整理
可以:加个过滤器检测到未登录 自动跳转就行了,详细可以参照章节:[[何时引导用户去登录]](/sso/sso-custom-login) 给出的建议进行设计
### 问:我参照文档SSO模式二搭建一直提示Ticket无效请问怎么回事
根据群友的反馈,出现此异常概率最大的原因是因为 `Client``Server` 没有连接同一个RedisSSO模式二中两者必须连接同一个 Redis 才可以登录成功
如果您排查之后不是此原因可以加入QQ群或者在issues反馈一下
### 问:我参照文档搭建SSO-Client一直提示Ticket无效请问怎么回事
如果使用的是模式二,出现此异常概率最大的原因是因为 `Client``Server` 没有连接同一个RedisSSO模式二中两者必须连接同一个 Redis 才可以登录成功
你可能会问:我看配置文件明明是同一个啊?
我的建议是:排查时不要仅凭肉眼判断,分别在你的 `Client``Server` 启动后调用 `SaManager.getSaTokenDao().set("name", "value", 100000);`
随便写入一个值看看能不能根据你的预期写进同一个Redis里如果能的话才能证明 `Client``Server` 连接的Reids 是同一个,再进行下一步排查。
如果使用的是模式三,则排查是否有重复校验 ticket 的代码,一个 ticket 码只能使用一次,多次重复使用就会提示这个。
### 模式一或者模式二报错Could not write JSON: No serializer found for class com.pj.sso.SysUser and no properties discovered to create BeanSerializer

View File

@@ -5,14 +5,14 @@
---
### 正式版本
v1.32.0 正式版,可上生产:
v1.33.0 正式版,可上生产:
``` xml
<!-- Sa-Token 权限认证 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.32.0</version>
<version>1.33.0</version>
</dependency>
```

View File

@@ -31,7 +31,7 @@ body{font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu
/* 右边导航 */
.doc-header .nav-right{margin: 0; float: right; padding-right: 4em; font-size: 15px;}
.doc-header .nav-right>*{padding: 0px; margin: 0 14px;}
.doc-header .nav-right>*{padding: 0px; margin: 0 15px;}
.doc-header .nav-right>*:last-child{position: relative; z-index: 1002;}
.doc-header .nav-right>select{border-color: #999; color: #666; outline: 0; cursor: pointer; transition: all 0.2s; background-color: #FFF; border-width: 1px; outline: 0;}
.doc-header .nav-right>select:hover{box-shadow: 0 0 10px #aaa;}
@@ -285,6 +285,7 @@ body {
background-size: 18px 18px;
background-image: url(icon/code.svg);
}
#main .dt-btn{display: none;}
/* ------------- 背景色相关 ------------- */

View File

@@ -106,7 +106,7 @@ body{font-size: 16px; color: #34495E; font-family: "Source Sans Pro","Helvetica
/* -------- 集成案例 --------- */
.s-case-box{justify-content: space-between;}
.s-case{border: 1px #e5e5e5 solid; flex: 0 0 31.5%; margin-top: 30px; text-align: left; box-sizing: border-box; padding-bottom: 16px; overflow: hidden;}
.s-case{position: relative; transition: all 0.2s;}
.s-case{position: relative; transition: all 0.2s; background-color: #FFF;}
.s-case-link{display: block; width: 100%; height: 0px; padding-bottom: 50%; position: relative; overflow: hidden;}
.s-case-link img{width: 100%; height: 100%; object-fit: cover; object-position: center; position: absolute;}
.s-case-link img{transition: all 0.3s;}
@@ -234,4 +234,27 @@ body{font-size: 16px; color: #34495E; font-family: "Source Sans Pro","Helvetica
/* 下三角小图标 */
.zk-icon{display: inline-block; width: 0px; height: 0px; position: relative;top: 3px; margin-left: 4px;}
.zk-icon{border-style: solid; border-width: 5px; border-color: #aaa transparent transparent transparent; }
.zk-icon{border-style: solid; border-width: 5px; border-color: #aaa transparent transparent transparent; }
/* ------------- 背景色相关 ------------- */
/* 侧边栏需要透明 */
.sidebar-toggle{background-color: transparent !important;}
.sidebar{background-color: transparent !important;}
/* 变色的动画 */
.doc-header,body{transition: all 0.5s !important;}
/* 调色按钮 */
.theme-btn{width: 25px; height: 25px; line-height: 60px; vertical-align: middle; position: relative; top: -1px;}
.theme-box{width: 160px; text-align: left; line-height: 20px; margin-top: -20px; white-space: normal;}
.theme-box span{
display: inline-block;
width: 20px;
height: 20px;
margin: 1px 2px;
border: 1px #ccc solid;
cursor: pointer;
border-radius: 1px;
box-sizing: border-box;
}

View File

@@ -10,7 +10,7 @@ Sa-Token 提供一种侦听器机制,通过注册侦听器,你可以订阅
![sa-token-listener](https://oss.dev33.cn/sa-token/doc/sa-token-listener.svg 's-w')
框架默认内置了侦听器 `SaTokenListenerForConsolePrint` 实现:[代码参考](https://gitee.com/dromara/sa-token/blob/master/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForConsolePrint.java)
框架默认内置了侦听器 `SaTokenListenerForLog` 实现:[代码参考](https://gitee.com/dromara/sa-token/blob/master/sa-token-core/src/main/java/cn/dev33/satoken/listener/SaTokenListenerForLog.java)
,功能是控制台 log 打印输出,你可以通过配置`sa-token.is-log=true`开启。
要注册自定义的侦听器也非常简单:
@@ -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) {

View File

@@ -83,7 +83,7 @@ for (String sessionId : sessionIdList) {
请根据业务实际水平合理调用API。
> 活动 Token 的统计方式会比实际情况略有延迟,如果需要精确统计实时在线用户信息建议采用 WebSocket。
> 活动 Token 的统计方式会比实际情况略有延迟,如果需要精确统计实时在线用户信息建议采用 WebSocket。
---

View File

@@ -98,7 +98,6 @@ public class SaTokenConfigure {
config.setIsShare(true); // 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
config.setTokenStyle("uuid"); // token风格
config.setIsLog(false); // 是否输出操作日志
return config;
}
}
```

View File

@@ -4,8 +4,8 @@ import java.util.Map;
import java.util.Objects;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.jwt.error.SaJwtErrorCode;
import cn.dev33.satoken.jwt.exception.SaJwtException;
import cn.dev33.satoken.jwt.exception.SaJwtExceptionCode;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
@@ -146,19 +146,19 @@ public class SaJwtTemplate {
try {
jwt = JWT.of(token);
} catch (JWTException e) {
throw new SaJwtException("jwt 解析失败:" + token, e).setCode(SaJwtExceptionCode.CODE_40101);
throw new SaJwtException("jwt 解析失败:" + token, e).setCode(SaJwtErrorCode.CODE_30201);
}
JSONObject payloads = jwt.getPayloads();
// 校验 Token 签名
boolean verify = jwt.setKey(keyt.getBytes()).verify();
if(verify == false) {
throw new SaJwtException("jwt 签名无效:" + token).setCode(SaJwtExceptionCode.CODE_40102);
throw new SaJwtException("jwt 签名无效:" + token).setCode(SaJwtErrorCode.CODE_30202);
};
// 校验 loginType
if(Objects.equals(loginType, payloads.getStr(LOGIN_TYPE)) == false) {
throw new SaJwtException("jwt loginType 无效:" + token).setCode(SaJwtExceptionCode.CODE_40103);
throw new SaJwtException("jwt loginType 无效:" + token).setCode(SaJwtErrorCode.CODE_30203);
}
// 校验 Token 有效期
@@ -166,7 +166,7 @@ public class SaJwtTemplate {
Long effTime = payloads.getLong(EFF, 0L);
if(effTime != NEVER_EXPIRE) {
if(effTime == null || effTime < System.currentTimeMillis()) {
throw new SaJwtException("jwt 已过期:" + token).setCode(SaJwtExceptionCode.CODE_40104);
throw new SaJwtException("jwt 已过期:" + token).setCode(SaJwtErrorCode.CODE_30204);
}
}
}

View File

@@ -6,7 +6,7 @@ import java.util.Map;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.ApiDisabledException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.jwt.error.SaJwtErrorCode;
import cn.dev33.satoken.jwt.exception.SaJwtException;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpLogic;
@@ -40,7 +40,7 @@ public class StpLogicJwtForMixin extends StpLogic {
*/
public String jwtSecretKey() {
String keyt = getConfig().getJwtSecretKey();
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
SaJwtException.throwByNull(keyt, "请配置jwt秘钥", SaJwtErrorCode.CODE_30205);
return keyt;
}

View File

@@ -2,7 +2,8 @@ package cn.dev33.satoken.jwt;
import java.util.Map;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.jwt.error.SaJwtErrorCode;
import cn.dev33.satoken.jwt.exception.SaJwtException;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
@@ -34,7 +35,7 @@ public class StpLogicJwtForSimple extends StpLogic {
*/
public String jwtSecretKey() {
String keyt = getConfig().getJwtSecretKey();
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
SaJwtException.throwByNull(keyt, "请配置jwt秘钥", SaJwtErrorCode.CODE_30205);
return keyt;
}

View File

@@ -5,7 +5,7 @@ import java.util.Map;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.ApiDisabledException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.jwt.error.SaJwtErrorCode;
import cn.dev33.satoken.jwt.exception.SaJwtException;
import cn.dev33.satoken.listener.SaTokenEventCenter;
import cn.dev33.satoken.stp.SaLoginModel;
@@ -42,7 +42,7 @@ public class StpLogicJwtForStateless extends StpLogic {
*/
public String jwtSecretKey() {
String keyt = getConfig().getJwtSecretKey();
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
SaJwtException.throwByNull(keyt, "请配置jwt秘钥", SaJwtErrorCode.CODE_30205);
return keyt;
}
@@ -90,7 +90,7 @@ public class StpLogicJwtForStateless extends StpLogic {
*/
@Override
public String createLoginSession(Object id, SaLoginModel loginModel) {
SaTokenException.throwByNull(id, "账号id不能为空");
SaJwtException.throwByNull(id, "账号id不能为空", SaJwtErrorCode.CODE_30206);
// ------ 1、初始化 loginModel
loginModel.build(getConfig());

View File

@@ -0,0 +1,29 @@
package cn.dev33.satoken.jwt.error;
/**
* 定义 sa-token-jwt 所有异常细分状态码
*
* @author kong
* @since 2022-10-31
*/
public class SaJwtErrorCode {
/** 对 jwt 字符串解析失败 */
public static final int CODE_30201 = 30201;
/** 此 jwt 的签名无效 */
public static final int CODE_30202 = 30202;
/** 此 jwt 的 loginType 字段不符合预期 */
public static final int CODE_30203 = 30203;
/** 此 jwt 已超时 */
public static final int CODE_30204 = 30204;
/** 没有配置jwt秘钥 */
public static final int CODE_30205 = 30205;
/** 登录时提供的账号id为空 */
public static final int CODE_30206 = 30206;
}

View File

@@ -1,12 +1,14 @@
package cn.dev33.satoken.jwt.exception;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.util.SaFoxUtil;
/**
* 一个异常:代表 jwt 解析错误
* 一个异常:代表 jwt 相关错误
*
* @author kong
* @since 2022-10-31
*/
public class SaJwtException extends SaTokenException {
@@ -52,5 +54,17 @@ public class SaJwtException extends SaTokenException {
throw new SaJwtException(message);
}
}
/**
* 如果value==null或者isEmpty则抛出message异常
* @param value 值
* @param message 异常信息
* @param code 异常细分状态码
*/
public static void throwByNull(Object value, String message, int code) {
if(SaFoxUtil.isEmpty(value)) {
throw new SaJwtException(message).setCode(code);
}
}
}

View File

@@ -1,23 +0,0 @@
package cn.dev33.satoken.jwt.exception;
/**
* 定义所有 JWT 异常细分状态码
*
* @author kong
* @since: 2022-5-1
*/
public class SaJwtExceptionCode {
/** 对 jwt 字符串解析失败 */
public static final int CODE_40101 = 40101;
/** 此 jwt 的签名无效 */
public static final int CODE_40102 = 40102;
/** 此 jwt 的 loginType 字段不符合预期 */
public static final int CODE_40103 = 40103;
/** 此 jwt 已超时 */
public static final int CODE_40104 = 40104;
}

View File

@@ -0,0 +1,98 @@
package cn.dev33.satoken.oauth2.error;
/**
* 定义 sa-token-oauth2 所有异常细分状态码
*
* @author kong
* @since: 2022-10-31
*/
public interface SaOAuth2ErrorCode {
/** client_id 不可为空 */
public static final int CODE_30101 = 30101;
/** scope 不可为空 */
public static final int CODE_30102 = 30102;
/** redirect_uri 不可为空 */
public static final int CODE_30103 = 30103;
/** LoginId 不可为空 */
public static final int CODE_30104 = 30104;
/** 无效client_id */
public static final int CODE_30105 = 30105;
/** 无效access_token */
public static final int CODE_30106 = 30106;
/** 无效 client_token */
public static final int CODE_30107 = 30107;
/** Access-Token 不具备指定的 Scope */
public static final int CODE_30108 = 30108;
/** Client-Token 不具备指定的 Scope */
public static final int CODE_30109 = 30109;
/** 无效 code 码 */
public static final int CODE_30110 = 30110;
/** 无效 Refresh-Token */
public static final int CODE_30111 = 30111;
/** 请求的Scope暂未签约 */
public static final int CODE_30112 = 30112;
/** 无效redirect_url */
public static final int CODE_30113 = 30113;
/** 非法redirect_url */
public static final int CODE_30114 = 30114;
/** 无效client_secret */
public static final int CODE_30115 = 30115;
/** 请求的Scope暂未签约 */
public static final int CODE_30116 = 30116;
/** 无效code */
public static final int CODE_30117 = 30117;
/** 无效client_id */
public static final int CODE_30118 = 30118;
/** 无效client_secret */
public static final int CODE_30119 = 30119;
/** 无效redirect_uri */
public static final int CODE_30120 = 30120;
/** 无效refresh_token */
public static final int CODE_30121 = 30121;
/** 无效client_id */
public static final int CODE_30122 = 30122;
/** 无效client_secret */
public static final int CODE_30123 = 30123;
/** 无效client_id */
public static final int CODE_30124 = 30124;
/** 无效response_type */
public static final int CODE_30125 = 30125;
/** 暂未开放授权码模式 */
public static final int CODE_30131 = 30131;
/** 暂未开放隐藏式模式 */
public static final int CODE_30132 = 30132;
/** 暂未开放密码式模式 */
public static final int CODE_30133 = 30133;
/** 暂未开放凭证式模式 */
public static final int CODE_30134 = 30134;
}

View File

@@ -26,10 +26,11 @@ public class SaOAuth2Exception extends SaTokenException {
* 如果flag==true则抛出message异常
* @param flag 标记
* @param message 异常信息
* @param code 异常细分码
*/
public static void throwBy(boolean flag, String message) {
public static void throwBy(boolean flag, String message, int code) {
if(flag) {
throw new SaOAuth2Exception(message);
throw new SaOAuth2Exception(message).setCode(code);
}
}

Some files were not shown because too many files have changed in this diff Show More