Sa-Token完成路由鉴权

最近在开发登录接口时,使用了SaToken权限认证框架来实现。sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,以此来记录下使用过程中遇到的问题

目录

鉴权的两种模式

核心概念

[1. 前端存储方式(数据存在哪里)](#1. 前端存储方式(数据存在哪里))

[2. 身份认证方式(服务器怎么确认 "你是你")](#2. 身份认证方式(服务器怎么确认 “你是你”))

[模式 1:普通模式(Cookie 认证模式)](#模式 1:普通模式(Cookie 认证模式))

[模式 2:localStorage 模式(Token 认证模式)](#模式 2:localStorage 模式(Token 认证模式))

1.login()

1、创建token,绑定会话

作用

功能解析

[2、将token写入 HTTP 响应头](#2、将token写入 HTTP 响应头)

作用

功能解析

2、checkLogin()

代码执行流程

Token获取方式


鉴权的两种模式

核心概念

1. 前端存储方式(数据存在哪里)

  • Cookie :浏览器内置的存储机制,容量小,会自动随每次 HTTP 请求发送到服务器,可设置过期时间、域名、路径限制。
  • localStorage :HTML5 新增的前端本地存储,容量大,不会自动随请求发送到服务器,需手动通过代码读取并携带,数据永久有效(除非手动删除)。

2. 身份认证方式(服务器怎么确认 "你是你")

  • Cookie 认证:传统方案,服务器直接把用户身份信息(或加密后的会话 ID)写入 Cookie,后续请求浏览器自动带 Cookie,服务器验证即可。
  • Token 认证 :现代方案(如 JWT、OAuth2.0),用户登录后服务器生成一个临时 "令牌"(Token)返回给前端,前端存起来,后续请求需手动携带 Token(如请求头 Authorization),服务器验证 Token 有效性。

模式 1:普通模式(Cookie 认证模式)

本质是「用 Cookie 作为存储载体 + Cookie 作为认证载体」的传统方案,流程如下:

  1. 用户输入账号密码登录;
  2. 服务器验证通过后,生成 Session ID (会话 ID,代表当前登录状态),通过 Set-Cookie 响应头把 Session ID 写入浏览器 Cookie;
  3. 后续用户访问其他页面 / 接口时,浏览器自动把 Cookie 里的 Session ID 带到服务器;
  4. 服务器通过 Session ID 查找对应的用户会话,确认身份后返回数据。

关键特点

  • 无需前端手动处理存储和携带,浏览器自动完成;
  • 依赖 Cookie 的同源策略,安全性有基础保障(可设置 HttpOnly 防止 JS 读取,Secure 仅 HTTPS 传输,SameSite 防止 CSRF 攻击);
  • 缺点:Cookie 容量小,无法存大量数据;跨域请求时 Cookie 携带复杂(需配置 CORS 的 credentials);分布式系统中 Session 共享麻烦(需 Redis 等存储 Session)。

模式 2:localStorage 模式(Token 认证模式)

本质是「用 localStorage 作为存储载体 + Token 作为认证载体」的现代方案,流程如下:

  1. 用户登录后,服务器验证通过,生成 Token(如 JWT Token,包含用户 ID、过期时间等加密信息),直接返回给前端(通常在响应体里);
  2. 前端通过代码把 Token 存入 localStorage(也可存 sessionStorage,区别是 sessionStorage 关闭标签页就失效);
  3. 后续请求接口时,前端手动从 localStorage 读取 Token,通过请求头(如 Authorization: Bearer <Token>)携带到服务器;
  4. 服务器解析并验证 Token(无需查数据库 / Redis,JWT 可自包含信息),确认身份后返回数据。

关键特点

  • 前端需手动处理 Token 的存储和携带(写代码实现);
  • 不依赖 Cookie,跨域请求更简单(无需处理 Cookie 跨域问题);
  • 容量大(localStorage 5MB),可存额外用户信息;
  • 缺点:localStorage 易被 XSS 攻击(恶意脚本可读取),需配合前端防 XSS(如输入过滤、CSP 策略);Token 过期需手动处理(如刷新 Token)。

下面介绍SaToken中常见的常用的两个方法,对其内部的源码进行简单的阅读和理解,目的是为了理解SaToken内部生成和存储token的方式,加深使用其他方法时,知道其中一些属性的来源

1.login()

login一共完成了两个步骤:

1、创建token,绑定会话

这段代码是Sa-Token 框架中负责创建登录会话、生成登录 Token 的核心方法 ,属于 Sa-Token 登录认证流程的核心实现逻辑(通常位于 StpLogic 类中)。其主要作用是完成用户登录时的 Token 生成、会话绑定、状态存储等一系列操作,下面逐行拆解其功能:

作用

接收用户唯一标识(id)和登录配置(loginModel),完成登录会话的创建:生成可用 Token、绑定用户会话、存储 Token 与用户的映射关系,并触发登录事件,最终返回生成的登录 Token。

功能解析

  1. 参数校验 this.checkLoginArgs(id, loginModel);校验登录参数的合法性(如 id 不能为空、loginModel 不能为 null 等),确保登录请求参数有效。

  2. 加载配置并构建登录模型

    java 复制代码
    SaTokenConfig config = this.getConfigOrGlobal();
    loginModel.build(config);
    • 获取 Sa-Token 的配置(优先当前 StpLogic 的配置,无则用全局配置);
    • 根据配置补全 loginModel 的默认值(如超时时间、设备类型、Token 签名标签等),确保登录模型参数完整。
  3. 生成可用的 Token String tokenValue = this.distUsableToken(id, loginModel);生成一个全局唯一的、可用的 Token 值(支持 UUID、JWT、自定义生成规则等),确保 Token 不重复、符合配置规则。

  4. 创建 / 获取用户会话并更新超时时间

    java 复制代码
    SaSession session = this.getSessionByLoginId(id, true);
    session.updateMinTimeout(loginModel.getTimeout());
    • 根据用户 id 获取会话(若不存在则创建,true 表示自动创建);
    • 更新会话的最小超时时间(确保会话有效期不低于本次登录的超时配置)。
  5. 记录 Token 签名信息到会话

    java 复制代码
    TokenSign tokenSign = new TokenSign(tokenValue, loginModel.getDeviceOrDefault(), loginModel.getTokenSignTag());
    session.addTokenSign(tokenSign);

    将本次登录的 Token、设备类型、签名标签等信息封装为 TokenSign 对象,存入用户会话中(用于支持多端登录、设备管理、踢人下线等功能)。

  6. 保存 Token 与用户的映射关系 this.saveTokenToIdMapping(tokenValue, id, loginModel.getTimeout());将 Token 与用户 id 绑定并存储(默认存储在 Redis 或内存中),同时设置 Token 的过期时间,用于后续认证时通过 Token 快速查询用户身份。

  7. 处理活跃超时(可选)

    java 复制代码
    if (this.isOpenCheckActiveTimeout()) {
        this.setLastActiveToNow(tokenValue, loginModel.getActiveTimeout(), loginModel.getTimeoutOrGlobalConfig());
    }

    若开启了 "活跃超时" 功能(即用户长时间不操作自动过期),则记录 Token 的最后活跃时间为当前时间,用于后续超时判断。

  8. 触发登录事件SaTokenEventCenter.doLogin(this.loginType, id, tokenValue, loginModel);发布登录成功事件,允许开发者通过监听事件实现自定义逻辑(如记录登录日志、发送登录通知等)。

  9. 处理最大登录数限制

    java 复制代码
    if (config.getMaxLoginCount() != -1) {
        this.logoutByMaxLoginCount(id, session, (String)null, config.getMaxLoginCount());
    }

    若配置了 "同一账号最大登录数"(非 -1 表示开启限制),则检查当前用户的登录会话数,若超过限制则自动踢掉最早的登录会话(实现 "单端登录 / 多端登录限制")。

  10. 返回生成的 Token return tokenValue;将最终生成的登录 Token 返回,供前端存储并用于后续请求认证。

2、将token写入 HTTP 响应头

作用

这段代码是Sa-Token 框架中负责将生成的 Token 分发到不同存储位置的辅助方法 ,核心作用是把登录成功后的 Token 传递给前端(或存储到上下文),确保前端能获取并在后续请求中携带 Token 完成认证。通常在 createLoginSession 生成 Token 后调用,是登录流程中 "Token 交付" 环节的关键实现。

功能解析

  1. Token 非空校验 if (!SaFoxUtil.isEmpty(tokenValue)) {通过 Sa-Token 内置工具类 SaFoxUtil 判断 Token 值非空,避免空值处理。

  2. Token 存入上下文存储 this.setTokenValueToStorage(tokenValue);将 Token 存入当前请求的上下文存储(如 ThreadLocal),方便后续请求处理中快速获取 Token(例如权限校验时直接从上下文读取),属于框架内部的 Token 临时存储。

  3. Token 写入 Cookie(可选)

    java 复制代码
    if (this.getConfigOrGlobal().getIsReadCookie()) {
        this.setTokenValueToCookie(tokenValue, loginModel.getCookieTimeout());
    }
    • 检查全局配置是否开启 "从 Cookie 读取 Token"(getIsReadCookie),若开启则将 Token 写入浏览器 Cookie;
    • 同时设置 Cookie 的超时时间(从 loginModel 中获取),确保 Cookie 有效期与 Token 一致,前端后续请求会自动携带 Cookie 中的 Token。
  4. Token 写入响应头(可选)

    java 复制代码
    if (loginModel.getIsWriteHeaderOrGlobalConfig()) {
        this.setTokenValueToResponseHeader(tokenValue);
    }
    • 检查 loginModel 或全局配置是否开启 "Token 写入响应头";
    • 若开启则将 Token 写入 HTTP 响应头(默认头名通常为 satokenAuthorization),前端可从响应头中提取 Token(例如 Ajax 请求获取响应头后存储到 localStorage)。

2、checkLogin()

代码执行流程

内部调用StpLogic的getLoginId 类,这段代码是Sa-Token 框架中获取当前登录用户 ID 的核心方法 (通常位于 StpLogic 类),负责从请求中解析 Token、验证 Token 有效性,并返回对应的登录用户 ID;若 Token 无效 / 过期 / 被踢等,会抛出针对性的未登录异常。

Token获取方式

优先级如下:

1.上下文获取(如果调用了login(),Token会被注册到上下文)

2.query参数获取

3.header中获取(鉴权时常用,一般header中的属性为Authorization)

4.cookie中获取(调用login()后,会自动在响应头中加入set-cookie字段,请求时会自动携带cookie字段,因此可以读取,注意,重启服务后,cookie存储的token会失效)


有了以上两个函数,其实鉴权就很容易了,首先在登陆成功后,调用login()函数,生成token返回给前端;后续前端携带该token来访问后端接口时,后端通过过滤器或拦截器进行拦截,调用checkLogin()方法进行鉴权即可。下面是鉴权两种写法的区别

1、注册过滤器(内部已经实现了Filter类,所以直接使用SaServletFilter 即可)

java 复制代码
@Configuration
public class SaTokenConfigure {
    @Autowired
    private UserService userService;
    /**
     * 注册 [Sa-Token全局过滤器]
     */
    @Bean
    public SaServletFilter getSaServletFilter() {
        return new SaServletFilter()
                // 指定 拦截路由 与 放行路由
                .addExclude("/see/**")
                .addExclude("/shareConfig/queryList")
                .addExclude("/api/user/**")
                .addInclude("/**")
                // 认证函数:每次请求都会执行
                .setAuth(obj -> {
                    System.out.println("---------- 进入Sa-Token全局认证 -----------");
                    // 从请求头获取token
                    StpUtil.checkLogin();
                })

                // 异常处理函数:每次认证函数发生异常时执行此函数
                .setError(e -> {
                    System.out.println("---------- 认证失败 -----------");
                    System.out.println(e.getMessage());
                    // 设置响应状态码为401未授权
                    SaHolder.getResponse()
                            .setStatus(401);
                    return SaResult.error(e.getMessage());
                });
    }

}

2、注册全局拦截器

java 复制代码
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
	// 注册拦截器
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。
		registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
				.addPathPatterns("/**")
				.excludePathPatterns("/user/doLogin");
	}
}

这是个人的学习笔记,有不清楚的地方见谅,佬们勿喷~

相关推荐
ProgramHan17 小时前
React 19 新特性深度解析:告别 useEffect 的时代
前端·react.js·前端框架
摸鱼仙人~17 小时前
RAG 系统中的 TOC Enhance:用“目录增强”提升检索与生成效果
linux·运维·服务器
IT_陈寒17 小时前
Redis 7.0 实战:5个被低估但超实用的新特性,让你的QPS提升40%
前端·人工智能·后端
Autumn729917 小时前
【 jupyter 】PyCharm配置服务器连接jupyter
服务器·jupyter·pycharm
华如锦17 小时前
一.2部署——大模型服务快速部署vLLM GPU 安装教程 (Linux)
java·linux·运维·人工智能·后端·python·vllm
Jacob程序员17 小时前
Linux scp命令:高效远程文件传输指南
linux·运维·服务器
奋斗者1号17 小时前
SSL/TLS 证书在客户端-服务器通信中的详解
服务器·网络协议·ssl
小鸡脚来咯17 小时前
设计模式,单例和工厂模式
java
web守墓人17 小时前
【前端】ikun-pptx编辑器前瞻问题四:通过AI编写一个前端pptx编辑器
前端