从零搭建开发脚手架 集成认证授权 Sa-Token(尝鲜)

  发布时间:2025-11-04 04:14:19   作者:玩站小弟   我要评论
本文转载自微信公众号「Java大厂面试官」,作者laker。转载本文请联系Java大厂面试官公众号。目前我仅以学习和尝鲜为目的来集成,不建议用于公司等正式环境,公司还是建议Shiro和Spring S 。

 

本文转载自微信公众号「Java大厂面试官」,从零成认尝鲜作者laker。搭建转载本文请联系Java大厂面试官公众号。脚手架集

目前我仅以学习和尝鲜为目的证授来集成,不建议用于公司等正式环境,从零成认尝鲜公司还是搭建建议Shiro和Spring Security那一套。(等我实战一波看看效果再说)

为什么要尝鲜Sa-Token

之前我还是脚手架集挺排斥国产小作坊的开源作品,毕竟不是证授根红苗正,但是从零成认尝鲜随着近几年国内开源社区的大力发展,以及在平时工作中又接触了解很多,搭建慢慢改变了我的脚手架集看法,其实国人开源作品还是证授很香的,其Api简单易用,从零成认尝鲜源码和官方文档都是搭建中文的,功能丰富且能满足很多中国式需求,脚手架集各种QQ、微信交流群活跃度非常高,总之就是极大程度满足中国式需求。

在权限认证框架领域,使用最多的莫过于Shiro和Spring Security,服务器租用但是一天在逛同性交友网站(github)的时候,赫然发现了Sa-Token其竟然有2K的star数量,看其中文介绍竟然是轻量级Java权限认证框架,看了下其特性和功能点,就唤起了我强烈的好奇心,于是乎就有了今天的尝鲜。

Sa-Token是什么?

sa-token是一个轻量级Java权限认证框架,主要解决:登录认证、权限认证、Session会话、单点登录、OAuth2.0 等一系列权限相关问题

框架针对踢人下线、自动续签、前后台分离、分布式会话……等常见业务进行N多适配,通过sa-token,你可以以一种极简的方式实现系统的权限认证部分

与其它权限认证框架相比,sa-token 具有以下优势:

简单 :可零配置启动框架,真正的开箱即用,低成本上手 强大 :目前已集成几十项权限相关特性,涵盖了大部分业务场景的IT技术网解决方案 易用 :如丝般顺滑的API调用,大量高级特性统统只需一行代码即可实现 高扩展 :几乎所有组件都提供了扩展接口,90%以上的逻辑都可以按需重写

Sa-Token 能做什么?

登录验证 —— 轻松登录鉴权,并提供五种细分场景值 权限验证 —— 适配RBAC权限模型,不同角色不同授权 Session会话 —— 专业的数据缓存中心 踢人下线 —— 将违规用户立刻清退下线 持久层扩展 —— 可集成Redis、Memcached等专业缓存中间件,重启数据不丢失 分布式会话 —— 提供jwt集成和共享数据中心两种分布式会话方案 单点登录 —— 一处登录,处处通行 模拟他人账号 —— 实时操作任意用户状态数据 临时身份切换 —— 将会话身份临时切换为其它账号 无Cookie模式 —— APP、小程序等前后台分离场景 同端互斥登录 —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录 多账号认证体系 —— 比如一个商城项目的user表和admin表分开鉴权 花式token生成 —— 内置六种token风格,还可自定义token生成策略 注解式鉴权 —— 优雅的将鉴权与业务代码分离 路由拦截式鉴权 —— 根据路由拦截鉴权,可适配restful模式 自动续签 —— 提供两种token过期策略,灵活搭配使用,还可自动续签 会话治理 —— 提供方便灵活的会话查询接口 记住我模式 —— 适配[记住我]模式,重启浏览器免验证 密码加密 —— 提供密码加密模块,可快速MD5、SHA1、SHA256、AES、RSA加密 组件自动注入 —— 零配置与Spring等框架集成

快速集成

依赖导入

<dependency>     <groupId>cn.dev33</groupId>     <artifactId>sa-token-spring-boot-starter</artifactId>     <version>1.15.2</version> </dependency> 

“最新版本去maven中央库自己查询下,当前是1.15.2。

配置文件

你可以零配置启动项目但同时你也可以在application.yml中增加如下配置,定制性使用框架:

spring:      # sa-token配置     sa-token:          # token名称 (同时也是cookie名称)         token-name: satoken         # token有效期,单位s 默认30天, -1代表永不过期          timeout: 2592000         # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒         activity-timeout: -1         # 是免费源码下载否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)          allow-concurrent-login: true         # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)          is-share: false         # token风格         token-style: uuid 

登录

@PostMapping("/api/v1/login")     @ApiOperationSupport(order = 1)     @ApiOperation(value = "登录")     public Response login(String userName, String pwd) {         log.info("login,username:{},pwd:{}", userName, pwd);         // 模拟 校验用户名密码          Long userId = check(userName,pwd);         StpUtil.setLoginId(userId);         return Response.ok(StpUtil.getTokenInfo());     } 

核心就一行StpUtil.setLoginId(userId),来看看它帮我们做了什么?

源码及其简单,还有很多中文注释,跟着读就行了,直接贴结论。

创建token 创建SaSession 在session上记录token签名 创建token、loginId映射 token写入cookie

底层会话等存储使用的是Map

源码如下:

/**  * 数据集合   */ public Map<String, Object> dataMap = new ConcurrentHashMap<String, Object>(); /**  * 过期时间集合 (单位: 毫秒) , 记录所有key的到期时间 [注意不是剩余存活时间]   */ public Map<String, Long> expireMap = new ConcurrentHashMap<String, Long>(); 

调用结果如下:

Response Heards Connection: keep-alive Content-Type: application/json Date: Fri, 09 Apr 2021 07:33:59 GMT Keep-Alive: timeout=60 // 重点 Set-Cookie: LakerToken=da14afd3f4b648a889a1e51ac3ec53d7; Max-Age=1800; Expires=Fri, 09-Apr-2021 08:03:59 GMT; Path=/ Transfer-Encoding: chunked  Response Body {  "code": 200,  "msg": "",  "data": {   "tokenName": "LakerToken",   "tokenValue": "da14afd3f4b648a889a1e51ac3ec53d7",   "isLogin": true,   "loginId": "1",   "loginKey": "login",   "tokenTimeout": 1784,   "sessionTimeout": 1784,   "tokenSessionTimeout": -2,   "tokenActivityTimeout": 30,   "loginDevice": "default-device"  } } 

可以看到返回heards中已自动设置:Set-Cookie: LakerToken=da14afd3f4b648a889a1e51ac3ec53d7; Max-Age=1800; Expires=Fri, 09-Apr-2021 08:03:59 GMT; Path=/

登出

@PostMapping("/api/v1/loginOut")  @ApiOperationSupport(order = 3)  @ApiOperation(value = "登出")  @SaCheckLogin  public Response loginOut() {      StpUtil.logout();      return Response.ok();  } 

核心也是一行StpUtil.logout(),来看看它帮我们做了什么?

获取HttpRequest 尝试从request里读取token 尝试从请求体里面读取token 尝试从header里读取token 尝试从cookie里读取token 删除cookie 删除token、loginId映射 注销session

请求拦截鉴权

第一步:配置全局拦截器

@Configuration public class MySaTokenConfig implements WebMvcConfigurer {  /**   * 注册sa-token的拦截器,打开注解式鉴权功能 (如果您不需要此功能,可以删除此类)    */     @Override     public void addInterceptors(InterceptorRegistry registry) {         registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");      } } 

第二步:在需要拦截的类或者方法上加注解

@SaCheckLogin: 标注在方法或类上,当前会话必须处于登录状态才可通过校验 @SaCheckRole("admin"): 标注在方法或类上,当前会话必须具有指定角色标识才能通过校验 @SaCheckPermission("user:add"): 标注在方法或类上,当前会话必须具有指定权限才能通过校验

例如:

@GetMapping("/api/v1/tokenInfo")    @ApiOperationSupport(order = 2)    @ApiOperation(value = "获取当前会话的token信息")    @SaCheckLogin    public Response tokenInfo() {        return Response.ok(StpUtil.getTokenInfo());    } 

加上@SaCheckLogin则该接口必须处于登录状态才可通过校验。

这里核心拦截校验又是如何工作的呢?可以看下SaAnnotationInterceptor.java源码,基于SpringMvc的拦截器实现的拦截校验。

实现功能如下:

验证登录 验证角色 验证权限

实现流程原理如下:

获取HttpRequest中的token 尝试从request里读取token 尝试从请求体里面读取token 尝试从header里读取token 尝试从cookie里读取token 判断token 无效token 过期 被顶下线 被踢下线 自动续期

权限和角色扩展

直接实现StpInterface接口,覆写getPermissionList和getRoleList方法即可。

@Component public class StpInterfaceImpl implements StpInterface {     /**      * 返回一个账号所拥有的权限码集合      */     @Override     public List<String> getPermissionList(Object loginId, String loginKey) {        xxx     }     /**      * 返回一个账号所拥有的角色标识集合      */     @Override     public List<String> getRoleList(Object loginId, String loginKey) {         xxx     } } 

集群环境

Sa-token默认将会话数据保存在内存中,此模式读写速度最快,且避免了序列化与反序列化带来的性能消耗,但是此模式也有一些缺点,比如:重启后数据会丢失,无法在集群模式下共享数据。

为此,sa-token将数据持久操作全部抽象到 SaTokenDao 接口中,此设计可以保证开发者对框架进行灵活扩展,比如我们可以将会话数据存储在 Redis、Memcached等专业的缓存中间件中,做到重启数据不丢失,而且保证分布式环境下多节点的会话一致性。

除了框架内部对SaTokenDao提供的基于内存的默认实现,我们使用官网提供的Redis扩展。

依赖导入

<!-- sa-token整合redis (使用jackson序列化方式) --> <dependency>     <groupId>cn.dev33</groupId>     <artifactId>sa-token-dao-redis-jackson</artifactId>     <version>1.15.2</version> </dependency> <!-- 提供redis连接池 --> <dependency>     <groupId>org.apache.commons</groupId>     <artifactId>commons-pool2</artifactId> </dependency> 

“使用Jackson序列化方式,Session序列化后可读性强,可灵活手动修改

配置文件

spring:      # redis配置      redis:         # Redis数据库索引(默认为0)         database: 0         # Redis服务器地址         host: 127.0.0.1         # Redis服务器连接端口         port: 6379         # Redis服务器连接密码(默认为空)         # password:          # 连接超时时间(毫秒)         timeout: 1000ms         lettuce:             pool:                 # 连接池最大连接数                 max-active: 200                 # 连接池最大阻塞等待时间(使用负值表示没有限制)                 max-wait: -1ms                 # 连接池中的最大空闲连接                 max-idle: 10                 # 连接池中的最小空闲连接                 min-idle: 0 

引入依赖和配置后,框架会自动使用Redis存储。

总结

初步尝试还挺不错的,文档和代码示例都很全,基本功能都能满足,源码简单易懂,可以随意二开,封装度非常高,不理解原理的就很容易变成工具人了,其他的等用一段时间再评论。

参考:

http://sa-token.dev33.cn/

https://github.com/dromara/sa-token

  • Tag:

相关文章

最新评论