热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

文章目录为什么要尝鲜Sa-TokenSa-Token是什么?Sa-Token能做什么?快速集成依赖导入配置文件登录登出请求拦截鉴权权限和角色扩展集群环境

文章目录

    • 为什么要尝鲜Sa-Token
    • Sa-Token是什么?
    • Sa-Token 能做什么?
    • 快速集成
      • 依赖导入
      • 配置文件
      • 登录
      • 登出
      • 请求拦截鉴权
        • 权限和角色扩展
      • 集群环境
        • 依赖导入
        • 配置文件
    • 总结



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

为什么要尝鲜Sa-Token

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

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

Sa-Token是什么?

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

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

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

  1. 简单 :可零配置启动框架,真正的开箱即用,低成本上手
  2. 强大 :目前已集成几十项权限相关特性,涵盖了大部分业务场景的解决方案
  3. 易用 :如丝般顺滑的API调用,大量高级特性统统只需一行代码即可实现
  4. 高扩展 :几乎所有组件都提供了扩展接口,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.dev33groupId><artifactId>sa-token-spring-boot-starterartifactId><version>1.15.2version>
dependency>

最新版本去maven中央库自己查询下&#xff0c;当前是1.15.2。


配置文件

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

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

登录

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

核心就一行StpUtil.setLoginId(userId)&#xff0c;来看看它帮我们做了什么&#xff1f;

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


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

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

源码如下&#xff1a;

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

调用结果如下&#xff1a;

  • Response Heards

Connection: keep-alive
Content-Type: application/json
Date: Fri, 09 Apr 2021 07:33:59 GMT
Keep-Alive: timeout&#61;60
// 重点
Set-COOKIE: LakerToken&#61;da14afd3f4b648a889a1e51ac3ec53d7; Max-Age&#61;1800; Expires&#61;Fri, 09-Apr-2021 08:03:59 GMT; Path&#61;/
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中已自动设置&#xff1a;Set-COOKIE: LakerToken&#61;da14afd3f4b648a889a1e51ac3ec53d7; Max-Age&#61;1800; Expires&#61;Fri, 09-Apr-2021 08:03:59 GMT; Path&#61;/


登出

&#64;PostMapping("/api/v1/loginOut")&#64;ApiOperationSupport(order &#61; 3)&#64;ApiOperation(value &#61; "登出")&#64;SaCheckLoginpublic Response loginOut() {StpUtil.logout();return Response.ok();}

核心也是一行StpUtil.logout()&#xff0c;来看看它帮我们做了什么&#xff1f;

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

请求拦截鉴权

第一步&#xff1a;配置全局拦截器

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

第二步&#xff1a;在需要拦截的类或者方法上加注解

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

例如&#xff1a;

&#64;GetMapping("/api/v1/tokenInfo")&#64;ApiOperationSupport(order &#61; 2)&#64;ApiOperation(value &#61; "获取当前会话的token信息")&#64;SaCheckLoginpublic Response tokenInfo() {return Response.ok(StpUtil.getTokenInfo());}

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

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

实现功能如下&#xff1a;

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

实现流程原理如下&#xff1a;

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

权限和角色扩展

直接实现StpInterface接口&#xff0c;覆写getPermissionListgetRoleList方法即可。

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

集群环境

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

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

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

依赖导入


<dependency><groupId>cn.dev33groupId><artifactId>sa-token-dao-redis-jacksonartifactId><version>1.15.2version>
dependency>

<dependency><groupId>org.apache.commonsgroupId><artifactId>commons-pool2artifactId>
dependency>

使用Jackson序列化方式&#xff0c;Session序列化后可读性强&#xff0c;可灵活手动修改


配置文件

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

引入依赖和配置后&#xff0c;框架会自动使用Redis存储


总结

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

参考&#xff1a;

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

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


    加群一起抱团取暖&#xff0c;共同进步

&#x1f34e;QQ群【837324215】
&#x1f34e;关注我的公众号【Java大厂面试官】&#xff0c;一起学习呗&#x1f34e;&#x1f34e;&#x1f34e;
&#x1f34e;个人vxlakernote

img


推荐阅读
author-avatar
手机用户2502852037
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有