一、设计思路
对于一些登录之后才能访问的接口(例如:查询我的账号资料),我们通常的做法是增加一层接口校验:
- 如果校验通过,则:正常返回数据。
- 如果校验未通过,则:抛出异常,告知其需要先进行登录。
那么,判断会话是否登录的依据是什么?我们先来简单分析一下登录访问流程:
- 用户提交 name + password 参数,调用登录接口。
- 登录成功,返回这个用户的 Token 会话凭证。
- 用户后续的每次请求,都携带上这个 Token。
- 服务器根据 Token 判断此会话是否登录成功。
所谓登录认证,指的就是服务器校验账号密码,为用户颁发 Token 会话凭证的过程,这个 Token 也是我们后续判断会话是否登录的关键所在。
动态图演示:

接下来,我们将介绍在 SpringBoot 中如何使用 Sa-Token 完成登录认证操作。
Sa-Token 是一个 java 权限认证框架,主要解决登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相关问题。
Gitee 开源地址:https://gitee.com/dromara/sa-token
首先在项目中引入 Sa-Token 依赖:- <dependency>
- <groupId>cn.dev33</groupId>
- <artifactId>sa-token-spring-boot-starter</artifactId>
- <version>1.34.0</version>
- </dependency>
复制代码 注:如果你使用的是 SpringBoot 3.x,只需要将 sa-token-spring-boot-starter 修改为 sa-token-spring-boot3-starter 即可。
二、登录与注销
根据以上思路,我们需要一个会话登录的函数:- // 会话登录:参数填写要登录的账号id,建议的数据类型:long | int | String, 不可以传入复杂类型,如:User、Admin 等等
- StpUtil.login(Object id);
复制代码 只此一句代码,便可以使会话登录成功,实际上,Sa-Token 在背后做了大量的工作,包括但不限于:
- 检查此账号是否之前已有登录
- 为账号生成 Token 凭证与 Session 会话
- 通知全局侦听器,xx 账号登录成功
- 将 Token 注入到请求上下文
- 等等其它工作……
你暂时不需要完整的了解整个登录过程,你只需要记住关键一点:Sa-Token 为这个账号创建了一个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("登录失败");
- }
复制代码 如果你对以上代码阅读没有压力,你可能会注意到略显奇怪的一点:此处仅仅做了会话登录,但并没有主动向前端返回 Token 信息。
是因为不需要吗?严格来讲是需要的,只不过 StpUtil.login(id) 方法利用了 Cookie 自动注入的特性,省略了你手写返回 Token 的代码。
如果你对 Cookie 功能还不太了解,也不用担心,我们会在之后的 [ 前后端分离 ] 章节中详细的阐述 Cookie 功能,现在你只需要了解最基本的两点:
- Cookie 可以从后端控制往浏览器中写入 Token 值。
- Cookie 会在前端每次发起请求时自动提交 Token 值。
因此,在 Cookie 功能的加持下,我们可以仅靠 StpUtil.login(id) 一句代码就完成登录认证。
除了登录方法,我们还需要:- // 当前会话注销登录
- StpUtil.logout();
- // 获取当前会话是否已经登录,返回true=已登录,false=未登录
- StpUtil.isLogin();
- // 检验当前会话是否已经登录, 如果未登录,则抛出异常:`NotLoginException`
- StpUtil.checkLogin();
复制代码 异常 NotLoginException 代表当前会话暂未登录,可能的原因有很多:
前端没有提交 Token、前端提交的 Token 是无效的、前端提交的 Token 已经过期 …… 等等。
Sa-Token 未登录场景值参照表:
场景值对应常量含义说明-1NotLoginException.NOT_TOKEN未能从请求中读取到 Token-2NotLoginException.INVALID_TOKEN已读取到 Token,但是 Token无效-3NotLoginException.TOKEN_TIMEOUT已读取到 Token,但是 Token已经过期-4NotLoginException.BE_REPLACED已读取到 Token,但是 Token 已被顶下线-5NotLoginException.KICK_OUT已读取到 Token,但是 Token 已被踢下线那么,如何获取场景值呢?废话少说直接上代码:- // 全局异常拦截(拦截项目中的NotLoginException异常)
- @ExceptionHandler(NotLoginException.class)
- public SaResult handlerNotLoginException(NotLoginException nle)
- throws Exception {
- // 打印堆栈,以供调试
- nle.printStackTrace();
-
- // 判断场景值,定制化异常信息
- String message = "";
- if(nle.getType().equals(NotLoginException.NOT_TOKEN)) {
- message = "未提供token";
- }
- else if(nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
- message = "token无效";
- }
- else if(nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
- message = "token已过期";
- }
- else if(nle.getType().equals(NotLoginException.BE_REPLACED)) {
- message = "token已被顶下线";
- }
- else if(nle.getType().equals(NotLoginException.KICK_OUT)) {
- message = "token已被踢下线";
- }
- else {
- message = "当前会话未登录";
- }
-
- // 返回给前端
- return SaResult.error(message);
- }
复制代码 注意:以上代码并非处理逻辑的最佳方式,只为以最简单的代码演示出场景值的获取与应用,大家可以根据自己的项目需求来定制化处理三、会话查询
- // 获取当前会话账号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 查询
- // 获取当前会话的token值
- StpUtil.getTokenValue();
- // 获取当前`StpLogic`的token名称
- StpUtil.getTokenName();
- // 获取指定token对应的账号id,如果未登录,则返回 null
- StpUtil.getLoginIdByToken(String tokenValue);
- // 获取当前会话剩余有效期(单位:s,返回-1代表永久有效)
- StpUtil.getTokenTimeout();
- // 获取当前会话的token信息参数
- StpUtil.getTokenInfo();
复制代码 TokenInfo 是 Token 信息 Model,用来描述一个 Token 的常用参数:- {
- "tokenName": "satoken", // token名称
- "tokenValue": "e67b99f1-3d7a-4a8d-bb2f-e888a0805633", // token值
- "isLogin": true, // 此token是否已经登录
- "loginId": "10001", // 此token对应的LoginId,未登录时为null
- "loginType": "login", // 账号类型标识
- "tokenTimeout": 2591977, // token剩余有效期 (单位: 秒)
- "sessionTimeout": 2591977, // User-Session剩余有效时间 (单位: 秒)
- "tokenSessionTimeout": -2, // Token-Session剩余有效时间 (单位: 秒) (-2表示系统中不存在这个缓存)
- "tokenActivityTimeout": -1, // token剩余无操作有效时间 (单位: 秒)
- "loginDevice": "default-device" // 登录设备类型
- }
复制代码 五、来个小测试,加深一下理解
新建 LoginAuthController,复制以下代码代码注释已针对每一步操作做出详细解释,大家可根据可参照注释中的访问链接进行逐步测试。
本示例代码已上传至 Gitee,可参考:
Sa-Token 登录认证示例
参考资料
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |