SaRouterUtil迁移到core核心包,优化依赖架构

This commit is contained in:
shengzhang 2021-03-22 17:02:09 +08:00
parent 36afaf74d3
commit c574e7f8d7
15 changed files with 126 additions and 281 deletions

View File

@ -160,12 +160,7 @@ public class SaTokenManager {
* @return 对应的StpLogic
*/
public static StpLogic getStpLogic(String loginKey) {
for (String key : stpLogicMap.keySet()) {
if(key.equals(loginKey)) {
return stpLogicMap.get(key);
}
}
return null;
return stpLogicMap.get(loginKey);
}

View File

@ -1,4 +1,4 @@
package cn.dev33.satoken.interceptor;
package cn.dev33.satoken.router;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

View File

@ -1,13 +1,9 @@
package cn.dev33.satoken.interceptor;
package cn.dev33.satoken.router;
import java.util.Arrays;
import java.util.List;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import cn.dev33.satoken.SaTokenManager;
import cn.dev33.satoken.autowired.SaTokenSpringAutowired;
import cn.dev33.satoken.fun.IsRunFunction;
import cn.dev33.satoken.fun.SaFunction;
@ -18,32 +14,6 @@ import cn.dev33.satoken.fun.SaFunction;
*/
public class SaRouterUtil {
/**
* 在进行路由匹配时所使用的 PathMatcher 对象
*/
private static PathMatcher pathMatcher;
/**
* @return 在进行路由匹配时所使用的的 PathMatcher 对象
*/
public static PathMatcher getPathMatcher() {
if(pathMatcher == null) {
pathMatcher = SaTokenSpringAutowired.pathMatcher;
if(pathMatcher == null) {
pathMatcher = new AntPathMatcher();
}
}
return pathMatcher;
}
/**
* @param pathMatcher 写入: 在进行路由匹配时所使用的的 PathMatcher 对象
*/
public static void setPathMatcher(PathMatcher pathMatcher) {
SaRouterUtil.pathMatcher = pathMatcher;
}
// -------------------- 路由匹配相关 --------------------
/**
@ -53,10 +23,7 @@ public class SaRouterUtil {
* @return 是否匹配成功
*/
public static boolean isMatch(String pattern, String path) {
if(getPathMatcher().match(pattern, path)) {
return true;
}
return false;
return SaTokenManager.getSaTokenServlet().matchPath(pattern, path);
}
/**

View File

@ -25,4 +25,12 @@ public interface SaTokenServlet {
*/
public HttpServletResponse getResponse();
/**
* 校验指定路由匹配符是否可以匹配成功指定路径
* @param pattern 路由匹配符
* @param path 需要匹配的路径
* @return 是否匹配成功
*/
public boolean matchPath(String pattern, String path);
}

View File

@ -18,7 +18,7 @@ public class SaTokenServletDefaultImpl implements SaTokenServlet {
*/
@Override
public HttpServletRequest getRequest() {
throw new SaTokenException("请实现SaTokenServlet接口后进行Servlet相关操作");
throw new SaTokenException("SaTokenServlet接口未实现");
}
/**
@ -26,7 +26,16 @@ public class SaTokenServletDefaultImpl implements SaTokenServlet {
*/
@Override
public HttpServletResponse getResponse() {
throw new SaTokenException("请实现SaTokenServlet接口后进行Servlet相关操作");
throw new SaTokenException("SaTokenServlet接口未实现");
}
/**
* 校验指定路由匹配符是否可以匹配成功指定路径
*/
@Override
public boolean matchPath(String pattern, String path) {
throw new SaTokenException("SaTokenServlet接口未实现");
}
}

View File

@ -4,7 +4,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
/**
@ -15,20 +14,6 @@ import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
@Configuration
public class MySaTokenConfig implements WebMvcConfigurer {
// 获取配置Bean (以代码的方式配置sa-token, 此配置会覆盖yml中的配置 )
// @Primary
// @Bean(name="MySaTokenConfig")
public SaTokenConfig getSaTokenConfig() {
SaTokenConfig config = new SaTokenConfig();
config.setTokenName("satoken"); // token名称 (同时也是cookie名称)
config.setTimeout(30 * 24 * 60 * 60); // token有效期单位s 默认30天
config.setActivityTimeout(-1); // token临时有效期 (指定时间内无操作就视为token过期) 单位:
config.setAllowConcurrentLogin(true); // 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
config.setIsShare(true); // 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
config.setTokenStyle("uuid"); // token风格
return config;
}
// 注册sa-token的拦截器打开注解式鉴权功能
@Override
public void addInterceptors(InterceptorRegistry registry) {

View File

@ -5,9 +5,10 @@ import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import com.pj.util.AjaxJson;
@ -18,7 +19,7 @@ import cn.dev33.satoken.exception.NotRoleException;
/**
* 全局异常处理
*/
@RestControllerAdvice // 可指定包前缀比如(basePackages = "com.pj.admin")
@ControllerAdvice // 可指定包前缀比如(basePackages = "com.pj.admin")
public class GlobalException {
// 在当前类每个方法进入之前触发的操作
@ -28,14 +29,14 @@ public class GlobalException {
}
// 全局异常拦截拦截项目中的所有异常
@ResponseBody
@ExceptionHandler
public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 打印堆栈以供调试
System.out.println("全局异常---------------");
e.printStackTrace();
// 不同异常返回不同状态码

View File

@ -3,8 +3,6 @@ package com.pj.test;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@ -241,7 +239,7 @@ public class TestController {
// 测试 浏览器访问 http://localhost:8081/test/test
@RequestMapping("test")
public AjaxJson test(HttpServletResponse response) {
public AjaxJson test() {
System.out.println("进来了");
// StpUtil.setLoginId(10001, new SaLoginModel()
// .setDevice("PC") // 此次登录的客户端设备标识, 用于[同端互斥登录]时指定此次登录的设备名称

View File

@ -17,6 +17,7 @@
- [注解式鉴权](/use/at-check)
- [路由拦截式鉴权](/use/route-check)
- [花式token](/use/token-style)
- [Token前缀](/use/token-prefix)
- [框架配置](/use/config)
- [会话治理](/use/search-session)
- [记住我模式](/use/remember-me)

View File

@ -71,4 +71,5 @@ public class MySaTokenConfig {
| dataRefreshPeriod | int | 30 | 默认dao层实现类中每次清理过期数据间隔的时间 (单位: 秒) 默认值30秒设置为-1代表不启动定时清理 |
| tokenSessionCheckLogin | Boolean | true | 获取token专属session时是否必须登录 (如果配置为true会在每次获取token专属session时校验是否登录) |
| autoRenew | Boolean | true | 是否打开自动续签 (如果此值为true, 框架会在每次直接或间接调用getLoginId()时进行一次过期检查与续签操作) |
| tokenPrefix | Boolean | true | token前缀, 格式样例(satoken: Bearer xxxx-xxxx-xxxx-xxxx) [参考token前缀](/use/token-prefix) |
| isV | Boolean | true | 是否在初始化配置时打印版本字符画 |

View File

@ -99,8 +99,18 @@ public class MySaTokenConfig implements WebMvcConfigurer {
## 注意事项
在`v1.14`及以前版本下,路由拦截器提供了提供了封装式写法,该方法代码比较冗余,在`v1.15`版本已移除,解决方案如下:
``` java
// 原写法
registry.addInterceptor(SaRouteInterceptor.createPermissionVal("user")).addPathPatterns("/user/**");
// 改为以下方式,效果同上
registry.addInterceptor(new SaRouteInterceptor((request, response, handler) -> {
SaRouterUtil.match("/user/**", () -> StpUtil.checkPermission("user"));
})).addPathPatterns("/**");
```

View File

@ -0,0 +1,32 @@
# Token前缀
### 需求场景
在某些系统中前端提交token时会在前面加个固定的前缀例如
``` js
{
"satoken": "Bearer xxxx-xxxx-xxxx-xxxx"
}
```
此时后端如果不做任何特殊处理,框架将会把`Bearer `视为token的一部分无法正常读取token信息导致鉴权失败
为此我们需要在yml中添加如下配置
``` java
spring:
# sa-token配置
sa-token:
# token前缀
tokenPrefix: Bearer
```
此时 sa-token 便可在读取token时裁剪掉 `Bearer`,成功获取`xxxx-xxxx-xxxx-xxxx`
### 注意点
1. `token前缀``token值` 之间必须有一个空格
2. 一旦配置了`token前缀`则前端提交token时必须带有前缀否则会导致框架无法读取token
3. 由于`Cookie`中无法存储空格字符也就意味配置token前缀后`Cookie`鉴权方式将会失效此时只能将token提交到`header`里进行传输

View File

@ -105,19 +105,15 @@ public class SaTokenSpringAutowired {
SaTokenManager.setSaTokenServlet(saTokenServlet);
}
/**
* 路由匹配器
*/
public static PathMatcher pathMatcher;
/**
* 利用自动匹配特性获取SpringMVC框架内部使用的路由匹配器
*
* @param pathMatcher 要设置的 pathMatcher
*/
@Autowired(required = false)
public static void setPathMatcher(PathMatcher pathMatcher) {
SaTokenSpringAutowired.pathMatcher = pathMatcher;
public void setPathMatcher(PathMatcher pathMatcher) {
SaTokenServletSpringImpl.setPathMatcher(pathMatcher);
}
}

View File

@ -5,8 +5,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.router.SaRouteFunction;
import cn.dev33.satoken.stp.StpUtil;
/**
@ -15,204 +14,25 @@ import cn.dev33.satoken.stp.StpUtil;
*/
public class SaRouteInterceptor implements HandlerInterceptor {
// ----------------- 属性 -----------------
/**
* 底层的 StpLogic 对象
*/
private StpLogic stpLogic;
/**
* 验证类型 (1=登录验证, 2=角色验证, 3=权限验证, 4=自定义验证)
*/
private int type;
/**
* 验证模式 AND | OR
*/
private SaMode mode;
/**
* 标识码数组
*/
private String[] codes;
/**
* 自定义模式下的执行函数
*/
private SaRouteFunction function;
/**
* 表示登录验证
*/
public static final int LOGIN = 1;
/**
* 表示角色验证
*/
public static final int ROLE = 2;
/**
* 表示权限验证
*/
public static final int PERMISSION = 3;
/**
* 表示自定义验证
*/
public static final int CUSTOM = 4;
/**
* @return 底层的 StpLogic 对象
*/
public StpLogic getStpLogic() {
if(stpLogic == null) {
stpLogic = StpUtil.stpLogic;
}
return stpLogic;
}
/**
* @param stpLogic 底层的 StpLogic 对象
* @return 拦截器自身
*/
public SaRouteInterceptor setStpLogic(StpLogic stpLogic) {
this.stpLogic = stpLogic;
return this;
}
/**
* @return 验证类型 (1=登录验证, 2=角色验证, 3=权限验证, 4=自定义验证)
*/
public int getType() {
return type;
}
/**
* @param type 验证类型 (1=登录验证, 2=角色验证, 3=权限验证, 4=自定义验证)
* @return 拦截器自身
*/
public SaRouteInterceptor setType(int type) {
this.type = type;
return this;
}
/**
* @return 验证模式 AND | OR
*/
public SaMode getMode() {
return mode;
}
/**
* @param mode 验证模式 AND | OR
* @return 拦截器自身
*/
public SaRouteInterceptor setMode(SaMode mode) {
this.mode = mode;
return this;
}
/**
* @return 标识码数组
*/
public String[] getCodes() {
return codes;
}
/**
* @param codes 标识码数组
* @return 拦截器自身
*/
public SaRouteInterceptor setCodes(String... codes) {
this.codes = codes;
return this;
}
/**
* @return 自定义模式下的执行函数
*/
public SaRouteFunction getFunction() {
return function;
}
/**
* @param function 设置自定义模式下的执行函数
* @return 拦截器自身
*/
public SaRouteInterceptor setFunction(SaRouteFunction function) {
this.type = SaRouteInterceptor.CUSTOM;
this.function = function;
return this;
}
// ----------------- 构建相关 -----------------
/**
* 创建 (全参数)
* @param type 验证类型 (1=登录验证, 2=角色验证, 3=权限验证, 4=自定义验证)
* @param mode 验证模式 AND | OR
* @param codes 标识码数组
*/
public SaRouteInterceptor(int type, SaMode mode, String... codes) {
super();
this.type = type;
this.mode = mode;
this.codes = codes;
}
/**
* 创建 (默认为登录验证)
*/
public SaRouteInterceptor() {
this(SaRouteInterceptor.LOGIN, null, new String[0]);
}
public SaRouteFunction function;
/**
* 创建 (默认为自定义认证)
* @param function 自定义模式下的执行函数
*/
public SaRouteInterceptor(SaRouteFunction function) {
this(SaRouteInterceptor.CUSTOM, null, new String[0]);
setFunction(function);
this.function = function;
}
/**
* 构建一个模式为登录认证的sa路由拦截器
* @return sa拦截器
*/
public static SaRouteInterceptor createLoginVal() {
return new SaRouteInterceptor();
}
/**
* 构建一个模式为角色认证的sa路由拦截器
* @param roles 需要验证的角色标识列表
* @return sa拦截器
*/
public static SaRouteInterceptor createRoleVal(String... roles) {
return new SaRouteInterceptor(SaRouteInterceptor.ROLE, SaMode.AND, roles);
}
/**
* 构建一个模式为权限认证的sa路由拦截器
* @param permissions 需要验证的权限列表
* @return sa拦截器
*/
public static SaRouteInterceptor createPermissionVal(String... permissions) {
return new SaRouteInterceptor(SaRouteInterceptor.PERMISSION, SaMode.AND, permissions);
}
/**
* 创建一个模式为自定义认证的sa路由拦截器
* 静态方法快速构建一个
* @param function 自定义模式下的执行函数
* @return sa拦截器
* @return sa路由拦截器
*/
public static SaRouteInterceptor createCustomVal(SaRouteFunction function) {
public static SaRouteInterceptor newInstance(SaRouteFunction function) {
return new SaRouteInterceptor(function);
}
@ -226,22 +46,10 @@ public class SaRouteInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 根据模式进行验证
if(this.type == SaRouteInterceptor.LOGIN) {
getStpLogic().checkLogin();
} else if(this.type == SaRouteInterceptor.ROLE) {
if(mode == SaMode.AND) {
getStpLogic().checkRoleAnd(codes);
// 如果未提供function默认进行登录验证
if(function == null) {
StpUtil.checkLogin();
} else {
getStpLogic().checkRoleOr(codes);
}
} else if(this.type == SaRouteInterceptor.PERMISSION) {
if(mode == SaMode.AND) {
getStpLogic().checkPermissionAnd(codes);
} else {
getStpLogic().checkPermissionOr(codes);
}
} else if(this.type == SaRouteInterceptor.CUSTOM) {
function.run(request, response, handler);
}
@ -250,6 +58,4 @@ public class SaRouteInterceptor implements HandlerInterceptor {
}
}

View File

@ -3,10 +3,13 @@ package cn.dev33.satoken.spring;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import cn.dev33.satoken.servlet.SaTokenServlet;
/**
* sa-token cookie的相关操作 接口实现类
* sa-token Cookie的相关操作 接口实现类
*
* @author kong
*
@ -29,4 +32,37 @@ public class SaTokenServletSpringImpl implements SaTokenServlet {
return SpringMVCUtil.getResponse();
}
/**
* 路由匹配器
*/
private static PathMatcher pathMatcher;
/**
* 获取路由匹配器
* @param pathMatcher 路由匹配器
*/
public static PathMatcher getPathMatcher() {
if(pathMatcher == null) {
pathMatcher = new AntPathMatcher();
}
return pathMatcher;
}
/**
* 写入路由匹配器
* @param pathMatcher 路由匹配器
*/
public static void setPathMatcher(PathMatcher pathMatcher) {
SaTokenServletSpringImpl.pathMatcher = pathMatcher;
}
/**
* 校验指定路由匹配符是否可以匹配成功指定路径
*/
@Override
public boolean matchPath(String pattern, String path) {
return getPathMatcher().match(pattern, path);
}
}