一个轻量级java权限认证框架,让鉴权变得简单、优雅(官方文档描述 哈哈)
一、pom依赖
<dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.27.0</version> </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 + "}"; } }View Code
自定义异常类 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); } }View Code
根据api搭建大概简单的鉴权框架
更多详细其他配置信息见官网:https://sa-token.dev33.cn/