mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-10-21 02:57:23 +08:00
新增Reactor响应式编程支持,WebFlux集成!
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -19,7 +19,9 @@
|
||||
<!-- 所有模块 -->
|
||||
<modules>
|
||||
<module>sa-token-core</module>
|
||||
<module>sa-token-servlet</module>
|
||||
<module>sa-token-spring-boot-starter</module>
|
||||
<module>sa-token-reactor-spring-boot-starter</module>
|
||||
<module>sa-token-dao-redis</module>
|
||||
<module>sa-token-dao-redis-jackson</module>
|
||||
<module>sa-token-spring-aop</module>
|
||||
|
@@ -11,6 +11,10 @@ import cn.dev33.satoken.context.SaTokenContext;
|
||||
import cn.dev33.satoken.context.SaTokenContextDefaultImpl;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl;
|
||||
import cn.dev33.satoken.filter.SaFilterErrorStrategy;
|
||||
import cn.dev33.satoken.filter.SaFilterErrorStrategyDefaultImpl;
|
||||
import cn.dev33.satoken.filter.SaFilterStrategy;
|
||||
import cn.dev33.satoken.filter.SaFilterStrategyDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
@@ -124,6 +128,42 @@ public class SaTokenManager {
|
||||
return saTokenContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局过滤器-认证策略 Bean
|
||||
*/
|
||||
private static SaFilterStrategy strategy;
|
||||
public static void setSaFilterStrategy(SaFilterStrategy strategy) {
|
||||
SaTokenManager.strategy = strategy;
|
||||
}
|
||||
public static SaFilterStrategy getSaFilterStrategy() {
|
||||
if (strategy == null) {
|
||||
synchronized (SaTokenManager.class) {
|
||||
if (strategy == null) {
|
||||
setSaFilterStrategy(new SaFilterStrategyDefaultImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return strategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局过滤器-异常处理策略 Bean
|
||||
*/
|
||||
private static SaFilterErrorStrategy errorStrategy;
|
||||
public static void setSaFilterErrorStrategy(SaFilterErrorStrategy errorStrategy) {
|
||||
SaTokenManager.errorStrategy = errorStrategy;
|
||||
}
|
||||
public static SaFilterErrorStrategy getSaFilterErrorStrategy() {
|
||||
if (errorStrategy == null) {
|
||||
synchronized (SaTokenManager.class) {
|
||||
if (errorStrategy == null) {
|
||||
setSaFilterErrorStrategy(new SaFilterErrorStrategyDefaultImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return errorStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* StpLogic集合, 记录框架所有成功初始化的StpLogic
|
||||
*/
|
||||
|
@@ -1,29 +1,37 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 与底层容器交互接口
|
||||
* Sa-Token 上下文处理器
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaTokenContext {
|
||||
|
||||
/**
|
||||
* 获取当前请求的 Request 对象
|
||||
* 获取当前请求的 [Request] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public SaRequest getRequest();
|
||||
|
||||
/**
|
||||
* 获取当前请求的 Response 对象
|
||||
* 获取当前请求的 [Response] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public SaResponse getResponse();
|
||||
|
||||
/**
|
||||
* 获取当前请求的 [存储器] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public SaStorage getStorage();
|
||||
|
||||
/**
|
||||
* 校验指定路由匹配符是否可以匹配成功指定路径
|
||||
*
|
||||
|
@@ -1,30 +1,44 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Sa-Token 与底层容器交互接口 [默认实现类]
|
||||
* Sa-Token 上下文处理器 [默认实现类]
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenContextDefaultImpl implements SaTokenContext {
|
||||
|
||||
/**
|
||||
* 获取当前请求的Request对象
|
||||
* 默认的错误提示语
|
||||
*/
|
||||
public static final String ERROR_MESSAGE = "未初始化任何有效上下文处理器";
|
||||
|
||||
/**
|
||||
* 获取当前请求的 [Request] 对象
|
||||
*/
|
||||
@Override
|
||||
public SaRequest getRequest() {
|
||||
throw new SaTokenException("未初始化任何有效容器");
|
||||
throw new SaTokenException(ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的Response对象
|
||||
* 获取当前请求的 [Response] 对象
|
||||
*/
|
||||
@Override
|
||||
public SaResponse getResponse() {
|
||||
throw new SaTokenException("未初始化任何有效容器");
|
||||
throw new SaTokenException(ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的 [存储器] 对象
|
||||
*/
|
||||
@Override
|
||||
public SaStorage getStorage() {
|
||||
throw new SaTokenException(ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,7 +46,9 @@ public class SaTokenContextDefaultImpl implements SaTokenContext {
|
||||
*/
|
||||
@Override
|
||||
public boolean matchPath(String pattern, String path) {
|
||||
throw new SaTokenException("未初始化任何有效容器");
|
||||
throw new SaTokenException(ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,34 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 上下文环境 [ThreadLocal版本]
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenContextForThreadLocal implements SaTokenContext {
|
||||
|
||||
@Override
|
||||
public SaRequest getRequest() {
|
||||
return SaTokenContextForThreadLocalStorage.getRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaResponse getResponse() {
|
||||
return SaTokenContextForThreadLocalStorage.getResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaStorage getStorage() {
|
||||
return SaTokenContextForThreadLocalStorage.getStorage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchPath(String pattern, String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 基于ThreadLocal的上下文对象存储器
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenContextForThreadLocalStorage {
|
||||
|
||||
/**
|
||||
* 基于 ThreadLocal 的上下文
|
||||
*/
|
||||
static ThreadLocal<Box> boxThreadLocal = new InheritableThreadLocal<Box>();
|
||||
|
||||
/**
|
||||
* 初始化 [容器]
|
||||
* @param request {@link SaRequest}
|
||||
* @param response {@link SaResponse}
|
||||
* @param storage {@link SaStorage}
|
||||
*/
|
||||
public static void setBox(SaRequest request, SaResponse response, SaStorage storage) {
|
||||
Box bok = new Box(request, response, storage);
|
||||
boxThreadLocal.set(bok);
|
||||
};
|
||||
|
||||
/**
|
||||
* 清除 [容器]
|
||||
*/
|
||||
public static void clearBox() {
|
||||
boxThreadLocal.remove();
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取 [容器]
|
||||
* @return see note
|
||||
*/
|
||||
public static Box getBox() {
|
||||
return boxThreadLocal.get();
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取 [容器], 如果为空则抛出异常
|
||||
* @return see note
|
||||
*/
|
||||
public static Box getBoxNotNull() {
|
||||
Box box = boxThreadLocal.get();
|
||||
if(box == null) {
|
||||
throw new SaTokenException("未成功初始化上下文");
|
||||
}
|
||||
return box;
|
||||
};
|
||||
|
||||
/**
|
||||
* 在 [上下文容器] 获取 [Request] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public static SaRequest getRequest() {
|
||||
return getBoxNotNull().getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [上下文容器] 获取 [Response] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public static SaResponse getResponse() {
|
||||
return getBoxNotNull().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [上下文容器] 获取 [存储器] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public static SaStorage getStorage() {
|
||||
return getBoxNotNull().getStorage();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 临时内部类,存储三个对象
|
||||
* @author kong
|
||||
*/
|
||||
/**
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public static class Box {
|
||||
|
||||
public SaRequest request;
|
||||
|
||||
public SaResponse response;
|
||||
|
||||
public SaStorage storage;
|
||||
|
||||
public Box(SaRequest request, SaResponse response, SaStorage storage){
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
public SaRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public void setRequest(SaRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public SaResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(SaResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public SaStorage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public void setStorage(SaStorage storage) {
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Box [request=" + request + ", response=" + response + ", storage=" + storage + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -13,26 +13,6 @@ public interface SaRequest {
|
||||
*/
|
||||
public Object getSource();
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里写入一个值
|
||||
* @param name 键
|
||||
* @param value 值
|
||||
*/
|
||||
public void setAttribute(String name, Object value);
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里获取一个值
|
||||
* @param name 键
|
||||
* @return 值
|
||||
*/
|
||||
public Object getAttribute(String name);
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里删除一个值
|
||||
* @param name 键
|
||||
*/
|
||||
public void removeAttribute(String name);
|
||||
|
||||
/**
|
||||
* 在 [请求体] 里获取一个值
|
||||
* @param name 键
|
||||
|
@@ -0,0 +1,37 @@
|
||||
package cn.dev33.satoken.context.model;
|
||||
|
||||
/**
|
||||
* [存储器] 包装类
|
||||
* <p> 在 Request作用域里: 存值、取值
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaStorage {
|
||||
|
||||
/**
|
||||
* 获取底层源对象
|
||||
* @return see note
|
||||
*/
|
||||
public Object getSource();
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里写入一个值
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
*/
|
||||
public void set(String key, Object value);
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里获取一个值
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public Object get(String key);
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里删除一个值
|
||||
* @param key 键
|
||||
*/
|
||||
public void delete(String key);
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
* 因为不能确定最终运行的容器属于标准Servlet模型还是非Servlet模型,特封装此包下的包装类进行对接
|
||||
*/
|
||||
package cn.dev33.satoken.context;
|
||||
package cn.dev33.satoken.context.model;
|
@@ -0,0 +1,17 @@
|
||||
package cn.dev33.satoken.filter;
|
||||
|
||||
/**
|
||||
* sa-token全局过滤器-异常处理策略
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaFilterErrorStrategy {
|
||||
|
||||
/**
|
||||
* 执行方法
|
||||
* @param e 异常对象
|
||||
* @return 输出对象(请提前序列化)
|
||||
*/
|
||||
public Object run(Throwable e);
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package cn.dev33.satoken.filter;
|
||||
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
|
||||
/**
|
||||
* sa-token全局过滤器-异常处理策略 [默认实现]
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaFilterErrorStrategyDefaultImpl implements SaFilterErrorStrategy {
|
||||
|
||||
/**
|
||||
* 执行方法
|
||||
* @throws Throwable 抛出异常
|
||||
*/
|
||||
@Override
|
||||
public Object run(Throwable e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package cn.dev33.satoken.filter;
|
||||
|
||||
/**
|
||||
* sa-token全局过滤器-认证策略
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaFilterStrategy {
|
||||
|
||||
/**
|
||||
* 执行方法
|
||||
* @param r 无含义参数,留作扩展
|
||||
*/
|
||||
public void run(Object r);
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package cn.dev33.satoken.filter;
|
||||
|
||||
/**
|
||||
* sa-token全局过滤器-认证策略 [默认实现]
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaFilterStrategyDefaultImpl implements SaFilterStrategy {
|
||||
|
||||
/**
|
||||
* 执行验证的方法
|
||||
*/
|
||||
@Override
|
||||
public void run(Object r) {
|
||||
// default no action
|
||||
}
|
||||
|
||||
}
|
@@ -14,6 +14,7 @@ import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
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.exception.DisableLoginException;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
@@ -93,15 +94,15 @@ public class StpLogic {
|
||||
*/
|
||||
public void setTokenValue(String tokenValue, int cookieTimeout){
|
||||
SaTokenConfig config = getConfig();
|
||||
// 将token保存到本次Request里
|
||||
SaRequest request = SaTokenManager.getSaTokenContext().getRequest();
|
||||
// 将token保存到[存储器]里
|
||||
SaStorage storage = SaTokenManager.getSaTokenContext().getStorage();
|
||||
// 判断是否配置了token前缀
|
||||
String tokenPrefix = config.getTokenPrefix();
|
||||
if(SaTokenInsideUtil.isEmpty(tokenPrefix)) {
|
||||
request.setAttribute(splicingKeyJustCreatedSave(), tokenValue);
|
||||
storage.set(splicingKeyJustCreatedSave(), tokenValue);
|
||||
} else {
|
||||
// 如果配置了token前缀,则拼接上前缀一起写入
|
||||
request.setAttribute(splicingKeyJustCreatedSave(), tokenPrefix + SaTokenConsts.TOKEN_CONNECTOR_CHAT + tokenValue);
|
||||
storage.set(splicingKeyJustCreatedSave(), tokenPrefix + SaTokenConsts.TOKEN_CONNECTOR_CHAT + tokenValue);
|
||||
}
|
||||
|
||||
// 注入Cookie
|
||||
@@ -117,14 +118,15 @@ public class StpLogic {
|
||||
*/
|
||||
public String getTokenValue(){
|
||||
// 0. 获取相应对象
|
||||
SaStorage storage = SaTokenManager.getSaTokenContext().getStorage();
|
||||
SaRequest request = SaTokenManager.getSaTokenContext().getRequest();
|
||||
SaTokenConfig config = getConfig();
|
||||
String keyTokenName = getTokenName();
|
||||
String tokenValue = null;
|
||||
|
||||
// 1. 尝试从request里读取
|
||||
if(request.getAttribute(splicingKeyJustCreatedSave()) != null) {
|
||||
tokenValue = String.valueOf(request.getAttribute(splicingKeyJustCreatedSave()));
|
||||
// 1. 尝试从Storage里读取
|
||||
if(storage.get(splicingKeyJustCreatedSave()) != null) {
|
||||
tokenValue = String.valueOf(storage.get(splicingKeyJustCreatedSave()));
|
||||
}
|
||||
// 2. 尝试从请求体里面读取
|
||||
if(tokenValue == null && config.getIsReadBody()){
|
||||
@@ -361,7 +363,7 @@ public class StpLogic {
|
||||
* @param loginId 指定账号id
|
||||
* @param disableTime 封禁时间, 单位: 秒 (-1=永久封禁)
|
||||
*/
|
||||
public void disableLoginId(Object loginId, long disableTime) {
|
||||
public void disable(Object loginId, long disableTime) {
|
||||
SaTokenManager.getSaTokenDao().set(splicingKeyDisable(loginId), DisableLoginException.BE_VALUE, disableTime);
|
||||
}
|
||||
|
||||
@@ -383,6 +385,15 @@ public class StpLogic {
|
||||
return SaTokenManager.getSaTokenDao().getTimeout(splicingKeyDisable(loginId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解封指定账号
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public void untieDisable(Object loginId) {
|
||||
SaTokenManager.getSaTokenDao().delete(splicingKeyDisable(loginId));
|
||||
}
|
||||
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
@@ -686,7 +697,7 @@ public class StpLogic {
|
||||
// 删除[最后操作时间]
|
||||
SaTokenManager.getSaTokenDao().delete(splicingKeyLastActivityTime(tokenValue));
|
||||
// 清除标记
|
||||
SaTokenManager.getSaTokenContext().getRequest().removeAttribute((SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY));
|
||||
SaTokenManager.getSaTokenContext().getStorage().delete((SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -699,8 +710,8 @@ public class StpLogic {
|
||||
return;
|
||||
}
|
||||
// 如果本次请求已经有了[检查标记], 则立即返回
|
||||
SaRequest request = SaTokenManager.getSaTokenContext().getRequest();
|
||||
if(request.getAttribute(SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY) != null) {
|
||||
SaStorage storage = SaTokenManager.getSaTokenContext().getStorage();
|
||||
if(storage.get(SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY) != null) {
|
||||
return;
|
||||
}
|
||||
// ------------ 验证是否已经 [临时过期]
|
||||
@@ -717,7 +728,7 @@ public class StpLogic {
|
||||
// --- 至此,验证已通过
|
||||
|
||||
// 打上[检查标记],标记一下当前请求已经通过验证,避免一次请求多次验证,造成不必要的性能消耗
|
||||
request.setAttribute(SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY, true);
|
||||
storage.set(SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1249,14 +1260,14 @@ public class StpLogic {
|
||||
* @param loginId 指定loginId
|
||||
*/
|
||||
public void switchTo(Object loginId) {
|
||||
SaTokenManager.getSaTokenContext().getRequest().setAttribute(splicingKeySwitch(), loginId);
|
||||
SaTokenManager.getSaTokenContext().getStorage().set(splicingKeySwitch(), loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束临时切换身份
|
||||
*/
|
||||
public void endSwitch() {
|
||||
SaTokenManager.getSaTokenContext().getRequest().removeAttribute(splicingKeySwitch());
|
||||
SaTokenManager.getSaTokenContext().getStorage().delete(splicingKeySwitch());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1264,7 +1275,7 @@ public class StpLogic {
|
||||
* @return 是否正处于[身份临时切换]中
|
||||
*/
|
||||
public boolean isSwitch() {
|
||||
return SaTokenManager.getSaTokenContext().getRequest().getAttribute(splicingKeySwitch()) != null;
|
||||
return SaTokenManager.getSaTokenContext().getStorage().get(splicingKeySwitch()) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1272,7 +1283,7 @@ public class StpLogic {
|
||||
* @return 返回[身份临时切换]的loginId
|
||||
*/
|
||||
public Object getSwitchLoginId() {
|
||||
return SaTokenManager.getSaTokenContext().getRequest().getAttribute(splicingKeySwitch());
|
||||
return SaTokenManager.getSaTokenContext().getStorage().get(splicingKeySwitch());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1292,7 +1303,4 @@ public class StpLogic {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -139,8 +139,8 @@ public class StpUtil {
|
||||
* @param loginId 指定账号id
|
||||
* @param disableTime 封禁时间, 单位: 秒 (-1=永久封禁)
|
||||
*/
|
||||
public static void disableLoginId(Object loginId, long disableTime) {
|
||||
stpLogic.disableLoginId(loginId, disableTime);
|
||||
public static void disable(Object loginId, long disableTime) {
|
||||
stpLogic.disable(loginId, disableTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,6 +161,13 @@ public class StpUtil {
|
||||
return stpLogic.getDisableTime(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解封指定账号
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void untieDisable(Object loginId) {
|
||||
stpLogic.untieDisable(loginId);
|
||||
}
|
||||
|
||||
// 查询相关
|
||||
|
||||
|
@@ -84,4 +84,9 @@ public class SaTokenConsts {
|
||||
*/
|
||||
public static final String TOKEN_CONNECTOR_CHAT = " ";
|
||||
|
||||
/**
|
||||
* 切面、拦截器、过滤器等各种组件的注册优先级顺序
|
||||
*/
|
||||
public static final int ASSEMBLY_ORDER = -100;
|
||||
|
||||
}
|
||||
|
@@ -46,17 +46,17 @@
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
</dependency> -->
|
||||
|
||||
<!-- 提供redis连接池 -->
|
||||
<dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合SpringAOP实现注解鉴权 -->
|
||||
<!-- <dependency>
|
||||
|
@@ -5,6 +5,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
|
||||
/**
|
||||
* sa-token整合SpringBoot 示例
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaTokenDemoApplication {
|
||||
|
||||
|
@@ -6,13 +6,14 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
|
||||
|
||||
|
||||
/**
|
||||
* sa-token代码方式进行配置
|
||||
* [Sa-Token 权限认证] 配置类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class MySaTokenConfig implements WebMvcConfigurer {
|
||||
public class SaTokenConfiguration implements WebMvcConfigurer {
|
||||
|
||||
// 注册sa-token的拦截器,打开注解式鉴权功能
|
||||
@Override
|
12
sa-token-demo-webflux/.gitignore
vendored
Normal file
12
sa-token-demo-webflux/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
|
||||
.factorypath
|
72
sa-token-demo-webflux/pom.xml
Normal file
72
sa-token-demo-webflux/pom.xml
Normal file
@@ -0,0 +1,72 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-webflux</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.15.2</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- springboot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token 权限认证(Reactor响应式集成), 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token整合redis (使用jdk默认序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合redis (使用jackson序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- 提供redis连接池 -->
|
||||
<!-- <dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency> -->
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
@@ -0,0 +1,32 @@
|
||||
package com.pj;
|
||||
|
||||
//import org.springframework.boot.SpringApplication;
|
||||
//import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.http.MediaType;
|
||||
//import org.springframework.web.reactive.function.server.RequestPredicates;
|
||||
//import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
//import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
//import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
//import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
|
||||
/**
|
||||
* sa-token整合webflux 示例
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaTokenWebfluxDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenWebfluxDemoApplication.class, args);
|
||||
System.out.println("\n启动成功:sa-token配置如下:" + SaTokenManager.getConfig());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.filter.SaFilterErrorStrategy;
|
||||
import cn.dev33.satoken.filter.SaFilterStrategy;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorFilter;
|
||||
import cn.dev33.satoken.router.SaRouterUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* [Sa-Token 权限认证] 配置类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class SaTokenConfiguration {
|
||||
|
||||
/**
|
||||
* 注册 [sa-token全局过滤器]
|
||||
*/
|
||||
@Bean
|
||||
public SaReactorFilter getSaReactorFilter() {
|
||||
return new SaReactorFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 [sa-token全局过滤器-认证策略]
|
||||
*/
|
||||
@Bean
|
||||
public SaFilterStrategy getSaFilterStrategy() {
|
||||
return r -> {
|
||||
System.out.println("---------- 进入sa-token全局过滤器 -----------");
|
||||
SaRouterUtil.match("/test/test", () -> StpUtil.checkLogin());
|
||||
// SaRouterUtil.match("/user/**", () -> StpUtil.checkPermission("user"));
|
||||
// SaRouterUtil.match("/admin/**", () -> StpUtil.checkPermission("admin"));
|
||||
// SaRouterUtil.match("/goods/**", () -> StpUtil.checkPermission("goods"));
|
||||
// SaRouterUtil.match("/orders/**", () -> StpUtil.checkPermission("orders"));
|
||||
// SaRouterUtil.match("/notice/**", () -> StpUtil.checkPermission("notice"));
|
||||
// SaRouterUtil.match("/comment/**", () -> StpUtil.checkPermission("comment"));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 [sa-token全局过滤器-异常处理策略]
|
||||
*/
|
||||
@Bean
|
||||
public SaFilterErrorStrategy getSaFilterErrorStrategy() {
|
||||
return e -> AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
|
||||
/**
|
||||
* 自定义权限验证接口扩展
|
||||
*/
|
||||
@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展
|
||||
public class StpInterfaceImpl implements StpInterface {
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的权限码集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginKey) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("101");
|
||||
list.add("user-add");
|
||||
list.add("user-delete");
|
||||
list.add("user-update");
|
||||
list.add("user-get");
|
||||
list.add("article-get");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的角色标识集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginKey) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("admin");
|
||||
list.add("super-admin");
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicates;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@Configuration
|
||||
public class DefineRoutes {
|
||||
|
||||
/**
|
||||
* 函数式编程,初始化路由表
|
||||
* @return 路由表
|
||||
*/
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> getRoutes() {
|
||||
return RouterFunctions.route(RequestPredicates.GET("/fun"), req -> {
|
||||
// 测试打印
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
|
||||
// 返回结果
|
||||
AjaxJson aj = AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8).syncBody(aj);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.exception.DisableLoginException;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*/
|
||||
@ControllerAdvice // 可指定包前缀,比如:(basePackages = "com.pj.admin")
|
||||
public class GlobalException {
|
||||
|
||||
// 全局异常拦截(拦截项目中的所有异常)
|
||||
@ResponseBody
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e)
|
||||
throws Exception {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
System.out.println("全局异常---------------");
|
||||
e.printStackTrace();
|
||||
|
||||
// 不同异常返回不同状态码
|
||||
AjaxJson aj = null;
|
||||
if (e instanceof NotLoginException) { // 如果是未登录异常
|
||||
NotLoginException ee = (NotLoginException) e;
|
||||
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
|
||||
} else if(e instanceof NotRoleException) { // 如果是角色异常
|
||||
NotRoleException ee = (NotRoleException) e;
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
|
||||
} else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
} else if(e instanceof DisableLoginException) { // 如果是被封禁异常
|
||||
DisableLoginException ee = (DisableLoginException) e;
|
||||
aj = AjaxJson.getNotJur("账号被封禁:" + ee.getDisableTime() + "秒后解封");
|
||||
} else { // 普通异常, 输出:500 + 异常信息
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回给前端
|
||||
return aj;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 测试专用Controller
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/test/")
|
||||
public class TestController {
|
||||
|
||||
// 测试登录接口 [同步模式], 浏览器访问: http://localhost:8081/test/login
|
||||
@RequestMapping("login")
|
||||
public AjaxJson login(@RequestParam(defaultValue="10001") String id) {
|
||||
StpUtil.setLoginId(id);
|
||||
return AjaxJson.getSuccess("登录成功");
|
||||
}
|
||||
|
||||
// API测试 [同步模式], 浏览器访问: http://localhost:8081/test/isLogin
|
||||
@RequestMapping("isLogin")
|
||||
public AjaxJson isLogin() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
}
|
||||
|
||||
// API测试 [异步模式], 浏览器访问: http://localhost:8081/test/isLogin2
|
||||
@RequestMapping("isLogin2")
|
||||
public Mono<AjaxJson> isLogin2() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
AjaxJson aj = AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
return Mono.just(aj);
|
||||
}
|
||||
|
||||
// API测试 [异步模式, 同一线程], 浏览器访问: http://localhost:8081/test/isLogin3
|
||||
@RequestMapping("isLogin3")
|
||||
public Mono<AjaxJson> isLogin3() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
// 异步方式
|
||||
return SaReactorHolder.getContent().map(e -> {
|
||||
System.out.println("当前会话是否登录2:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
});
|
||||
}
|
||||
|
||||
// API测试 [异步模式, 不同线程], 浏览器访问: http://localhost:8081/test/isLogin4
|
||||
@RequestMapping("isLogin4")
|
||||
public Mono<AjaxJson> isLogin4() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("线程id-----" + Thread.currentThread().getId());
|
||||
return Mono.delay(Duration.ofSeconds(1)).flatMap(r->{
|
||||
return SaReactorHolder.getContent().map(rr->{
|
||||
System.out.println("线程id---内--" + Thread.currentThread().getId());
|
||||
System.out.println("当前会话是否登录2:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
System.out.println("线程id-----------Controller--" + Thread.currentThread().getId() + "\t\t");
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
}
|
||||
|
||||
|
||||
}
|
154
sa-token-demo-webflux/src/main/java/com/pj/util/AjaxJson.java
Normal file
154
sa-token-demo-webflux/src/main/java/com/pj/util/AjaxJson.java
Normal file
@@ -0,0 +1,154 @@
|
||||
package com.pj.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
|
||||
/**
|
||||
* ajax请求返回Json格式数据的封装
|
||||
*/
|
||||
public class AjaxJson implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L; // 序列化版本号
|
||||
|
||||
public static final int CODE_SUCCESS = 200; // 成功状态码
|
||||
public static final int CODE_ERROR = 500; // 错误状态码
|
||||
public static final int CODE_WARNING = 501; // 警告状态码
|
||||
public static final int CODE_NOT_JUR = 403; // 无权限状态码
|
||||
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
|
||||
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
|
||||
|
||||
public int code; // 状态码
|
||||
public String msg; // 描述信息
|
||||
public Object data; // 携带对象
|
||||
public Long dataCount; // 数据总数,用于分页
|
||||
|
||||
/**
|
||||
* 返回code
|
||||
* @return
|
||||
*/
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给msg赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
public String getMsg() {
|
||||
return this.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给data赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setData(Object data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将data还原为指定类型并返回
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(Class<T> cs) {
|
||||
return (T) data;
|
||||
}
|
||||
|
||||
// ============================ 构建 ==================================
|
||||
|
||||
public AjaxJson(int code, String msg, Object data, Long dataCount) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
this.dataCount = dataCount;
|
||||
}
|
||||
|
||||
// 返回成功
|
||||
public static AjaxJson getSuccess() {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg, Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessData(Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessArray(Object... data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
|
||||
// 返回失败
|
||||
public static AjaxJson getError() {
|
||||
return new AjaxJson(CODE_ERROR, "error", null, null);
|
||||
}
|
||||
public static AjaxJson getError(String msg) {
|
||||
return new AjaxJson(CODE_ERROR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回警告
|
||||
public static AjaxJson getWarning() {
|
||||
return new AjaxJson(CODE_ERROR, "warning", null, null);
|
||||
}
|
||||
public static AjaxJson getWarning(String msg) {
|
||||
return new AjaxJson(CODE_WARNING, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回未登录
|
||||
public static AjaxJson getNotLogin() {
|
||||
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
|
||||
}
|
||||
|
||||
// 返回没有权限的
|
||||
public static AjaxJson getNotJur(String msg) {
|
||||
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回一个自定义状态码的
|
||||
public static AjaxJson get(int code, String msg){
|
||||
return new AjaxJson(code, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回分页和数据的
|
||||
public static AjaxJson getPageData(Long dataCount, Object data){
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
|
||||
}
|
||||
|
||||
// 返回,根据受影响行数的(大于0=ok,小于0=error)
|
||||
public static AjaxJson getByLine(int line){
|
||||
if(line > 0){
|
||||
return getSuccess("ok", line);
|
||||
}
|
||||
return getError("error").setData(line);
|
||||
}
|
||||
|
||||
// 返回,根据布尔值来确定最终结果的 (true=ok,false=error)
|
||||
public static AjaxJson getByBoolean(boolean b){
|
||||
return b ? getSuccess("ok") : getError("error");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
// 转JSON格式输出
|
||||
try {
|
||||
return new ObjectMapper().writeValueAsString(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
45
sa-token-demo-webflux/src/main/resources/application.yml
Normal file
45
sa-token-demo-webflux/src/main/resources/application.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
spring:
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken
|
||||
# token有效期,单位s 默认30天, -1代表永不过期
|
||||
timeout: 2592000
|
||||
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||
activity-timeout: -1
|
||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
allow-concurrent-login: true
|
||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
is-share: true
|
||||
# token风格
|
||||
token-style: uuid
|
||||
|
||||
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间(毫秒)
|
||||
timeout: 10000ms
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
@@ -65,6 +65,14 @@
|
||||
alias: {
|
||||
'/.*/_sidebar.md': '/_sidebar.md'
|
||||
},
|
||||
// tab选项卡
|
||||
tabs: {
|
||||
persist : true, // 是否在刷新页面时重置选项卡
|
||||
sync : true, // 页面上的多个tab是否同步切换
|
||||
theme : 'classic', // 主题:'classic', 'material', false
|
||||
tabComments: true, // 用注释来标注选项卡标题,例如:<!-- tab:SpringBoot -->
|
||||
tabHeadings: true // 用标题+粗体来定制选项卡
|
||||
},
|
||||
plugins: [ // 自定义插件
|
||||
function(hook, vm) {
|
||||
// 解析之后执行
|
||||
@@ -102,6 +110,7 @@
|
||||
<script src="https://unpkg.zhimg.com/docsify-copy-code@2.1.0/dist/docsify-copy-code.min.js"></script>
|
||||
<script src="https://unpkg.zhimg.com/prismjs@1.19.0/components/prism-java.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||
<script src="https://unpkg.zhimg.com/docsify-tabs@1.4.4"></script>
|
||||
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
|
||||
|
@@ -31,13 +31,16 @@ StpUtil.logoutByTokenValue("xxxx-xxxx-xxxx-xxxx-xxxx");
|
||||
// 封禁指定账号
|
||||
// 参数一:账号id
|
||||
// 参数二:封禁时长,单位:秒 (86400秒=1天,此值为-1时,代表永久封禁)
|
||||
StpUtil.disableLoginId(10001, 86400);
|
||||
StpUtil.disable(10001, 86400);
|
||||
|
||||
// 获取指定账号是否已被封禁 (true=已被封禁, false=未被封禁)
|
||||
StpUtil.isDisable(10001);
|
||||
|
||||
// 获取指定账号剩余封禁时间,单位:秒
|
||||
StpUtil.getDisableTime(10001);
|
||||
|
||||
// 解除封禁
|
||||
StpUtil.untieDisable(10001);
|
||||
```
|
||||
|
||||
|
||||
|
@@ -51,6 +51,7 @@
|
||||
<div class="btn-box">
|
||||
<a href="https://github.com/dromara/sa-token" target="_blank">GitHub</a>
|
||||
<a href="https://gitee.com/dromara/sa-token" target="_blank">码云</a>
|
||||
<a href="https://jq.qq.com/?_wv=1027&k=45H977HM" target="_blank">加QQ群</a>
|
||||
<!-- <a href="http://sa-app.dev33.cn/wall.html?name=sa-token" target="_blank">需求墙</a> -->
|
||||
<a href="doc/index.html" target="_self" class="doc-btn">开发文档</a>
|
||||
</div>
|
||||
@@ -253,7 +254,7 @@
|
||||
<h3>联系我们</h3>
|
||||
<ul class="list-unstyle">
|
||||
<!-- <li>电话:<a href="tel:123;">123</a></li> -->
|
||||
<li>QQ群 :<a href="https://jq.qq.com/?_wv=1027&k=5DHN5Ib" target="_blank">1002350610</a></li>
|
||||
<li>QQ群 :<a href="https://jq.qq.com/?_wv=1027&k=45H977HM" target="_blank">1002350610</a></li>
|
||||
<li>邮箱:<a href="javascript: alert('暂无');">暂无</a></li>
|
||||
<li>联系:<a href="javascript: alert('暂无');">暂无</a></li>
|
||||
</ul>
|
||||
|
12
sa-token-reactor-spring-boot-starter/.gitignore
vendored
Normal file
12
sa-token-reactor-spring-boot-starter/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.factorypath
|
||||
|
||||
.idea/
|
63
sa-token-reactor-spring-boot-starter/pom.xml
Normal file
63
sa-token-reactor-spring-boot-starter/pom.xml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.15.2</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sa-token-reactor-spring-boot-starter</name>
|
||||
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
|
||||
<description>springboot reactor integrate sa-token</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- sa-token-core -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-core</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
<!-- spring-boot-starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- spring-web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>5.0.4.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- reactor-core -->
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<version>3.1.4.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
</dependency> -->
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
@@ -0,0 +1,63 @@
|
||||
package cn.dev33.satoken.reactor.context;
|
||||
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Reactor全局过滤器
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Order(SaTokenConsts.ASSEMBLY_ORDER)
|
||||
public class SaReactorFilter implements WebFilter {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
// ---------- 全局认证处理
|
||||
try {
|
||||
// 写入全局上下文 (同步)
|
||||
SaReactorSyncHolder.setContent(exchange);
|
||||
|
||||
// 执行全局过滤器
|
||||
SaTokenManager.getSaFilterStrategy().run(null);
|
||||
|
||||
} catch (Throwable e) {
|
||||
// 1. 获取异常处理策略结果
|
||||
Object result = SaTokenManager.getSaFilterErrorStrategy().run(e);
|
||||
String resultString = String.valueOf(result);
|
||||
|
||||
// 2. 写入输出流
|
||||
if(exchange.getResponse().getHeaders().getFirst("Content-Type") == null) {
|
||||
exchange.getResponse().getHeaders().set("Content-Type", "text/plain; charset=utf-8");
|
||||
}
|
||||
return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(resultString.getBytes())));
|
||||
|
||||
} finally {
|
||||
// 清除上下文
|
||||
SaReactorSyncHolder.clearContent();
|
||||
}
|
||||
|
||||
// ---------- 执行
|
||||
|
||||
// 写入全局上下文 (同步)
|
||||
SaReactorSyncHolder.setContent(exchange);
|
||||
|
||||
// 执行
|
||||
return chain.filter(exchange).subscriberContext(ctx -> {
|
||||
// 写入全局上下文 (异步)
|
||||
ctx = ctx.put(SaReactorHolder.CONTEXT_KEY, exchange);
|
||||
return ctx;
|
||||
}).doFinally(r -> {
|
||||
// 清除上下文
|
||||
SaReactorSyncHolder.clearContent();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package cn.dev33.satoken.reactor.context;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Reactor上下文操作 [异步]
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaReactorHolder {
|
||||
|
||||
/**
|
||||
* key
|
||||
*/
|
||||
public static final Class<ServerWebExchange> CONTEXT_KEY = ServerWebExchange.class;
|
||||
|
||||
/**
|
||||
* 获取上下文对象
|
||||
* @return see note
|
||||
*/
|
||||
public static Mono<ServerWebExchange> getContent() {
|
||||
// 从全局 Mono<Context> 获取
|
||||
return Mono.subscriberContext().map(ctx -> ctx.get(CONTEXT_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上下文对象, 并设置到同步上下文中
|
||||
* @return see note
|
||||
*/
|
||||
public static Mono<ServerWebExchange> getContentAndSetSync() {
|
||||
// 从全局 Mono<Context> 获取
|
||||
return Mono.subscriberContext().map(ctx -> {
|
||||
// 设置到sync中
|
||||
SaReactorSyncHolder.setContent(ctx.get(CONTEXT_KEY));
|
||||
return ctx.get(CONTEXT_KEY);
|
||||
}).doFinally(r->{
|
||||
// 从sync中清除
|
||||
SaReactorSyncHolder.clearContent();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
package cn.dev33.satoken.reactor.context;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage;
|
||||
import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage.Box;
|
||||
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.fun.SaFunction;
|
||||
import cn.dev33.satoken.reactor.model.SaRequestForReactor;
|
||||
import cn.dev33.satoken.reactor.model.SaResponseForReactor;
|
||||
import cn.dev33.satoken.reactor.model.SaStorageForReactor;
|
||||
|
||||
/**
|
||||
* Reactor上下文操作 [同步]
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaReactorSyncHolder {
|
||||
|
||||
/**
|
||||
* 写入上下文对象
|
||||
* @param exchange see note
|
||||
*/
|
||||
public static void setContent(ServerWebExchange exchange) {
|
||||
SaRequest request = new SaRequestForReactor(exchange.getRequest());
|
||||
SaResponse response = new SaResponseForReactor(exchange.getResponse());
|
||||
SaStorage storage = new SaStorageForReactor(exchange);
|
||||
SaTokenContextForThreadLocalStorage.setBox(request, response, storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上下文对象
|
||||
* @return see note
|
||||
*/
|
||||
public static ServerWebExchange getContent() {
|
||||
Box box = SaTokenContextForThreadLocalStorage.getBoxNotNull();
|
||||
return (ServerWebExchange)box.getStorage().getSource();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除上下文对象
|
||||
*/
|
||||
public static void clearContent() {
|
||||
SaTokenContextForThreadLocalStorage.clearBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入上下文对象, 并在执行函数后将其清除
|
||||
* @param exchange see note
|
||||
* @param fun see note
|
||||
*/
|
||||
public static void setContent(ServerWebExchange exchange, SaFunction fun) {
|
||||
try {
|
||||
setContent(exchange);
|
||||
fun.run();
|
||||
} finally {
|
||||
clearContent();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
package cn.dev33.satoken.reactor.model;
|
||||
|
||||
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
|
||||
/**
|
||||
* Request for Reactor
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaRequestForReactor implements SaRequest {
|
||||
|
||||
/**
|
||||
* 底层Request对象
|
||||
*/
|
||||
ServerHttpRequest request;
|
||||
|
||||
/**
|
||||
* 实例化
|
||||
* @param request request对象
|
||||
*/
|
||||
public SaRequestForReactor(ServerHttpRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层源对象
|
||||
*/
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [请求体] 里获取一个值
|
||||
*/
|
||||
@Override
|
||||
public String getParameter(String name) {
|
||||
return request.getQueryParams().getFirst(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [请求头] 里获取一个值
|
||||
*/
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
return request.getHeaders().getFirst(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [Cookie作用域] 里获取一个值
|
||||
*/
|
||||
@Override
|
||||
public String getCookieValue(String name) {
|
||||
HttpCookie cookie = request.getCookies().getFirst(name);
|
||||
if(cookie == null) {
|
||||
return null;
|
||||
}
|
||||
return cookie.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前请求的URL
|
||||
*/
|
||||
@Override
|
||||
public String getRequestURI() {
|
||||
return request.getURI().getPath();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
package cn.dev33.satoken.reactor.model;
|
||||
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.ResponseCookie.ResponseCookieBuilder;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* Response for Reactor
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaResponseForReactor implements SaResponse {
|
||||
|
||||
/**
|
||||
* 底层Response对象
|
||||
*/
|
||||
ServerHttpResponse response;
|
||||
|
||||
/**
|
||||
* 实例化
|
||||
* @param response response对象
|
||||
*/
|
||||
public SaResponseForReactor(ServerHttpResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层源对象
|
||||
*/
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定Cookie
|
||||
*/
|
||||
@Override
|
||||
public void deleteCookie(String name) {
|
||||
addCookie(name, null, null, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入指定Cookie
|
||||
*/
|
||||
@Override
|
||||
public void addCookie(String name, String value, String path, String domain, int timeout) {
|
||||
|
||||
// 构建CookieBuilder
|
||||
ResponseCookieBuilder builder = ResponseCookie.from(name, value)
|
||||
.domain(domain)
|
||||
.path(path)
|
||||
.maxAge(timeout)
|
||||
;
|
||||
|
||||
// set path
|
||||
if(SaTokenInsideUtil.isEmpty(path) == true) {
|
||||
path = "/";
|
||||
}
|
||||
builder.path(path);
|
||||
|
||||
// set domain
|
||||
if(SaTokenInsideUtil.isEmpty(domain) == false) {
|
||||
builder.domain(domain);
|
||||
}
|
||||
|
||||
// 写入Cookie
|
||||
response.addCookie(builder.build());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
package cn.dev33.satoken.reactor.model;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
|
||||
/**
|
||||
* Storage for Reactor
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaStorageForReactor implements SaStorage {
|
||||
|
||||
/**
|
||||
* 底层Request对象
|
||||
*/
|
||||
ServerWebExchange exchange;
|
||||
|
||||
/**
|
||||
* 实例化
|
||||
* @param exchange exchange对象
|
||||
*/
|
||||
public SaStorageForReactor(ServerWebExchange exchange) {
|
||||
this.exchange = exchange;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层源对象
|
||||
*/
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return exchange;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里写入一个值
|
||||
*/
|
||||
@Override
|
||||
public void set(String key, Object value) {
|
||||
exchange.getAttributes().put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里获取一个值
|
||||
*/
|
||||
@Override
|
||||
public Object get(String key) {
|
||||
return exchange.getAttributes().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里删除一个值
|
||||
*/
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
exchange.getAttributes().remove(key);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* sa-token集成Reactor响应式编程的各个组件
|
||||
*/
|
||||
package cn.dev33.satoken.reactor;
|
@@ -0,0 +1,37 @@
|
||||
package cn.dev33.satoken.reactor.spring;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaPathMatcherHolder {
|
||||
|
||||
/**
|
||||
* 路由匹配器
|
||||
*/
|
||||
public static PathMatcher pathMatcher;
|
||||
|
||||
/**
|
||||
* 获取路由匹配器
|
||||
* @return 路由匹配器
|
||||
*/
|
||||
public static PathMatcher getPathMatcher() {
|
||||
if(pathMatcher == null) {
|
||||
pathMatcher = new AntPathMatcher();
|
||||
}
|
||||
return pathMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入路由匹配器
|
||||
* @param pathMatcher 路由匹配器
|
||||
*/
|
||||
public static void setPathMatcher(PathMatcher pathMatcher) {
|
||||
SaPathMatcherHolder.pathMatcher = pathMatcher;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,138 @@
|
||||
package cn.dev33.satoken.reactor.spring;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.action.SaTokenAction;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
import cn.dev33.satoken.context.SaTokenContextForThreadLocal;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.filter.SaFilterErrorStrategy;
|
||||
import cn.dev33.satoken.filter.SaFilterStrategy;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
|
||||
/**
|
||||
* 利用spring的自动装配来加载开发者重写的Bean
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class SaTokenSpringAutowired {
|
||||
|
||||
/**
|
||||
* 获取配置Bean
|
||||
*
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.sa-token")
|
||||
public SaTokenConfig getSaTokenConfig() {
|
||||
return new SaTokenConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入配置Bean
|
||||
*
|
||||
* @param saTokenConfig 配置对象
|
||||
*/
|
||||
@Autowired
|
||||
public void setConfig(SaTokenConfig saTokenConfig) {
|
||||
SaTokenManager.setConfig(saTokenConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入持久化Bean
|
||||
*
|
||||
* @param saTokenDao SaTokenDao对象
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaTokenDao(SaTokenDao saTokenDao) {
|
||||
SaTokenManager.setSaTokenDao(saTokenDao);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入权限认证Bean
|
||||
*
|
||||
* @param stpInterface StpInterface对象
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setStpInterface(StpInterface stpInterface) {
|
||||
SaTokenManager.setStpInterface(stpInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入框架行为Bean
|
||||
*
|
||||
* @param saTokenAction SaTokenAction对象
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaTokenAction(SaTokenAction saTokenAction) {
|
||||
SaTokenManager.setSaTokenAction(saTokenAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取容器交互Bean (ThreadLocal版)
|
||||
*
|
||||
* @return 容器交互Bean (ThreadLocal版)
|
||||
*/
|
||||
@Bean
|
||||
public SaTokenContext getSaTokenContext() {
|
||||
return new SaTokenContextForThreadLocal() {
|
||||
/**
|
||||
* 重写路由匹配方法
|
||||
*/
|
||||
@Override
|
||||
public boolean matchPath(String pattern, String path) {
|
||||
return SaPathMatcherHolder.getPathMatcher().match(pattern, path);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入容器交互Bean
|
||||
*
|
||||
* @param saTokenContext SaTokenContext对象
|
||||
*/
|
||||
@Autowired
|
||||
public void setSaTokenContext(SaTokenContext saTokenContext) {
|
||||
SaTokenManager.setSaTokenContext(saTokenContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入[sa-token全局过滤器-认证策略]
|
||||
*
|
||||
* @param strategy see note
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaFilterStrategy(SaFilterStrategy strategy) {
|
||||
SaTokenManager.setSaFilterStrategy(strategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入[sa-token全局过滤器-异常处理策略]
|
||||
*
|
||||
* @param errorStrategy see note
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaFilterErrorStrategy(SaFilterErrorStrategy errorStrategy) {
|
||||
SaTokenManager.setSaFilterErrorStrategy(errorStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 利用自动匹配特性,获取SpringMVC框架内部使用的路由匹配器
|
||||
*
|
||||
* @param pathMatcher 要设置的 pathMatcher
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
SaPathMatcherHolder.setPathMatcher(pathMatcher);
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.reactor.spring.SaTokenSpringAutowired
|
@@ -16,7 +16,7 @@
|
||||
<description>sa-token authentication by Sservlet API</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- sa-token-spring-boot-starter -->
|
||||
<!-- sa-token-core -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-core</artifactId>
|
||||
@@ -27,6 +27,7 @@
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package cn.dev33.satoken.context.model.servlet;
|
||||
package cn.dev33.satoken.servlet.model;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -33,30 +33,6 @@ public class SaRequestForServlet implements SaRequest {
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里写入一个值
|
||||
*/
|
||||
@Override
|
||||
public void setAttribute(String name, Object value) {
|
||||
request.setAttribute(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里获取一个值
|
||||
*/
|
||||
@Override
|
||||
public Object getAttribute(String name) {
|
||||
return request.getAttribute(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里删除一个值
|
||||
*/
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
request.removeAttribute(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [请求体] 里获取一个值
|
||||
*/
|
@@ -1,4 +1,4 @@
|
||||
package cn.dev33.satoken.context.model.servlet;
|
||||
package cn.dev33.satoken.servlet.model;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
@@ -0,0 +1,59 @@
|
||||
package cn.dev33.satoken.servlet.model;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
|
||||
/**
|
||||
* Storage for Servlet
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaStorageForServlet implements SaStorage {
|
||||
|
||||
/**
|
||||
* 底层Request对象
|
||||
*/
|
||||
HttpServletRequest request;
|
||||
|
||||
/**
|
||||
* 实例化
|
||||
* @param request request对象
|
||||
*/
|
||||
public SaStorageForServlet(HttpServletRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层源对象
|
||||
*/
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里写入一个值
|
||||
*/
|
||||
@Override
|
||||
public void set(String key, Object value) {
|
||||
request.setAttribute(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里获取一个值
|
||||
*/
|
||||
@Override
|
||||
public Object get(String key) {
|
||||
return request.getAttribute(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里删除一个值
|
||||
*/
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
request.removeAttribute(key);
|
||||
}
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
* Sa-Token对接ServletAPI容器所需要的实现类接口包
|
||||
*/
|
||||
package cn.dev33.satoken.context.model.servlet;
|
||||
package cn.dev33.satoken.servlet;
|
@@ -10,6 +10,7 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
/**
|
||||
* sa-token 基于 Spring Aop 的注解鉴权
|
||||
@@ -18,14 +19,9 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Order(SaCheckAspect.aspectOrder)
|
||||
@Order(SaTokenConsts.ASSEMBLY_ORDER)
|
||||
public class SaCheckAspect {
|
||||
|
||||
/**
|
||||
* 切面执行顺序
|
||||
*/
|
||||
public static final int aspectOrder = -100;
|
||||
|
||||
/**
|
||||
* 构建
|
||||
*/
|
||||
|
@@ -1,25 +0,0 @@
|
||||
package cn.dev33.satoken.autowired;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* 将此注解加到springboot启动类上,即可完成sa-token与springboot的集成
|
||||
* <p>注: v1.7版本以上已不再需要此注解,直接引入sa-token-spring-boot-starter依赖即可
|
||||
* <p>请直接忽略此注解
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Documented
|
||||
@Target({java.lang.annotation.ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Configuration
|
||||
@Import({SaTokenSpringAutowired.class})
|
||||
public @interface SaTokenSetup {
|
||||
|
||||
}
|
@@ -5,9 +5,9 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import cn.dev33.satoken.context.model.servlet.SaRequestForServlet;
|
||||
import cn.dev33.satoken.context.model.servlet.SaResponseForServlet;
|
||||
import cn.dev33.satoken.router.SaRouteFunction;
|
||||
import cn.dev33.satoken.servlet.model.SaRequestForServlet;
|
||||
import cn.dev33.satoken.servlet.model.SaResponseForServlet;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
|
@@ -0,0 +1,37 @@
|
||||
package cn.dev33.satoken.spring;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaPathMatcherHolder {
|
||||
|
||||
/**
|
||||
* 路由匹配器
|
||||
*/
|
||||
public static PathMatcher pathMatcher;
|
||||
|
||||
/**
|
||||
* 获取路由匹配器
|
||||
* @return 路由匹配器
|
||||
*/
|
||||
public static PathMatcher getPathMatcher() {
|
||||
if(pathMatcher == null) {
|
||||
pathMatcher = new AntPathMatcher();
|
||||
}
|
||||
return pathMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入路由匹配器
|
||||
* @param pathMatcher 路由匹配器
|
||||
*/
|
||||
public static void setPathMatcher(PathMatcher pathMatcher) {
|
||||
SaPathMatcherHolder.pathMatcher = pathMatcher;
|
||||
}
|
||||
|
||||
}
|
@@ -1,13 +1,12 @@
|
||||
package cn.dev33.satoken.spring;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.context.model.servlet.SaRequestForServlet;
|
||||
import cn.dev33.satoken.context.model.servlet.SaResponseForServlet;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.servlet.model.SaRequestForServlet;
|
||||
import cn.dev33.satoken.servlet.model.SaResponseForServlet;
|
||||
import cn.dev33.satoken.servlet.model.SaStorageForServlet;
|
||||
|
||||
/**
|
||||
* sa-token 对Cookie的相关操作 接口实现类
|
||||
@@ -34,27 +33,11 @@ public class SaTokenContextForSpring implements SaTokenContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配器
|
||||
* 获取当前请求的 [存储器] 对象
|
||||
*/
|
||||
private static PathMatcher pathMatcher;
|
||||
|
||||
/**
|
||||
* 获取路由匹配器
|
||||
* @return 路由匹配器
|
||||
*/
|
||||
public static PathMatcher getPathMatcher() {
|
||||
if(pathMatcher == null) {
|
||||
pathMatcher = new AntPathMatcher();
|
||||
}
|
||||
return pathMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入路由匹配器
|
||||
* @param pathMatcher 路由匹配器
|
||||
*/
|
||||
public static void setPathMatcher(PathMatcher pathMatcher) {
|
||||
SaTokenContextForSpring.pathMatcher = pathMatcher;
|
||||
@Override
|
||||
public SaStorage getStorage() {
|
||||
return new SaStorageForServlet(SpringMVCUtil.getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +45,9 @@ public class SaTokenContextForSpring implements SaTokenContext {
|
||||
*/
|
||||
@Override
|
||||
public boolean matchPath(String pattern, String path) {
|
||||
return getPathMatcher().match(pattern, path);
|
||||
return SaPathMatcherHolder.getPathMatcher().match(pattern, path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package cn.dev33.satoken.autowired;
|
||||
package cn.dev33.satoken.spring;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@@ -11,7 +11,8 @@ import cn.dev33.satoken.action.SaTokenAction;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.spring.SaTokenContextForSpring;
|
||||
import cn.dev33.satoken.filter.SaFilterErrorStrategy;
|
||||
import cn.dev33.satoken.filter.SaFilterStrategy;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
|
||||
/**
|
||||
@@ -94,6 +95,26 @@ public class SaTokenSpringAutowired {
|
||||
SaTokenManager.setSaTokenContext(saTokenContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入[sa-token全局过滤器-认证策略]
|
||||
*
|
||||
* @param strategy see note
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaFilterStrategy(SaFilterStrategy strategy) {
|
||||
SaTokenManager.setSaFilterStrategy(strategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入[sa-token全局过滤器-异常处理策略]
|
||||
*
|
||||
* @param errorStrategy see note
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaFilterErrorStrategy(SaFilterErrorStrategy errorStrategy) {
|
||||
SaTokenManager.setSaFilterErrorStrategy(errorStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 利用自动匹配特性,获取SpringMVC框架内部使用的路由匹配器
|
||||
*
|
||||
@@ -101,7 +122,7 @@ public class SaTokenSpringAutowired {
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
SaTokenContextForSpring.setPathMatcher(pathMatcher);
|
||||
SaPathMatcherHolder.setPathMatcher(pathMatcher);
|
||||
}
|
||||
|
||||
|
@@ -1 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.autowired.SaTokenSpringAutowired
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.spring.SaTokenSpringAutowired
|
Reference in New Issue
Block a user