一個輕量級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 + "}"; } }
自定義異常類 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/