
Sa-Token 前后端分离鉴权指南无 Cookie 模式下的 Token 传递完整实践【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token本篇技术指南聚焦 Sa-Token 在app、小程序、前后端分离等无 Cookie 场景下的鉴权实现方案。核心思路一句话概括Token 不写 Cookie改由后端返回、前端存储、请求头手动携带。读完本文你将掌握前后端如何协作完成 Token 的下发—保存—提交—读取全链路并能直接照搬可运行的代码示例。何为无 Cookie 模式无 Cookie 模式特指不支持 Cookie 功能的终端通俗来讲就是我们常说的 ——前后端分离模式。常规 Web 端鉴权方法一般由Cookie模式完成而 Cookie 有两个特性可由后端控制写入—— 后端在响应时下发Set-Cookie浏览器自动落盘每次请求自动提交—— 后续请求浏览器自动在Cookie头中携带。这两个特性决定了在传统 PC Web 场景中前端代码无需任何特殊操作就能完成鉴权的全部流程因为整个流程都是后端控制完成的。而在 app、小程序等前后端分离场景中一般没有 Cookie 这一功能此时大多数人都会一脸懵逼咋进行鉴权啊见招拆招其实答案很简单不能后端控制写入了就前端自己写入。难点在后端如何将 Token 传递到前端每次请求不能自动提交了那就手动提交。难点在前端如何将 Token 传递到后端同时后端将其读取出来Sa-Token 正是围绕这两点把整个无 Cookie 模式的鉴权链路设计得开箱即用。1、后端将 Token 返回到前端登录成功后后端需要主动把 Token 信息交到前端手上让前端完成本地保存。步骤只有两步调用StpUtil.login(id)进行登录。调用StpUtil.getTokenInfo()返回当前会话的 token 详细参数。此方法返回一个SaTokenInfo对象其有两个关键属性tokenName和tokenValuetoken 的名称和 token 的值。将此对象传递到前台让前端人员将这两个值保存到本地。代码示例// 登录接口 RequestMapping(doLogin) public SaResult doLogin() { // 第1步先登录上 StpUtil.login(10001); // 第2步获取 Token 相关参数 SaTokenInfo tokenInfo StpUtil.getTokenInfo(); // 第3步返回给前端 return SaResult.data(tokenInfo); }源码级验证SaTokenInfo 到底返回了什么上述接口返回的tokenInfo是一个 JSON 对象。从 SaTokenInfo.java 的源码看它完整描述了一个 Token 的全部状态{ tokenName: satoken, // token 名称同时也是请求参数名 / Cookie 名 tokenValue: e67b99f1-3d7a-4a8d-bb2f-e888a0805633, // token 值 isLogin: true, // 此 token 是否已经登录 loginId: 10001, // 此 token 对应的 LoginId未登录时为 null loginType: login, // 账号类型标识多账号体系下区分端 tokenTimeout: 2591977, // token 剩余有效期单位: 秒 sessionTimeout: 2591977, // Account-Session 剩余有效时间单位: 秒 tokenSessionTimeout: -2, // Token-Session 剩余有效时间单位: 秒-2 表示系统中不存在这个缓存 tokenActiveTimeout: -1, // token 距离被冻结还剩多少时间单位: 秒-1 表示不冻结 loginDeviceType: DEF // 登录设备类型 }该对象由StpLogic.getTokenInfo()组装见 StpLogic.java其中tokenName取自全局配置默认值为satoken定义于 SaTokenConfig.java。这一点非常关键前端请求头里携带的参数名必须与后端tokenName配置完全一致Sa-Token 才会认账。仓库现成样例仓库中的 NotCookieController.java 给出了可直接运行的对照实现doLogin是前后端一体模式的常规登录登录后 Sa-Token 自动把 Token 写入 Cookie/响应doLogin2则是前后端分离模式的登录与常规登录唯一的不同点就是把StpUtil.getTokenInfo()作为响应体返回给前端// 前后端分离模式的登录样例 ---- http://localhost:8081/NotCookie/doLogin2?namezhangpwd123456 RequestMapping(doLogin2) public SaResult doLogin2(String name, String pwd) { if(zhang.equals(name) 123456.equals(pwd)) { // 会话登录 StpUtil.login(10001); // 与常规登录不同点之处这里需要把 Token 信息从响应体中返回到前端 SaTokenInfo tokenInfo StpUtil.getTokenInfo(); return SaResult.data(tokenInfo); } return SaResult.error(登录失败); }2、前端将 Token 提交到后端无论是 app 还是小程序其传递方式都大同小异。那就是将 token 塞到请求header里格式为{tokenName: tokenValue}。以经典跨端框架 uni-app 为例方式1简单粗暴—— 只存储 tokenValueheader 参数名写死// 1、首先在登录时将 tokenValue 存储在本地例如 uni.setStorageSync(tokenValue, tokenValue); // 2、在发起ajax请求的地方获取这个值并塞到header里 uni.request({ url: https://www.example.com/request, // 仅为示例并非真实接口地址。 header: { content-type: application/x-www-form-urlencoded, satoken: uni.getStorageSync(tokenValue) // ⚠️ 关键代码, 注意参数名字是 satoken }, success: (res) { console.log(res.data); } });注意方式 1 中 header 参数名satoken写死它正是后端默认的tokenName。若你在 SaTokenConfig.java 中修改过tokenName这里的参数名也要同步修改。方式2更加灵活—— 从后端返回的 tokenInfo 中动态取 tokenName 和 tokenValue// 1、首先在登录时将tokenName和tokenValue一起存储在本地例如 uni.setStorageSync(tokenName, tokenName); uni.setStorageSync(tokenValue, tokenValue); // 2、在发起ajax的地方获取这两个值, 并组织到head里 var tokenName uni.getStorageSync(tokenName); // 从本地缓存读取tokenName值 var tokenValue uni.getStorageSync(tokenValue); // 从本地缓存读取tokenValue值 var header { content-type: application/x-www-form-urlencoded }; if (tokenName ! undefined tokenName ! ) { header[tokenName] tokenValue; } // 3、后续在发起请求时将 header 对象塞到请求头部 uni.request({ url: https://www.example.com/request, // 仅为示例并非真实接口地址。 header: header, success: (res) { console.log(res.data); } });只要按照如此方法将token值传递到后端Sa-Token 就能像传统 PC 端一样自动读取到 token 值进行鉴权。你可能会有疑问难道我每个ajax都要写这么一坨岂不是麻烦死了你当然不能每个 ajax 都写这么一坨因为这种重复性代码都是要封装在一个函数里统一调用的。实际项目中通常将request二次封装为统一请求方法登录时保存 Token发请求时统一注入 header同时在后端返回未登录错误码如 401时自动跳转登录页。源码级验证后端如何自动读取到 header 里的 Token前端只要按上述方式携带了 headerSa-Token 就能自动读取。从StpLogic.getTokenValueNotCut()的实现看StpLogic.java它的读取顺序是先从 Storage 存储器里读取—— 优先命中当前请求内刚登录生成的 Token一次请求内先登录、后取 Token不会落空再尝试从请求体里读取—— 对应isReadBodytrue时读取 URL 参数或表单参数再尝试从 header 头里读取—— 对应isReadHeadertrue时读取{tokenName: tokenValue}请求头这正是前后端分离模式的核心路径最后尝试从 cookie 里读取—— 对应isReadCookietrue兼容传统 PC 场景。这四项读取开关isReadBody/isReadHeader/isReadCookie默认均为true见 SaTokenConfig.java也就是说无需任何额外配置只要前端把 Token 放进 headerSa-Token 就会自动识别并完成鉴权。进阶token-prefix 前缀模式下的 header 写法如果你的项目配置了token-prefix例如Bearer那么前端 header 中需要携带带前缀的完整值形如satoken: Bearer xxxxxxSa-Token 读取时会自动校验前缀并裁剪StpLogic.javaToken 值不是以tokenPrefix 开头时会被置空从而判定为未登录前缀合法时才裁剪掉前缀去匹配真实 Token。具体前缀配置见 token-prefix 专题文档。进阶isWriteHeader 自动写入响应头除了响应体返回 tokenInfo这种方式Sa-Token 还提供了isWriteHeadertrue配置默认false见 SaTokenConfig.java登录后自动把 Token 写入响应头并自动追加Access-Control-Expose-Headers以允许前端 JS 读取该响应头实现见 StpLogic.setTokenValueToResponseHeader()。前端只需在响应拦截器中读取satoken响应头并保存即可两种方案可依据团队习惯任选。其它解决方案如果你对 Cookie 非常了解那你就会明白所谓 Cookie本质上就是一个特殊的header参数而已。Cookie 通过Set-Cookie响应头写入、通过Cookie请求头提交与普通 header 的差异仅在于浏览器是否自动托管。而既然它只是一个 header 参数我们就能手动模拟实现它从而完成鉴权操作前端在收到Set-Cookie时自行解析并持久化保存后续请求时把保存的 Cookie 内容手动拼进Cookie请求头提交。这其实是对无Cookie模式的另一种解决方案有兴趣的同学可以自行深入了解一下在此暂不赘述。总结无 Cookie 模式的鉴权链路前后端分离无 Cookie场景下的完整鉴权流程可以归纳为四条链路环节负责方具体动作Sa-Token 对应 API / 配置登录后端会话登录StpUtil.login(id)Token 下发后端返回 tokenInfo 给前端或写入响应头StpUtil.getTokenInfo()/isWriteHeaderToken 保存前端将 tokenName、tokenValue 存本地uni.setStorageSync等本地存储Token 提交前端每次请求放入 header{tokenName: tokenValue}请求拦截器统一封装Token 读取后端按 Storage → 请求体 → header → cookie 顺序自动读取isReadBody/isReadHeader/isReadCookie鉴权判定后端校验 Token 并识别登录身份StpUtil.checkLogin()等校验 API整个过程后端只负责登录 下发 读取前端只负责保存 提交Sa-Token 在其中充当了无感知的 Token 搬运工——无论请求来自 PC 浏览器、app 还是小程序只要 Token 以{tokenName: tokenValue}的形式被携带Sa-Token 都能像传统 PC 端一样自动完成鉴权。这就是无 Cookie 模式下前后端分离鉴权的全部秘密。【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考