高端网站建设 骆,青岛做网站公司电话,wordpress 常用工具,网站开发资源网一、设计思路
对于一些登录之后才能访问的接口#xff08;例如#xff1a;查询我的账号资料#xff09;#xff0c;我们通常的做法是增加一层接口校验#xff1a;
如果校验通过#xff0c;则#xff1a;正常返回数据。如果校验未通过#xff0c;则#xff1a;抛出异…一、设计思路
对于一些登录之后才能访问的接口例如查询我的账号资料我们通常的做法是增加一层接口校验
如果校验通过则正常返回数据。如果校验未通过则抛出异常告知其需要先进行登录。
那么判断会话是否登录的依据是什么我们先来简单分析一下登录访问流程
用户提交 name password 参数调用登录接口。登录成功返回这个用户的 Token 会话凭证。用户后续的每次请求都携带上这个 Token。服务器根据 Token 判断此会话是否登录成功。
所谓登录认证指的就是服务器校验账号密码为用户颁发 Token 会话凭证的过程这个 Token 也是我们后续判断会话是否登录的关键所在。
动态图演示 接下来我们将介绍在 SpringBoot 中如何使用 Sa-Token 完成登录认证操作。 Sa-Token 是一个 java 权限认证框架主要解决登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相关问题。 Gitee 开源地址https://gitee.com/dromara/sa-token 首先在项目中引入 Sa-Token 依赖
!-- Sa-Token 权限认证 --
dependencygroupIdcn.dev33/groupIdartifactIdsa-token-spring-boot-starter/artifactIdversion1.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未登录时为nullloginType: 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;/*** Sa-Token 登录认证示例 * * author kong* since 2022-10-13*/
RestController
RequestMapping(/acc/)
public class LoginAuthController {// 会话登录接口 ---- http://localhost:8081/acc/doLogin?namezhangpwd123456RequestMapping(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/isLoginRequestMapping(isLogin)public SaResult isLogin() {// StpUtil.isLogin() 查询当前客户端是否登录返回 true 或 false boolean isLogin StpUtil.isLogin();return SaResult.ok(当前客户端是否登录 isLogin);}// 校验当前登录状态 ---- http://localhost:8081/acc/checkLoginRequestMapping(checkLogin)public SaResult checkLogin() {// 检验当前会话是否已经登录, 如果未登录则抛出异常NotLoginExceptionStpUtil.checkLogin();// 抛出异常后代码将走入全局异常处理GlobalException.java如果没有抛出异常则代表通过了登录校验返回下面信息 return SaResult.ok(校验登录成功这行字符串是只有登录后才会返回的信息);}// 获取当前登录的账号是谁 ---- http://localhost:8081/acc/getLoginIdRequestMapping(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/tokenInfoRequestMapping(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?satokenxxxx-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/logoutRequestMapping(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(退出登录成功);}}代码注释已针对每一步操作做出详细解释大家可根据可参照注释中的访问链接进行逐步测试。
本示例代码已上传至 Gitee可参考 Sa-Token 登录认证示例 参考资料
Gitee 仓库地址https://gitee.com/dromara/sa-tokenGitHub 仓库地址https://github.com/dromara/sa-tokenSa-Token 在线文档https://sa-token.dev33.cn/