微服务网关SpringCloudGateway+SaToken鉴权

发布于:2025-06-06 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

概念

前置知识回顾

拿到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;

    /**
     * 手机号
     */
    @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());
        }
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到