一个轻量级java权限认证框架,让鉴权变得简单、优雅(官方文档描述 哈哈)
一、pom依赖
<dependency>
<groupId>cn.dev33groupId>
<artifactId>sa-token-spring-boot-starterartifactId>
<version>1.27.0version>
dependency>
二、yml文件配置
# Sa-Token配置
sa-token:
# token名称 (同时也是cookie名称)
token-name: satoken
# token有效期,单位s 默认30天, -1代表永不过期
timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
activity-timeout: -1
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: false
# token风格
token-style: simple-uuid
# 是否输出操作日志
is-log: false
# 是否打印sa-token标识
is-print: false
三、实现sa-token权限、角色信息注入
import cn.dev33.satoken.stp.StpInterface;
import com.subgame.dao.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author dly
* @Description 实现satoken权限、角色信息注入
* @Date 2021/11/6
* @Param
* @return
**/
@Component
public class StpInterfaceImpl implements StpInterface {
@Autowired
UserMapper userMapper;
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
List<String> list = new ArrayList<>();
list.add("user_get");return list;
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
String roles = userMapper.selectByPrimaryKey(Integer.valueOf(loginId.toString())).getRoles();
List<String> list = Arrays.asList(roles.split(","));
return list;
}
四、注册拦截器
import cn.dev33.satoken.interceptor.SaRouteInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册Sa-Token的路由拦截器
registry.addInterceptor(new SaRouteInterceptor(
(req, res, handler)-> {
// 根据路由划分模块,不同模块不同鉴权
SaRouter.match("/user/**", r -> StpUtil.checkPermission("user_get"));
SaRouter.match("/manager/**",r -> StpUtil.checkRole("ADMIN"));
}))
.addPathPatterns("/**")
.excludePathPatterns("/manager/adminLogin");
}
}
五、登录接口信息识别(根据实际情况自己写的实现类)
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.strategy.SaStrategy;
import com.subgame.base.RespResult;
import com.subgame.base.SubGameException;
import com.subgame.dao.UserMapper;
import com.subgame.model.User;
import com.subgame.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.UUID;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public RespResult adminLogin(User user, HttpServletResponse response) {
User userinfo = userMapper.selectUserByLoginName(user.getLoginName());
String saltPassword = SaSecureUtil.md5BySalt(user.getPassword(), User.salt);
if(null == userinfo||!saltPassword.equals(userinfo.getPassword())){
throw new SubGameException(SubGameException.LoginError,SubGameException.LoginError_Code);
}
StpUtil.login(userinfo.getId().toString());
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
response.setHeader(tokenInfo.getTokenName(),tokenInfo.getTokenValue());
RespResult result = new RespResult();
result.setData(userinfo);
return result;
}
}
六、异常处理
全局异常拦截(拦截项目中所有异常)
import cn.dev33.satoken.exception.DisableLoginException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import com.subgame.base.RespResult;
import com.subgame.base.SubGameException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ControllerAdvice
public class GlobalException {
// 全局异常拦截(拦截项目中的所有异常)
@ResponseBody
@ExceptionHandler
public RespResult handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 打印堆栈,以供调试
System.out.println("全局异常---------------");
e.printStackTrace();
// 不同异常返回不同状态码
RespResult result = new RespResult();
if (e instanceof NotLoginException) { // 如果是未登录异常
NotLoginException ee = (NotLoginException) e;
result.setMessage(ee.getMessage());
result.setReturnCode("-1");
}
else if(e instanceof NotRoleException) { // 如果是角色异常
NotRoleException ee = (NotRoleException) e;
result.setReturnCode("-2");
result.setMessage(" "+ee.getMessage());
}
else if(e instanceof NotPermissionException) { // 如果是权限异常
NotPermissionException ee = (NotPermissionException) e;
result.setReturnCode("-3");
result.setMessage("无此权限"+ ee.getCode());
}
else if(e instanceof DisableLoginException) { // 如果是被封禁异常
DisableLoginException ee = (DisableLoginException) e;
result.setReturnCode("-4");
result.setMessage("账号被封禁:" + ee.getDisableTime() + "秒后解封");
}else if(e instanceof SubGameException){
SubGameException ee = (SubGameException)e;
result.setReturnCode(ee.getCode());
result.setMessage(ee.getMessage());
}
else { // 普通异常, 输出:500 + 异常信息
result.setMessage(e.getMessage());
result.setReturnCode("-999");
}
// 返回给前端
return result;
}
}
封装返回对象信息RespResult
import java.io.Serializable;
public class RespResult<T> implements Serializable {
private static final long serialVersionUID = 5461950698245974856L;
// 返回代码
private String returnCode;
// 返回代码描述
private String message;
// 返回数据
private T data;
public RespResult() {
this.returnCode = "0";
this.message = "操作成功";
}
public String getReturnCode() {
return returnCode;
}
public void setReturnCode(String returnCode) {
this.returnCode = returnCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "{" + "\"" + "returnCode" + "\"" + ":" + returnCode + ", " + "\"" + "data" + "\"" + ":" + data + "}";
}
}
自定义异常类 SubGameException
import org.apache.ibatis.annotations.Case;
public class SubGameException extends RuntimeException{
public static final String LoginError = "用户名或密码错误";
public static final String LoginError_Code = "-5";
public String code;
public String getCode() {
return code;
}
public SubGameException(String message,String code){
super(message);
this.code = code;
}
public SubGameException(String message, Throwable cause) {
super(message, cause);
}
public SubGameException(Throwable cause) {
super(cause);
}
}
根据api搭建大概简单的鉴权框架
更多详细其他配置信息见官网:https://sa-token.dev33.cn/