目录
概念
做权限认证的时候 我们首先要明确两点
我们需要的角色有几种 我们需要的权限有几种
角色 分两种
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;
/**
* 手机号
*/
@SensitiveStrategyPhone
private 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 用户的权限列表,以字符串集合形式返回
*/
@Override
public 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 用户的角色列表,以字符串集合形式返回
*/
@Override
public 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 实例
*/
@Bean
public 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());
}
}
}