目录
概念
前置知识回顾
拿到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());}}
}