目錄
概念
前置知識回顧
拿到UserInfo 用于自定義權限和角色的獲取邏輯
最后進行要進行 satoken 過濾器全局配置
概念
做權限認證的時候 我們首先要明確兩點
我們需要的角色有幾種 我們需要的權限有幾種
角色 分兩種
ADMIN 管理員 :可管理商品
CUSTIOMER 普通用戶 :
權限 分四種
BASIC 基本權限 :可瀏覽商品
AUTU 已實名認證權限 :可下單支付
FROZEN 被凍結用戶權限
NONE 沒有任何權限
前置知識回顧
在閱讀完 sa-token 的文檔后
我發現原來權限驗證如此簡單
我們只需要 使用StpUtil即可
這個在開發博客的時候寫過相關的
// 用戶已存在,直接登錄
StpUtil.login(userInfo.getUserId(), new SaLoginModel().setIsLastingCookie(loginParam.getRememberMe()).setTimeout(DEFAULT_LOGIN_SESSION_TIMEOUT));
// 將用戶信息存入會話
StpUtil.getSession().set(userInfo.getUserId().toString(), userInfo);
// 創建登錄結果對象
LoginVO loginVO = new LoginVO(userInfo);
// 返回登錄成功響應
return Result.success(loginVO);
拿到UserInfo 用于自定義權限和角色的獲取邏輯
我們在用 StpUtil 去拿這個登錄信息
進行校驗
很容易理解 我們在登錄模塊 放入了一個 UserInfo 對象進去
包含了用戶了信息
package cn.hollis.nft.turbo.api.user.response.data;import cn.hollis.nft.turbo.api.user.constant.UserRole;
import cn.hollis.nft.turbo.api.user.constant.UserStateEnum;
import com.github.houbb.sensitive.annotation.strategy.SensitiveStrategyPhone;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;import java.util.Date;/*** @author Hollis*/
@Getter
@Setter
@NoArgsConstructor
public class UserInfo extends BasicUserInfo {private static final long serialVersionUID = 1L;/*** 手機號*/@SensitiveStrategyPhoneprivate String telephone;/*** 狀態** @see UserStateEnum*/private String state;/*** 區塊鏈地址*/private String blockChainUrl;/*** 區塊鏈平臺*/private String blockChainPlatform;/*** 實名認證*/private Boolean certification;/*** 用戶角色*/private UserRole userRole;/*** 邀請碼*/private String inviteCode;/*** 注冊時間*/private Date createTime;public boolean userCanBuy() {if (this.getUserRole() != null && !this.getUserRole().equals(UserRole.CUSTOMER)) {return false;}// 判斷買家狀態if (this.getState() != null && !this.getState().equals(UserStateEnum.ACTIVE.name())) {return false;}// 判斷買家狀態if (this.getState() != null && !this.getCertification()) {return false;}return true;}
}
我們sa-token 提供的接口中重寫權限校驗方法
實現stpInterface 調用處理這個接口方法
我們就是再把這個對象拿出來
// 根據用戶登錄ID和登錄類型返回不同的權限列表
UserInfo userInfo = (UserInfo) StpUtil.getSessionByLoginId(loginId).get((String) loginId);
然后找到權限 返回枚舉值
枚舉值要包裝成 list 類型
這邊返回的是List類型
這些文檔里都有...
package cn.hollis.nft.turbo.gateway.auth;import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil;
import cn.hollis.nft.turbo.api.user.constant.UserPermission;
import cn.hollis.nft.turbo.api.user.constant.UserRole;
import cn.hollis.nft.turbo.api.user.constant.UserStateEnum;
import cn.hollis.nft.turbo.api.user.response.data.UserInfo;
import org.springframework.stereotype.Component;import java.util.List;/*** 自定義權限驗證接口實現類* StpInterface 接口用于自定義權限和角色的獲取邏輯。* StpInterfaceImpl 類實現了 StpInterface 接口,通過用戶的會話信息動態獲取用戶的權限和角色列表。* 在 Sa - Token 框架進行權限驗證時,會調用該類的方法來確定用戶是否具備相應的權限和角色。* 注意:這邊通過用戶會話信息動態獲取用戶權限和角色列表** @author Hollis*/
@Component
public class StpInterfaceImpl implements StpInterface {/*** 根據用戶的登錄 ID 和登錄類型獲取用戶的權限列表** @param loginId 用戶的登錄 ID* @param loginType 用戶的登錄類型* @return 用戶的權限列表,以字符串集合形式返回*/@Overridepublic List<String> getPermissionList(Object loginId, String loginType) {// 從會話中根據登錄 ID 獲取用戶信息UserInfo userInfo = (UserInfo) StpUtil.getSessionByLoginId(loginId).get((String) loginId);// 如果用戶角色是管理員,或者用戶狀態為激活狀態、已認證狀態if (userInfo.getUserRole() == UserRole.ADMIN|| userInfo.getState().equals(UserStateEnum.ACTIVE.name())|| userInfo.getState().equals(UserStateEnum.AUTH.name()) ) {// 賦予用戶基礎權限和認證權限return List.of(UserPermission.BASIC.name(), UserPermission.AUTH.name());}// 如果用戶狀態為初始狀態if (userInfo.getState().equals(UserStateEnum.INIT.name())) {// 賦予用戶基礎權限return List.of(UserPermission.BASIC.name());}// 如果用戶狀態為凍結狀態if (userInfo.getState().equals(UserStateEnum.FROZEN.name())) {// 賦予用戶凍結權限return List.of(UserPermission.FROZEN.name());}// 其他情況,賦予用戶無權限return List.of(UserPermission.NONE.name());}/*** 根據用戶的登錄 ID 和登錄類型獲取用戶的角色列表** @param loginId 用戶的登錄 ID* @param loginType 用戶的登錄類型* @return 用戶的角色列表,以字符串集合形式返回*/@Overridepublic List<String> getRoleList(Object loginId, String loginType) {// 從會話中根據登錄 ID 獲取用戶信息UserInfo userInfo = (UserInfo) StpUtil.getSessionByLoginId(loginId).get((String) loginId);// 如果用戶角色是管理員if (userInfo.getUserRole() == UserRole.ADMIN) {// 返回管理員角色return List.of(UserRole.ADMIN.name());}// 其他情況,返回普通用戶角色return List.of(UserRole.CUSTOMER.name());}
}
最后進行要進行 satoken 過濾器全局配置
SaReactorFilter
package cn.hollis.nft.turbo.gateway.auth;import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import cn.hollis.nft.turbo.api.user.constant.UserPermission;
import cn.hollis.nft.turbo.api.user.constant.UserRole;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** sa-token的全局配置類,用于配置鑒權過濾器和異常處理邏輯** @author Hollis*/
@Configuration
@Slf4j
public class SaTokenConfigure {/*** 創建并配置 SaReactorFilter 實例,該過濾器用于對請求進行鑒權和異常處理** @return 配置好的 SaReactorFilter 實例*/@Beanpublic SaReactorFilter getSaReactorFilter() {return new SaReactorFilter()// 配置需要攔截的請求地址,/** 表示攔截所有請求.addInclude("/**")// 配置不需要攔截的請求地址,這里排除了網站圖標請求.addExclude("/favicon.ico")// 設置鑒權方法,每次請求進入時會執行該方法進行鑒權.setAuth(obj -> {// 登錄校驗:攔截所有路由,但排除指定的開放路由,對其他請求進行登錄狀態檢查SaRouter.match("/**").notMatch("/auth/**", "/collection/collectionList", "/collection/collectionInfo", "/wxPay/**").check(r -> StpUtil.checkLogin());// 權限認證:針對不同模塊的請求,校驗不同的權限// 管理界面請求需要用戶具備管理員角色SaRouter.match("/admin/**", r -> StpUtil.checkRole(UserRole.ADMIN.name()));// 下單界面請求需要用戶具備實名認證權限SaRouter.match("/trade/**", r -> StpUtil.checkPermission(UserPermission.AUTH.name()));// 用戶界面請求需要用戶具備基本權限或凍結權限SaRouter.match("/user/**", r -> StpUtil.checkPermissionOr(UserPermission.BASIC.name(), UserPermission.FROZEN.name()));// 商品界面請求需要用戶具備基本權限或凍結權限SaRouter.match("/order/**", r -> StpUtil.checkPermissionOr(UserPermission.BASIC.name(),UserPermission.FROZEN.name()));})// 設置異常處理方法,當鑒權方法拋出異常時會進入該方法進行處理.setError(this::getSaResult);}/*** 根據不同的異常類型,返回相應的錯誤信息** @param throwable 捕獲到的異常對象* @return 封裝了錯誤信息的 SaResult 對象*/private SaResult getSaResult(Throwable throwable) {switch (throwable) {// 處理用戶未登錄異常case NotLoginException notLoginException:// 記錄錯誤日志log.error("請先登錄");// 返回未登錄的錯誤信息return SaResult.error("請先登錄");// 處理用戶角色不匹配異常case NotRoleException notRoleException:// 判斷是否是管理員角色權限問題if (UserRole.ADMIN.name().equals(notRoleException.getRole())) {// 記錄越權使用的錯誤日志log.error("請勿越權使用!");// 返回越權使用的錯誤信息return SaResult.error("請勿越權使用!");}// 記錄無權限操作的錯誤日志log.error("您無權限進行此操作!");// 返回無權限操作的錯誤信息return SaResult.error("您無權限進行此操作!");// 處理用戶權限不足異常case NotPermissionException notPermissionException:// 判斷是否是實名認證權限問題if (UserPermission.AUTH.name().equals(notPermissionException.getPermission())) {// 記錄需要實名認證的錯誤日志log.error("請先完成實名認證!");// 返回需要實名認證的錯誤信息return SaResult.error("請先完成實名認證!");}// 記錄無權限操作的錯誤日志log.error("您無權限進行此操作!");// 返回無權限操作的錯誤信息return SaResult.error("您無權限進行此操作!");// 處理其他未知異常default:// 返回異常的錯誤信息return SaResult.error(throwable.getMessage());}}
}