对接SaToken @SaCheckEL 鉴权注解
对接SaToken @SaCheckEL 鉴权注解
文章目录
- 对接SaToken @SaCheckEL 鉴权注解
- 前言
- 一、引入插件和配置SaToken属性配置
- 二、配置
- 1.自定义SaTokenInterceptor并注入Bean
- 2.WebMvcConfig配置SaTokenInterceptor,拦截所有请求路径
- 三、重载SaToken权限接口和方法注入@SaCheckEL
- 1.重载SaToken权限接口
- 2.控制层注入@SaCheckEL
前言
原生SpringBoot 2.6.3、Spring 5.3.25(JDK8) 框架系统 对接SaToken 身份认证,使用 SpEL 表达式进行资源请求的鉴权。
一、引入插件和配置SaToken属性配置
<!-- Sa-Token 权限认证,用于启动SaTokenContext实现 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.40.0</version>
</dependency>
<!-- Sa-Token 注解鉴权使用 EL 表达式 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-el</artifactId>
<version>1.40.0</version>
</dependency>
sa-token:
# token前缀
token-prefix: Bearer
# token名称 (同时也是cookie名称)
token-name: Token
# token有效期,单位s 默认12小时, -1代表永不过期
timeout: 43200
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
active-timeout: -1
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: false
# token风格
token-style: uuid
# 是否输出操作日志
is-log: false
二、配置
1.自定义SaTokenInterceptor并注入Bean
SaTokenInterceptor类
@Component
@Slf4j
public class SaTokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
log.debug("------------>进入拦截器SaTokenInterceptor:{}", handler.toString());
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "86400");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
//当请求路径包含/doc.html(swagger文档),跳过Token验证
if ( swaggerDocSkipToken(request.getRequestURI())) {
return true;
}
String token = request.getHeader("satoken");
String baseToken = request.getHeader("token");
SaSession tokenSession = null;
if (StrUtil.isEmpty(baseToken) || StrUtil.isEmpty(token)) {
PrintWriter out = response.getWriter();
QuantdoResponse resp = new QuantdoResponse();
log.info("SaTokenInterceptor:Satoken=" + token + ",Token=" + baseToken + "为空!");
resp.setCode(OrderNotLoginException.TOKEN_TIMEOUT_CODE);
resp.setMessage(OrderNotLoginException.TOKEN_TIMEOUT_MESSAGE);
out.append(JSONObject.toJSONString(resp));
return false;
}
if (StrUtil.isNotBlank(token)) {
tokenSession = StpUtil.getTokenSessionByToken(token);
}
if (tokenSession != null ) {
Long userId = (Long) tokenSession.getLoginId();
return true;
} else {
PrintWriter out = response.getWriter();
QuantdoResponse resp = new QuantdoResponse();
log.info("SaTokenInterceptor:未获取到登录用户信息!"); resp.setCode(OrderNotLoginException.NOT_VALID_EMP_CODE);
return false;
}
}
else{
PrintWriter out = response.getWriter();
QuantdoResponse resp = new QuantdoResponse();
log.info("SaTokenInterceptor:未获取到令牌satoken!");
resp.setCode(OrderNotLoginException.TOKEN_TIMEOUT_CODE);
out.append(JSONObject.toJSONString(resp));
return false;
}
}
//Swagger Doc
public static final boolean swaggerDocSkipToken(String requestURL){
if (requestURL.contains("/doc.html") || requestURL.contains(".js") || requestURL.contains(".css")
|| requestURL.contains(".ico") || requestURL.contains("/swagger-resources")
|| requestURL.contains("/api-docs") ) {
return true;
}
else{
return false;
}
}
}
SaTokenConfig 类注入SaTokenInterceptor Bean
package com.isoftstone.order.service.config;
import com.isoftstone.order.service.interceptor.SaTokenInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SaTokenConfig {
@Bean
public SaTokenInterceptor getSaTokenInterceptor() {
return new SaTokenInterceptor();
}
}
2.WebMvcConfig配置SaTokenInterceptor,拦截所有请求路径
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private SaTokenInterceptor saTokenInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(saTokenInterceptor)
.addPathPatterns("/**") // 拦截所有请求路径
.excludePathPatterns("/login", "/assets/**");
// 排除登录和注册接口
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Long.class, ToStringSerializer.instance);
module.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(module);
objectMapper.configure(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
converter.setObjectMapper(objectMapper);
converters.add(0,converter);
}
}
三、重载SaToken权限接口和方法注入@SaCheckEL
1.重载SaToken权限接口
/**
* @Description 工单权限校验,完成 Sa-Token 的自定义权限验证扩展
* @Date 2025/2/18 16:11
* @Created by chenjun
*/
@Component
@Slf4j
public class OrderStpInterfaceImpl implements StpInterface {
@Autowired
private UserAndRoleService userAndRoleService;
@Resource
private StringRedisUtil stringRedisUtil;
public static final String env = "test";//'local'环境跳过Permission认证
/**
* 通过用户id查询用户基础资源表code集合,单个资源表code如:tenant:tenant:user:add
* @param loginId 账号id:登录defUser实体id
* @param loginType 账号类型
* @return "tenant:tenant:user|tenant:tenant:user:query|tenant:tenant:user:add|tenant:tenant:user:delete|tenant:tenant:user:edit";
*
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
List<String> resourceCodes = userAndRoleService.getMenuResourceCodeByUser(loginId);
return resourceCodes;
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
return null;
}
}
2.控制层注入@SaCheckEL
用户控制层UserRest 注入@SaCheckEL
@Api(tags = {"用户控制层"})
@RestController
@RequestMapping("/userRest/")
public class UserRest {
@Autowired
private UserService userService;
//用户查询
@SaCheckEL("stp.checkPermission('tenant:tenant:user:query')")
@GetMapping("")
public List<OrderWorkListResp> query(){
.....
}
//用户增加
@SaCheckEL("stp.checkPermission('tenant:tenant:user:add')")
@PostMapping("")
public boolean add(User vo){
.....
}
//用户编辑
@SaCheckEL("stp.checkPermission('tenant:tenant:user:edit')")
@PutMapping("")
public boolean edit(User vo){
.....
}
}
注明:
多个权限注入方法@SaCheckEL(“stp.checkPermissionOr(‘tenant:tenant:user:query’,‘tenant:tenant:user:add’)”)
@SaCheckEL(“stp.checkPermissionAnd(‘tenant:tenant:user:query’,‘tenant:tenant:user:add’)”)
参考 SpEL 表达式注解鉴权