文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

SpringBoot怎么使用Sa-Token实现登录认证

2023-07-05 23:17

关注

这篇文章主要讲解了“SpringBoot怎么使用Sa-Token实现登录认证”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“SpringBoot怎么使用Sa-Token实现登录认证”吧!

一、设计思路

对于一些登录之后才能访问的接口(例如:查询我的账号资料),我们通常的做法是增加一层接口校验:

那么,判断会话是否登录的依据是什么?我们先来简单分析一下登录访问流程:

所谓登录认证,指的就是服务器校验账号密码,为用户颁发 Token 会话凭证的过程,这个 Token 也是我们后续判断会话是否登录的关键所在。

动态图演示:

SpringBoot怎么使用Sa-Token实现登录认证

接下来,我们将介绍在 SpringBoot 中如何使用 Sa-Token 完成登录认证操作。

Sa-Token 是一个 java 权限认证框架,主要解决登录认证、权限认证、单点登录、OAuth3、微服务网关鉴权 等一系列权限相关问题。
Gitee 开源地址:https://gitee.com/dromara/sa-token

首先在项目中引入 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 在背后做了大量的工作,包括但不限于:

你暂时不需要完整的了解整个登录过程,你只需要记住关键一点: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 功能的加持下,我们可以仅靠 StpUtil.login(id) 一句代码就完成登录认证。

除了登录方法,我们还需要:

// 当前会话注销登录StpUtil.logout();// 获取当前会话是否已经登录,返回true=已登录,false=未登录StpUtil.isLogin();// 检验当前会话是否已经登录, 如果未登录,则抛出异常:`NotLoginException`StpUtil.checkLogin();

异常 NotLoginException 代表当前会话暂未登录,可能的原因有很多:
前端没有提交 Token、前端提交的 Token 是无效的、前端提交的 Token 已经过期 &hellip;&hellip; 等等。

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,如果未登录,则返回 nullStpUtil.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,复制以下代码

package com.pj.cases.use;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import cn.dev33.satoken.stp.SaTokenInfo;import cn.dev33.satoken.stp.StpUtil;import cn.dev33.satoken.util.SaResult;@RestController@RequestMapping("/acc/")public class LoginAuthController {    // 会话登录接口  ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456    @RequestMapping("doLogin")    public SaResult doLogin(String name, String pwd) {                // 第一步:比对前端提交的 账号名称 & 密码 是否正确,比对成功后开始登录         //         此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对         if("zhang".equals(name) && "123456".equals(pwd)) {                        // 第二步:根据账号id,进行登录             //         此处填入的参数应该保持用户表唯一,比如用户id,不可以直接填入整个 User 对象             StpUtil.login(10001);                        // SaResult 是 Sa-Token 中对返回结果的简单封装,下面的示例将不再赘述             return SaResult.ok("登录成功");        }                return SaResult.error("登录失败");    }    // 查询当前登录状态  ---- http://localhost:8081/acc/isLogin    @RequestMapping("isLogin")    public SaResult isLogin() {        // StpUtil.isLogin() 查询当前客户端是否登录,返回 true 或 false         boolean isLogin = StpUtil.isLogin();        return SaResult.ok("当前客户端是否登录:" + isLogin);    }    // 校验当前登录状态  ---- http://localhost:8081/acc/checkLogin    @RequestMapping("checkLogin")    public SaResult checkLogin() {        // 检验当前会话是否已经登录, 如果未登录,则抛出异常:`NotLoginException`        StpUtil.checkLogin();        // 抛出异常后,代码将走入全局异常处理(GlobalException.java),如果没有抛出异常,则代表通过了登录校验,返回下面信息         return SaResult.ok("校验登录成功,这行字符串是只有登录后才会返回的信息");    }    // 获取当前登录的账号是谁  ---- http://localhost:8081/acc/getLoginId    @RequestMapping("getLoginId")    public SaResult getLoginId() {        // 需要注意的是,StpUtil.getLoginId() 自带登录校验效果        // 也就是说如果在未登录的情况下调用这句代码,框架就会抛出 `NotLoginException` 异常,效果和 StpUtil.checkLogin() 是一样的         Object userId = StpUtil.getLoginId();        System.out.println("当前登录的账号id是:" + userId);                // 如果不希望 StpUtil.getLoginId() 触发登录校验效果,可以填入一个默认值        // 如果会话未登录,则返回这个默认值,如果会话已登录,将正常返回登录的账号id         Object userId2 = StpUtil.getLoginId(0);        System.out.println("当前登录的账号id是:" + userId2);                // 或者使其在未登录的时候返回 null         Object userId3 = StpUtil.getLoginIdDefaultNull();        System.out.println("当前登录的账号id是:" + userId3);                // 类型转换:        // StpUtil.getLoginId() 返回的是 Object 类型,你可以使用以下方法指定其返回的类型         int userId4 = StpUtil.getLoginIdAsInt();  // 将返回值转换为 int 类型         long userId5 = StpUtil.getLoginIdAsLong();  // 将返回值转换为 long 类型         String userId6 = StpUtil.getLoginIdAsString();  // 将返回值转换为 String 类型                 // 疑问:数据基本类型不是有八个吗,为什么只封装以上三种类型的转换?        // 因为大多数项目都是拿 int、long 或 String 声明 UserId 的类型的,实在没见过哪个项目用 double、float、boolean 之类来声明 UserId         System.out.println("当前登录的账号id是:" + userId4 + " --- " + userId5 + " --- " + userId6);                // 返回给前端         return SaResult.ok("当前客户端登录的账号id是:" + userId);    }    // 查询 Token 信息  ---- http://localhost:8081/acc/tokenInfo    @RequestMapping("tokenInfo")    public SaResult tokenInfo() {        // TokenName 是 Token 名称的意思,此值也决定了前端提交 Token 时应该使用的参数名称         String tokenName = StpUtil.getTokenName();        System.out.println("前端提交 Token 时应该使用的参数名称:" + tokenName);                // 使用 StpUtil.getTokenValue() 获取前端提交的 Token 值         // 框架默认前端可以从以下三个途径中提交 Token:        //         Cookie         (浏览器自动提交)        //         Header头    (代码手动提交)        //         Query 参数    (代码手动提交) 例如: /user/getInfo?satoken=xxxx-xxxx-xxxx-xxxx         // 读取顺序为: Query 参数 --> Header头 -- > Cookie         // 以上三个地方都读取不到 Token 信息的话,则视为前端没有提交 Token         String tokenValue = StpUtil.getTokenValue();        System.out.println("前端提交的Token值为:" + tokenValue);                // TokenInfo 包含了此 Token 的大多数信息         SaTokenInfo info = StpUtil.getTokenInfo();        System.out.println("Token 名称:" + info.getTokenName());        System.out.println("Token 值:" + info.getTokenValue());        System.out.println("当前是否登录:" + info.getIsLogin());        System.out.println("当前登录的账号id:" + info.getLoginId());        System.out.println("当前登录账号的类型:" + info.getLoginType());        System.out.println("当前登录客户端的设备类型:" + info.getLoginDevice());        System.out.println("当前 Token 的剩余有效期:" + info.getTokenTimeout()); // 单位:秒,-1代表永久有效,-2代表值不存在        System.out.println("当前 Token 的剩余临时有效期:" + info.getTokenActivityTimeout()); // 单位:秒,-1代表永久有效,-2代表值不存在        System.out.println("当前 User-Session 的剩余有效期" + info.getSessionTimeout()); // 单位:秒,-1代表永久有效,-2代表值不存在        System.out.println("当前 Token-Session 的剩余有效期" + info.getTokenSessionTimeout()); // 单位:秒,-1代表永久有效,-2代表值不存在                // 返回给前端         return SaResult.data(StpUtil.getTokenInfo());    }        // 会话注销  ---- http://localhost:8081/acc/logout    @RequestMapping("logout")    public SaResult logout() {        // 退出登录会清除三个地方的数据:        //         1、Redis中保存的 Token 信息        //         2、当前请求上下文中保存的 Token 信息         //         3、Cookie 中保存的 Token 信息(如果未使用Cookie模式则不会清除)        StpUtil.logout();                // StpUtil.logout() 在未登录时也是可以调用成功的,        // 也就是说,无论客户端有没有登录,执行完 StpUtil.logout() 后,都会处于未登录状态         System.out.println("当前是否处于登录状态:" + StpUtil.isLogin());                // 返回给前端         return SaResult.ok("退出登录成功");    }    }

感谢各位的阅读,以上就是“SpringBoot怎么使用Sa-Token实现登录认证”的内容了,经过本文的学习后,相信大家对SpringBoot怎么使用Sa-Token实现登录认证这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯