东东
发布于 2024-12-18 / 0 阅读 / 0 评论 / 0 点赞

Sa-Token

基础

Java 权限认证框架

官网链接:Sa-Token

功能:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权

其他权限认证框架 Shiro、SpringSecurity

操作更加简化,封装程度更高,引入后使用非常便捷

常用功能

登录

先看代码

// 会话登录:参数填写要登录的账号id,建议的数据类型:long | int | String, 不可以传入复杂类型,如:User、Admin 等等
StpUtil.login(Object id);     

通常我们在用户登录后,还要完成其他的事项:

检查是否已经登录?生成token?创建session会话?返回token?

这些sa都封装在登录方法中,这里的登录,简单理解就是 sa创建了token,并且通过cookie上下文返回给了前端,后续的请求都可以携带这个

看示例:

// 会话登录接口 
@RequestMapping("doLogin")
public SaResult doLogin(String name, String pwd) {
    // 第一步:比对前端提交的账号名称、密码
    if("zhang".equals(name) && "123456".equals(pwd)) {
        // 第二步:根据账号id,进行登录 
        StpUtil.login(10001);
        return SaResult.ok("登录成功");
    }
    return SaResult.error("登录失败");
}

使用

引入坐标

<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot-starter</artifactId>
    <version>1.39.0</version>
</dependency>

引入配置

server:
    # 端口
    port: 8081
    
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token: 
    # token 名称(同时也是 cookie 名称)
    token-name: satoken
    # token 有效期(单位:秒) 默认30天,-1 代表永久有效
    timeout: 2592000
    # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
    active-timeout: -1
    # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
    is-concurrent: true
    # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
    is-share: true
    # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
    token-style: uuid
    # 是否输出操作日志 
    is-log: true

如何使用权限校验(这里需要重写方法让sa知道当前用户有哪些权限)

这里要注意,SA中的角色和权限可以分开做校验

需要实现StpService

/**
 * 自定义权限加载接口实现类
 */
@Component    // 保证此类被 SpringBoot 扫描,完成 Sa-Token 的自定义权限验证扩展 
public class StpInterfaceImpl implements StpInterface {

    /**
     * 返回一个账号所拥有的权限码集合 
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询权限
        List<String> list = new ArrayList<String>();    
        list.add("101");
        list.add("user.add");
        list.add("user.update");
        list.add("user.get");
        // list.add("user.delete");
        list.add("art.*");
        return list;
    }

    /**
     * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        // 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询角色
        List<String> list = new ArrayList<String>();    
        list.add("admin");
        list.add("super-admin");
        return list;
    }

}

如何在请求时进行权限校验

注解的方式

@SaCheckRole(UserConstant.ADMIN_ROLE)

也可通过api的方法进行校验

// 获取:当前账号所拥有的角色集合
StpUtil.getRoleList();

// 判断:当前账号是否拥有指定角色, 返回 true 或 false
StpUtil.hasRole("super-admin");        

// 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
StpUtil.checkRole("super-admin");        

// 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
StpUtil.checkRoleAnd("super-admin", "shop-admin");        

// 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] 
StpUtil.checkRoleOr("super-admin", "shop-admin");        

处理sa的异常

在全局异常处理中增加sa的异常处理

@ExceptionHandler(NotRoleException.class)
public BaseResponse<?> notRoleExceptionHandler(RuntimeException e) {
    log.error("NotRoleException", e);
    return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "无权限");
}

@ExceptionHandler(NotLoginException.class)
public BaseResponse<?> notLoginExceptionHandler(RuntimeException e) {
    log.error("NotLoginException", e);
    return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "未登录");
}

集成redis使用

引入依赖

<!-- Sa-Token 整合 Redis (使用 jdk 默认序列化方式) -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-redis</artifactId>
    <version>1.39.0</version>
</dependency>

<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-redis-jackson</artifactId>
    <version>1.39.0</version>
</dependency>

<!-- 提供Redis连接池 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

添加redis配置

spring: 
  # redis配置 
  redis:
    # Redis数据库索引(默认为0)
    database: 1
    # Redis服务器地址
    host: 127.0.0.1
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    # password: 
    # 连接超时时间
    timeout: 10s
    lettuce:
      pool:
        # 连接池最大连接数
        max-active: 200
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池中的最小空闲连接
        min-idle: 0

项目示意

登录成功后,控制台会打印日志

SA [INFO] -->: SaSession [mianshiya:login:session:1867208590974427138] 创建成功
SA [INFO] -->: 账号 1867208590974427138 登录成功 (loginType=login), 会话凭证 token=d6aab73e-85a3-404f-ac97-3534f2a39ae2

如果开启同账号同设备登录检测,重复登录机会提示

SA [INFO] -->: 账号 1867208590974427138 被顶下线 (loginType=login), 会话凭证 token=d6aab73e-85a3-404f-ac97-3534f2a39ae2
SA [INFO] -->: 账号 1867208590974427138 登录成功 (loginType=login), 会话凭证 token=d334cc99-2d1e-4aa8-8d42-1befc205a5f2

用户注销

SA [INFO] -->: 账号 1867208590974427138 注销登录 (loginType=login), 会话凭证 token=d334cc99-2d1e-4aa8-8d42-1befc205a5f2
SA [INFO] -->: SaSession [mianshiya:login:session:1867208590974427138] 注销成功

用户查询无权限(默认是抛出了异常)

cn.dev33.satoken.exception.NotLoginException: 未能读取到有效 token