sa-token框架用法速查

sa-token框架用法速查

周三 12月 17 2025 sa-token
5546 字 · 33 分钟

零、依赖引入

👉SaToken官网

XML
<!-- Sa-Token 权限认证 -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot3-starter</artifactId>
    <version>1.44.0</version>
</dependency>
<!-- Sa-Token 插件:整合SSO -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-sso</artifactId>
    <version>1.44.0</version>
</dependency>

<!-- Sa-Token 整合 RedisTemplate  -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-redis-template</artifactId>
    <version>1.44.0</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

<!-- Sa-Token插件:权限缓存与业务缓存分离 -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-alone-redis</artifactId>
    <version>1.44.0</version>
</dependency>

一、登录认证

1.1 原理

原文链接:https://blog.csdn.net/weixin_39570751/article/details/121291274

所谓登录认证,说白了就是限制某些API接口必须登录后才能访问(例:查询我的账号资料) 那么如何判断一个会话是否登录?

框架会在登录成功后给你做标记,每次登录认证时校验这个标记,有标记者视为已登录,无标记者视为未登录!

Saolder为什么能获取response对象,

因为SaHolder调用了SaManager.getSaTokenContext(),得到了SaTokenContext才可以通过getResponse方法获取SaResponse,而SaTokenContext的真实身份其实是SaTokenContextForSpringSaTokenContextForSpring重写了getResponse,就是在这个方法去调用SpringResponse

JAVA
StpUtil.login(user.getId());

// 👇源码逻辑
// 1. 获取当前StpLogic实例(默认是"login"类型)
StpLogic stpLogic = SaManager.getStpLogic("login", false);
    
// 2、执行登录逻辑,创建 token

// 3、在当前会话写入当前 tokenValue 
setTokenValue(tokenValue, loginModel.getCookieTimeout());

// 4、注入 cookie
response.addCookie(getTokenName(), tokenValue, "/", config.getCookieDomain(), cookieTimeout);

❓默认情况下,登录类型是什么、如何自定义登录类型

  • 默认登录方式是login
  • 在springboot下,可以通过自定义Bean更换默认登录方式👇
JAVA
@Configuration
public class SaTokenLogicConfig {

    @Bean("stpLogicApplet")
    @Primary
    public StpLogic stpLogicApplet() {
        return new StpLogic("appletLoginType");
    }

    @Bean("stpLogicWeb")
    public StpLogic stpLogicWeb() {
        return new StpLogic("webLoginType");
    }
}

❗使用什么类型登录,检验登录、获取用户信息时,必须使用对应的StpLogic

JAVA
@Component
public class SaTokenUtil {

    @Autowired
    @Qualifier("stpLogicApplet")
    private StpLogic stpLogicApplet;

    @Autowired
    @Qualifier("stpLogicWeb")
    private StpLogic stpLogicWeb;

    /**
     * 获取当前applet端登录的LoginUser对象
     */
    public LoginUser getAppletLoginUser() {
        Object obj = stpLogicApplet.getSession().get("userInfo");
        if (obj instanceof LoginUser) {
            return (LoginUser) obj;
        }
        return null;
    }

    /**
     * 获取当前web端登录的LoginUser对象
     */
    public LoginUser getWebLoginUser() {
        Object obj = stpLogicWeb.getSession().get("userInfo");
        if (obj instanceof LoginUser) {
            return (LoginUser) obj;
        }
        return null;
    }

    /**
     * 获取当前登录的 wid
     *
     * @return wid
     */
    public Integer getAppletLoginWid() {
        return stpLogicApplet.getLoginIdAsInt();
    }

    /**
     * 获取当前登录的 wid
     *
     * @return wid
     */
    public Integer getWebLoginWid() {
        return stpLogicWeb.getLoginIdAsInt();
    }
}

1.2 配置

👉参数配置大全

YAML
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: false
    # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
    token-style: uuid
    # 是否输出操作日志 
    is-log: true
@Configuration
public class SaTokenConfigure {
    @Autowired
    public void configSaToken(SaTokenConfig config) {
        config.setTokenName("satoken")
              .setTimeout(60 * 60 * 24 * 30)
              .setActivityTimeout(1800)
              .setIsConcurrent(true)
              .setIsShare(true)
              .setTokenStyle("uuid")
              .setTokenSessionCheckLogin(true)
              .setAutoRenew(true)
              .setIsLog(true)
              .setJwtSecretKey("your-jwt-secret-key-here")
              .setIdTokenTimeout(60 * 60 * 24);
    }
}

1.3 踹人

JAVA
// 当前会话注销登录
StpUtil.logout();

// 获取当前会话是否已经登录,返回true=已登录,false=未登录
StpUtil.isLogin();

StpUtil.logout(10001);                    // 强制指定账号注销下线
StpUtil.logout(10001, "PC");              // 强制指定账号指定端注销下线
StpUtil.logoutByTokenValue("token");      // 强制指定 Token 注销下线
StpUtil.kickout(10001);                   // 将指定账号踢下线 
StpUtil.kickout(10001, "PC");             // 将指定账号指定端踢下线
StpUtil.kickoutByTokenValue("token");     // 将指定 Token 踢下线

1.4 登录状态查询

JAVA
// 检验当前会话是否已经登录, 如果未登录,则抛出异常:NotLoginException
StpUtil.checkLogin();

// 获取当前会话账号id, 如果未登录,则抛出异常:`NotLoginException`
StpUtil.getLoginId();

// 类似查询API还有:
StpUtil.getLoginIdAsString();    // 获取当前会话账号id, 并转化为`String`类型
StpUtil.getLoginIdAsInt();       // 获取当前会话账号id, 并转化为`int`类型
StpUtil.getLoginIdAsLong();      // 获取当前会话账号id, 并转化为`long`类型

// ---------- 指定未登录情形下返回的默认值 ----------

// 获取当前会话账号id, 如果未登录,则返回 null 
StpUtil.getLoginIdDefaultNull();

// 获取当前会话账号id, 如果未登录,则返回默认值 (`defaultValue`可以为任意类型)
StpUtil.getLoginId(T defaultValue);

// 获取当前会话的 token 值
StpUtil.getTokenValue();

// 获取当前`StpLogic`的 token 名称
StpUtil.getTokenName();

// 获取指定 token 对应的账号id,如果未登录,则返回 null
StpUtil.getLoginIdByToken(String tokenValue);

// 获取当前会话剩余有效期(单位:s,返回-1代表永久有效)
StpUtil.getTokenTimeout();

// 获取当前会话的 token 信息参数
StpUtil.getTokenInfo();

二、鉴权

2.1 权限分配

JAVA
/**
 * 自定义权限加载接口实现类
 */
@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;
    }

}

2.2 角色校验

JAVA
// 获取:当前账号所拥有的权限集合
StpUtil.getPermissionList();

// 判断:当前账号是否含有指定权限, 返回 true 或 false
StpUtil.hasPermission("user.add");

// 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException 
// NotPermissionException 对象可通过 getLoginType() 方法获取具体是哪个 StpLogic 抛出的异常
StpUtil.checkPermission("user.add");

// 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
StpUtil.checkPermissionAnd("user.add", "user.delete", "user.get");

// 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
StpUtil.checkPermissionOr("user.add", "user.delete", "user.get");    

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

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

// 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
// NotRoleException 对象可通过 getLoginType() 方法获取具体是哪个 StpLogic 抛出的异常
StpUtil.checkRole("super-admin");

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

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

2.3 通配符*

PLAINTEXT
当一个账号拥有 "*" 权限时,他可以验证通过任何权限码 (角色认证同理)

2.4 权限精准

前后端分离项目,则:

  1. 在登录时,把当前账号拥有的所有权限码一次性返回给前端
  2. 前端将权限码集合保存在localStorage或其它全局状态管理对象中。
  3. 在需要权限控制的按钮上,使用 js 进行逻辑判断,例如在Vue框架中我们可以使用如下写法:
HTML
// `arr`是当前用户拥有的权限码数组
// `user.delete`是显示按钮需要拥有的权限码
// `删除按钮`是用户拥有权限码才可以看到的内容。
<button v-if="arr.indexOf('user.delete') > -1">删除按钮</button>

三、注解鉴权

  • @SaCheckLogin: 登录校验 —— 只有登录之后才能进入该方法。
  • @SaCheckRole("admin"): 角色校验 —— 必须具有指定角色标识才能进入该方法。
  • @SaCheckPermission("user:add"): 权限校验 —— 必须具有指定权限才能进入该方法。
  • @SaCheckSafe: 二级认证校验 —— 必须二级认证之后才能进入该方法。
  • @SaCheckHttpBasic: HttpBasic校验 —— 只有通过 HttpBasic 认证后才能进入该方法。
  • @SaCheckHttpDigest: HttpDigest校验 —— 只有通过 HttpDigest 认证后才能进入该方法。
  • @SaCheckDisable("comment"):账号服务封禁校验 —— 校验当前账号指定服务是否被封禁。
    • @SaCheckSign:API 签名校验 —— 用于跨系统的 API 签名参数校验。
  • @SaIgnore:忽略校验 —— 表示被修饰的方法或类无需进行注解鉴权和路由拦截器鉴权。

Sa-Token 使用全局拦截器完成注解鉴权功能,为了不为项目带来不必要的性能负担,拦截器默认处于关闭状态

因此,为了使用注解鉴权,你必须手动将 Sa-Token 的全局拦截器注册到你项目中

JAVA
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
    // 注册 Sa-Token 拦截器,打开注解式鉴权功能 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册 Sa-Token 拦截器,打开注解式鉴权功能 
        registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
    }
}

3.1 设定校验模式

@SaCheckRole@SaCheckPermission注解可设置校验模式,mode有两种取值:

  • SaMode.AND,标注一组权限,会话必须全部具有才可通过校验。
  • SaMode.OR,标注一组权限,会话只要具有其一即可通过校验。
JAVA
// 注解式鉴权:只要具有其中一个权限即可通过校验 
@RequestMapping("atJurOr")
@SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR)
public SaResult atJurOr() {
    return SaResult.data("用户信息");
}

3.2 角色权限or校验

假设有以下业务场景:一个接口在具有权限 user.add 或角色 admin 时可以调通。怎么写?

JAVA
// 注解式鉴权:只要具有其中一个权限即可通过校验 
@RequestMapping("atJurOr")
@SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR)
public SaResult atJurOr() {
    return SaResult.data("用户信息");
}

orRole 字段代表权限校验未通过时的次要选择,两者只要其一校验成功即可进入请求方法,其有三种写法:

  • 写法一:orRole = "admin",代表需要拥有角色 admin 。
  • 写法二:orRole = {"admin", "manager", "staff"},代表具有三个角色其一即可。
  • 写法三:orRole = {"admin, manager, staff"},代表必须同时具有三个角色。

3.3 @SaIgnore

JAVA
@SaCheckLogin
@RestController
public class TestController {

    // ... 其它方法 

    // 此接口加上了 @SaIgnore 可以游客访问 
    @SaIgnore
    @RequestMapping("getList")
    public SaResult getList() {
        // ... 
        return SaResult.ok();
    }
}

如上代码表示:TestController 中的所有方法都需要登录后才可以访问,但是 getList 接口可以匿名游客访问。

  • @SaIgnore 修饰方法时代表这个方法可以被游客访问,修饰类时代表这个类中的所有接口都可以游客访问。
  • @SaIgnore 具有最高优先级,当 @SaIgnore 和其它鉴权注解一起出现时,其它鉴权注解都将被忽略。
  • @SaIgnore 同样可以忽略掉 Sa-Token 拦截器中的路由鉴权

3.4 批量注解or、and鉴权

JAVA
// 在 `@SaCheckOr` 中可以指定多个注解,只要当前会话满足其中一个注解即可通过验证,进入方法。
@SaCheckOr(
        login = @SaCheckLogin,
        role = @SaCheckRole("admin"),
        permission = @SaCheckPermission("user.add"),
        safe = @SaCheckSafe("update-password"),
        httpBasic = @SaCheckHttpBasic(account = "sa:123456"),
        disable = @SaCheckDisable("submit-orders")
)
@RequestMapping("test")
public SaResult test() {
    // ... 
    return SaResult.ok();
}

// 当前客户端只要有 [ login 账号登录] 或者 [user 账号登录] 其一,就可以通过验证进入方法。
// 注意:`type = "login"` 和 `type = "user"` 是多账号模式章节的扩展属性,此处你可以先略过这个知识点
@SaCheckOr(
        login = { @SaCheckLogin(type = "login"), @SaCheckLogin(type = "user") }
)
@RequestMapping("test")
public SaResult test() {
    // ... 
    return SaResult.ok();
}
// 其实没有checkAnd
// 当你在一个方法上写多个注解鉴权时,其默认就是要满足所有注解规则后
// 才可以进入方法,只要有一个不满足,就会抛出异常
@SaCheckLogin
@SaCheckRole("admin")
@SaCheckPermission("user.add")
@RequestMapping("test")
public SaResult test() {
    // ... 
    return SaResult.ok();
}

// 测试:只有通过登录校验,或者提供了正确的 ApiKey,才可以进入方法
// 使用 append 字段追加抓取扩展包里的注解
@RequestMapping("/test")
@SaCheckOr(login = @SaCheckLogin, append = { SaCheckApiKey.class })
@SaCheckApiKey
public SaResult test() {
    // ...
    return SaResult.ok();
}

四、路由拦截

4.1 注册拦截器

JAVA
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {

                    // handle -> StpUtil.checkLogin()代表只进行注册拦截器                        
    public void addInterceptors(InterceptorRegistry registry) {
        // 只进行登录校验功能
        registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
                .addPathPatterns("/**") // 拦截所有路径
                .excludePathPatterns(   // 排除不需要拦截的路径
                        "/acc/password",    // 你的登录接口
                        "/user/doLogin",    // 示例接口
                        "/error",           // 排除错误页面
                        "/favicon.ico"
                );
    }
}

// lambda表达式自定义规则                                                                        
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册 Sa-Token 拦截器,定义详细认证规则 
        registry.addInterceptor(new SaInterceptor(handler -> {
            // 指定一条 match 规则
            SaRouter
                .match("/**")    // 拦截的 path 列表,可以写多个 */
                .notMatch("/user/doLogin")        // 排除掉的 path 列表,可以写多个 
                .check(r -> StpUtil.checkLogin());        // 要执行的校验动作,可以写完整的 lambda 表达式
                
            // 根据路由划分模块,不同模块不同鉴权 
            SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
            SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
            SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
            SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
            SaRouter.match("/notice/**", r -> StpUtil.checkPermission("notice"));
            SaRouter.match("/comment/**", r -> StpUtil.checkPermission("comment"));
            
            // 甚至你可以随意的写一个打印语句
            SaRouter.match("/**", r -> System.out.println("----啦啦啦----"));
            // 连缀写法
            SaRouter.match("/**").check(r -> System.out.println("----啦啦啦----"));
        })).addPathPatterns("/**");
    }
}  

4.2 自定义异常拦截

JAVA
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理所有:未登录异常
     */
    @ExceptionHandler(NotLoginException.class)
    public Result handleNotLoginException(NotLoginException nle) {
        // 根据不同子异常类型,可以给出更精确的提示(可选)
        // nle.getType() 可以获取异常具体类型,如 NOT_TOKEN、INVALID_TOKEN、TOKEN_TIMEOUT 等
        return new Result(UNAUTHORIZED, null, "您尚未登录,请先登录!");
    }

    /**
     * 处理所有:权限异常
     */
    @ExceptionHandler(NotPermissionException.class)
    public Result handleNotPermissionException(NotPermissionException npe) {
        return new Result(FORBIDDEN, null, "抱歉,您没有权限进行此操作!");
    }

    // ... 可以继续添加 DisableLoginException, NotRoleException 等异常的处理 ...
}

4.3 关闭注解校验 && 匹配特征大全

注解 @SaIgnore 的忽略效果只针对 SaInterceptor拦截器 和 AOP注解鉴权 生效,对自定义拦截器与过滤器不生效。

SaInterceptor 只要注册到项目中,默认就会打开注解校验,如果要关闭此能力,需要指定 isAnnotation 为 false:

JAVA
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(
            new SaInterceptor(handle -> {
                SaRouter.match("/**").check(r -> StpUtil.checkLogin());
            }).isAnnotation(false)  // 指定关闭掉注解鉴权能力,这样框架就只会做路由拦截校验了
    ).addPathPatterns("/**");
}

// 使用 setBeforeAuth 注册认证前置函数:
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new SaInterceptor(handle -> {
                System.out.println(1);
            })
                    .setBeforeAuth(handle -> {
                        System.out.println(2);
                    })
    ).addPathPatterns("/**");
}
// 如上代码,先执行 2,再执行注解鉴权,再执行 1,
// 如果 beforeAuth 里包含 SaRouter.stop() 将跳过后续的注解鉴权和 auth 认证环节。

stop() 与 back() 函数的区别在于:

  • SaRouter.stop() 会停止匹配,进入Controller。
  • SaRouter.back() 会停止匹配,直接返回结果到前端。
  • free() 的作用是:打开一个独立的作用域,使内部的 stop() 不再一次性跳出整个 Auth 函数,而是仅仅跳出当前 free 作用域。
JAVA
// 进入 free 独立作用域 
SaRouter.match("/**").free(r -> {
        SaRouter.match("/a/**").check(/* --- */);
    SaRouter.match("/b/**").check(/* --- */).stop();
    SaRouter.match("/c/**").check(/* --- */);
});
// 执行 stop() 函数跳出 free 后继续执行下面的 match 匹配 
        SaRouter.match("/**").check(/* --- */);

五、redis集成

5.0 依赖

XML
<!-- Sa-Token 整合 RedisTemplate -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-redis-template</artifactId>
    <version>1.44.0</version>
</dependency>

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

5.1 token前缀

YAML
{"satoken": "Bearer xxxx-xxxx-xxxx-xxxx"}
// 此时后端如果不做任何特殊处理,框架将会把Bearer视为token的一部分,
// 无法正常读取token信息,导致鉴权失败。
sa-token: 
    # 指定 token 提交时的前缀
    token-prefix: Bearer
    # 注:Token前缀 与 Token值 之间必须有一个空格

由于Cookie中无法存储空格字符,所以配置 Token 前缀后,Cookie 模式将会失效,无法成功提交带有前缀的 token。

如果需要在这种场景下仍然使用 Cookie 模式验证 token,可以使用 cookieAutoFillPrefix 配置项打开 Cookie 模式自动填充前缀:

YAML
sa-token: 
    # 指定 Cookie 模式下自动填充 token 提交前缀
    cookie-auto-fill-prefix: true

5.3 同端互斥登录

JAVA
// 指定`账号id`和`设备类型`进行登录
StpUtil.login(10001, "PC");    
// 调用此方法登录后,同设备的会被顶下线(不同设备不受影响),
// 再次访问系统时会抛出 NotLoginException 异常,场景值=-4

// 指定`账号id`和`设备类型`进行强制注销 
StpUtil.logout(10001, "PC");    
// 如果第二个参数填写null或不填,代表将这个账号id所有在线端强制注销,
// 被踢出者再次访问系统时会抛出 NotLoginException 异常,场景值=-2


// 返回当前token的登录设备类型
StpUtil.getLoginDevice();    

// 获取指定loginId指定设备类型端的tokenValue 
StpUtil.getTokenValueByLoginId(10001, "APP");    

5.4 登录与注销参数

JAVA
// 指定`账号id`和`设备类型`进行登录
StpUtil.login(10001, "PC");

// 设置登录账号 id 为 10001,并指定是否为 “记住我” 模式
StpUtil.login(10001, false);

StpUtil.login(10001, new SaLoginParameter()
        .setDeviceType("PC")             // 此次登录的客户端设备类型, 一般用于完成 [同端互斥登录] 功能
        .setDeviceId("xxxxxxxxx")        // 此次登录的客户端设备ID, 登录成功后该设备将标记为可信任设备
        .setIsLastingCookie(true)        // 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在)
        .setTimeout(60 * 60 * 24 * 7)    // 指定此次登录 token 的有效期, 单位:秒,-1=永久有效
        .setActiveTimeout(60 * 60 * 24 * 7) // 指定此次登录 token 的最低活跃频率, 单位:秒,-1=不进行活跃检查
        .setIsConcurrent(true)           // 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
        .setIsShare(false)                // 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个token, 为 false 时每次登录新建一个 token)
        .setMaxLoginCount(12)            // 同一账号最大登录数量,-1代表不限 (只有在 isConcurrent=true, isShare=false 时此配置项才有意义)
        .setMaxTryTimes(12)              // 在每次创建 token 时的最高循环次数,用于保证 token 唯一性(-1=不循环尝试,直接使用)
        .setExtra("key", "value")        // 记录在 Token 上的扩展参数(只在 jwt 模式下生效)
        .setToken("xxxx-xxxx-xxxx-xxxx") // 预定此次登录的生成的Token 
        .setIsWriteHeader(false)         // 是否在登录后将 Token 写入到响应头
        .setTerminalExtra("key", "value")// 本次登录挂载到 SaTerminalInfo 的自定义扩展数据
        .setReplacedRange(SaReplacedRange.CURR_DEVICE_TYPE) // 顶人下线的范围: CURR_DEVICE_TYPE=当前指定的设备类型端, ALL_DEVICE_TYPE=所有设备类型端
        .setOverflowLogoutMode(SaLogoutMode.LOGOUT)         // 溢出 maxLoginCount 的客户端,将以何种方式注销下线: LOGOUT=注销下线, KICKOUT=踢人下线, REPLACED=顶人下线
        .setRightNowCreateTokenSession(true)                // 是否立即创建对应的 Token-Session (true=在登录时立即创建,false=在第一次调用 getTokenSession() 时创建)
        .setupCookieConfig(cookie->{     // 设置 Cookie 配置项 
        cookie.setDomain("sa-token.cc");  // 设置:作用域
            cookie.setPath("/shop");          // 设置:路径 (一般只有当你在一个域名下部署多个项目时才会用到此值。)
            cookie.setSecure(true);           // 设置:是否只在 https 协议下有效
            cookie.setHttpOnly(true);         // 设置:是否禁止 js 操作 Cookie 
            cookie.setSameSite("Lax");        // 设置:第三方限制级别(Strict=完全禁止,Lax=部分允许,None=不限制)
            cookie.addExtraAttr("aa", "bb");  // 设置:额外扩展属性
        }
                );

// 当前客户端注销 
StpUtil.logout(new SaLogoutParameter()
// 注销范围: TOKEN=只注销当前 token 的会话,ACCOUNT=注销当前 token 指向的 loginId 其所有客户端会话
// 此参数只在调用 StpUtil.logout() 时有效
        .setRange(SaLogoutRange.TOKEN)   
);

// 指定 token 注销
        StpUtil.logoutByTokenValue("xxxxxxxxxxxxxxxxxxxxxxx", new SaLogoutParameter()
// 如果 token 已被冻结,是否保留其操作权 (是否允许此 token 调用注销API)(默认 false)
// 此参数只在调用 StpUtil.[logout/kickout/replaced]ByTokenValue("token") 时有效
        .setIsKeepFreezeOps(false)
// 是否保留此 token 的 Token-Session 对象(默认 false)
        .setIsKeepTokenSession(true)  
);

// 指定 loginId 注销
        StpUtil.logout(10001, new SaLogoutParameter()
        .setDeviceType("PC")  // 设置注销的设备类型 (如果不指定,则默认注销所有客户端)
        .setIsKeepTokenSession(true)  // 是否保留对应 token 的 Token-Session 对象(默认 false)
        .setMode(SaLogoutMode.REPLACED)  // 设置注销模式:LOGOUT=注销登录、KICKOUT=踢人下线,REPLACED=顶人下线(默认LOGOUT)
);

5.5 二级认证

JAVA
// 在当前会话 开启二级认证,时间为120秒
StpUtil.openSafe(120);
// 在当前会话 开启二级认证,业务标识为client,时间为600秒
StpUtil.openSafe("client", 600); 

// 获取:当前会话是否处于二级认证时间内
StpUtil.isSafe();
// 获取:当前会话是否已完成指定业务的二级认证,下方参数也可以传递"client"
StpUtil.isSafe("client");

// 检查当前会话是否已通过二级认证,如未通过则抛出异常
StpUtil.checkSafe();

// 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
StpUtil.getSafeTime();

// 在当前会话 结束二级认证
StpUtil.closeSafe(); 

在一个方法上使用 @SaCheckSafe 注解,可以在代码进入此方法之前进行一次二级认证校验

JAVA
// 二级认证:必须二级认证之后才能进入该方法 
@SaCheckSafe
@RequestMapping("add")
public String add() {
    return "用户增加";
}

// 指定业务类型,进行二级认证校验
@SaCheckSafe("art")
@RequestMapping("add2")
public String add2() {
    return "文章增加";
}

5.6 封号斗罗

JAVA
// 先踢下线
StpUtil.kickout(10001);
// 再封禁账号
StpUtil.disable(10001, 86400); 

旧版本在 StpUtil.login() 时会自动校验账号是否被封禁,v1.31.0 之后将 校验封禁 和 登录 两个动作分离成两个方法,不再自动校验。

JAVA
// 校验指定账号是否已被封禁,如果被封禁则抛出异常 `DisableServiceException`
StpUtil.checkDisable(10001);
// 通过校验后,再进行登录:
StpUtil.login(10001); 


// 封禁指定账号 
StpUtil.disable(10001, 86400);
// 获取指定账号是否已被封禁 (true=已被封禁, false=未被封禁) 
StpUtil.isDisable(10001);
// 校验指定账号是否已被封禁,如果被封禁则抛出异常 `DisableServiceException`
StpUtil.checkDisable(10001);
// 获取指定账号剩余封禁时间,单位:秒,如果该账号未被封禁,则返回-2 
StpUtil.getDisableTime(10001);
// 解除封禁
StpUtil.untieDisable(10001); 

Thanks for reading!

sa-token框架用法速查

周三 12月 17 2025 sa-token
5546 字 · 33 分钟