從零搭建開發(fā)腳手架 集成認(rèn)證授權(quán) Sa-Token(嘗鮮)
本文轉(zhuǎn)載自微信公眾號(hào)「Java大廠面試官」,作者laker。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java大廠面試官公眾號(hào)。
目前我僅以學(xué)習(xí)和嘗鮮為目的來(lái)集成,不建議用于公司等正式環(huán)境,公司還是建議Shiro和Spring Security那一套。(等我實(shí)戰(zhàn)一波看看效果再說(shuō))
為什么要嘗鮮Sa-Token
之前我還是挺排斥國(guó)產(chǎn)小作坊的開源作品,畢竟不是根紅苗正,但是隨著近幾年國(guó)內(nèi)開源社區(qū)的大力發(fā)展,以及在平時(shí)工作中又接觸了解很多,慢慢改變了我的看法,其實(shí)國(guó)人開源作品還是很香的,其Api簡(jiǎn)單易用,源碼和官方文檔都是中文的,功能豐富且能滿足很多中國(guó)式需求,各種QQ、微信交流群活躍度非常高,總之就是極大程度滿足中國(guó)式需求。
在權(quán)限認(rèn)證框架領(lǐng)域,使用最多的莫過(guò)于Shiro和Spring Security,但是一天在逛同性交友網(wǎng)站(github)的時(shí)候,赫然發(fā)現(xiàn)了Sa-Token其竟然有2K的star數(shù)量,看其中文介紹竟然是輕量級(jí)Java權(quán)限認(rèn)證框架,看了下其特性和功能點(diǎn),就喚起了我強(qiáng)烈的好奇心,于是乎就有了今天的嘗鮮。
Sa-Token是什么?
sa-token是一個(gè)輕量級(jí)Java權(quán)限認(rèn)證框架,主要解決:登錄認(rèn)證、權(quán)限認(rèn)證、Session會(huì)話、單點(diǎn)登錄、OAuth2.0 等一系列權(quán)限相關(guān)問(wèn)題
框架針對(duì)踢人下線、自動(dòng)續(xù)簽、前后臺(tái)分離、分布式會(huì)話……等常見業(yè)務(wù)進(jìn)行N多適配,通過(guò)sa-token,你可以以一種極簡(jiǎn)的方式實(shí)現(xiàn)系統(tǒng)的權(quán)限認(rèn)證部分
與其它權(quán)限認(rèn)證框架相比,sa-token 具有以下優(yōu)勢(shì):
- 簡(jiǎn)單 :可零配置啟動(dòng)框架,真正的開箱即用,低成本上手
- 強(qiáng)大 :目前已集成幾十項(xiàng)權(quán)限相關(guān)特性,涵蓋了大部分業(yè)務(wù)場(chǎng)景的解決方案
- 易用 :如絲般順滑的API調(diào)用,大量高級(jí)特性統(tǒng)統(tǒng)只需一行代碼即可實(shí)現(xiàn)
- 高擴(kuò)展 :幾乎所有組件都提供了擴(kuò)展接口,90%以上的邏輯都可以按需重寫
Sa-Token 能做什么?
- 登錄驗(yàn)證 —— 輕松登錄鑒權(quán),并提供五種細(xì)分場(chǎng)景值
- 權(quán)限驗(yàn)證 —— 適配RBAC權(quán)限模型,不同角色不同授權(quán)
- Session會(huì)話 —— 專業(yè)的數(shù)據(jù)緩存中心
- 踢人下線 —— 將違規(guī)用戶立刻清退下線
- 持久層擴(kuò)展 —— 可集成Redis、Memcached等專業(yè)緩存中間件,重啟數(shù)據(jù)不丟失
- 分布式會(huì)話 —— 提供jwt集成和共享數(shù)據(jù)中心兩種分布式會(huì)話方案
- 單點(diǎn)登錄 —— 一處登錄,處處通行
- 模擬他人賬號(hào) —— 實(shí)時(shí)操作任意用戶狀態(tài)數(shù)據(jù)
- 臨時(shí)身份切換 —— 將會(huì)話身份臨時(shí)切換為其它賬號(hào)
- 無(wú)Cookie模式 —— APP、小程序等前后臺(tái)分離場(chǎng)景
- 同端互斥登錄 —— 像QQ一樣手機(jī)電腦同時(shí)在線,但是兩個(gè)手機(jī)上互斥登錄
- 多賬號(hào)認(rèn)證體系 —— 比如一個(gè)商城項(xiàng)目的user表和admin表分開鑒權(quán)
- 花式token生成 —— 內(nèi)置六種token風(fēng)格,還可自定義token生成策略
- 注解式鑒權(quán) —— 優(yōu)雅的將鑒權(quán)與業(yè)務(wù)代碼分離
- 路由攔截式鑒權(quán) —— 根據(jù)路由攔截鑒權(quán),可適配restful模式
- 自動(dòng)續(xù)簽 —— 提供兩種token過(guò)期策略,靈活搭配使用,還可自動(dòng)續(xù)簽
- 會(huì)話治理 —— 提供方便靈活的會(huì)話查詢接口
- 記住我模式 —— 適配[記住我]模式,重啟瀏覽器免驗(yàn)證
- 密碼加密 —— 提供密碼加密模塊,可快速M(fèi)D5、SHA1、SHA256、AES、RSA加密
- 組件自動(dòng)注入 —— 零配置與Spring等框架集成
快速集成
依賴導(dǎo)入
- <dependency>
- <groupId>cn.dev33</groupId>
- <artifactId>sa-token-spring-boot-starter</artifactId>
- <version>1.15.2</version>
- </dependency>
“最新版本去maven中央庫(kù)自己查詢下,當(dāng)前是1.15.2。
配置文件
你可以零配置啟動(dòng)項(xiàng)目但同時(shí)你也可以在application.yml中增加如下配置,定制性使用框架:
- spring:
- # sa-token配置
- sa-token:
- # token名稱 (同時(shí)也是cookie名稱)
- token-name: satoken
- # token有效期,單位s 默認(rèn)30天, -1代表永不過(guò)期
- timeout: 2592000
- # token臨時(shí)有效期 (指定時(shí)間內(nèi)無(wú)操作就視為token過(guò)期) 單位: 秒
- activity-timeout: -1
- # 是否允許同一賬號(hào)并發(fā)登錄 (為true時(shí)允許一起登錄, 為false時(shí)新登錄擠掉舊登錄)
- allow-concurrent-login: true
- # 在多人登錄同一賬號(hào)時(shí),是否共用一個(gè)token (為true時(shí)所有登錄共用一個(gè)token, 為false時(shí)每次登錄新建一個(gè)token)
- is-share: false
- # token風(fēng)格
- 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);
- // 模擬 校驗(yàn)用戶名密碼
- Long userId = check(userName,pwd);
- StpUtil.setLoginId(userId);
- return Response.ok(StpUtil.getTokenInfo());
- }
核心就一行StpUtil.setLoginId(userId),來(lái)看看它幫我們做了什么?
源碼及其簡(jiǎn)單,還有很多中文注釋,跟著讀就行了,直接貼結(jié)論。
- 創(chuàng)建token
- 創(chuàng)建SaSession
- 在session上記錄token簽名
- 創(chuàng)建token、loginId映射
- token寫入cookie
底層會(huì)話等存儲(chǔ)使用的是Map
源碼如下:
- /**
- * 數(shù)據(jù)集合
- */
- public Map<String, Object> dataMap = new ConcurrentHashMap<String, Object>();
- /**
- * 過(guò)期時(shí)間集合 (單位: 毫秒) , 記錄所有key的到期時(shí)間 [注意不是剩余存活時(shí)間]
- */
- public Map<String, Long> expireMap = new ConcurrentHashMap<String, Long>();
調(diào)用結(jié)果如下:
- Response Heards
- Connection: keep-alive
- Content-Type: application/json
- Date: Fri, 09 Apr 2021 07:33:59 GMT
- Keep-Alive: timeout=60
- // 重點(diǎn)
- 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中已自動(dòng)設(shè)置: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(),來(lái)看看它幫我們做了什么?
- 獲取HttpRequest
- 嘗試從request里讀取token
- 嘗試從請(qǐng)求體里面讀取token
- 嘗試從header里讀取token
- 嘗試從cookie里讀取token
- 刪除cookie
- 刪除token、loginId映射
- 注銷session
請(qǐng)求攔截鑒權(quán)
第一步:配置全局?jǐn)r截器
- @Configuration
- public class MySaTokenConfig implements WebMvcConfigurer {
- /**
- * 注冊(cè)sa-token的攔截器,打開注解式鑒權(quán)功能 (如果您不需要此功能,可以刪除此類)
- */
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
- }
- }
第二步:在需要攔截的類或者方法上加注解
- @SaCheckLogin: 標(biāo)注在方法或類上,當(dāng)前會(huì)話必須處于登錄狀態(tài)才可通過(guò)校驗(yàn)
- @SaCheckRole("admin"): 標(biāo)注在方法或類上,當(dāng)前會(huì)話必須具有指定角色標(biāo)識(shí)才能通過(guò)校驗(yàn)
- @SaCheckPermission("user:add"): 標(biāo)注在方法或類上,當(dāng)前會(huì)話必須具有指定權(quán)限才能通過(guò)校驗(yàn)
例如:
- @GetMapping("/api/v1/tokenInfo")
- @ApiOperationSupport(order = 2)
- @ApiOperation(value = "獲取當(dāng)前會(huì)話的token信息")
- @SaCheckLogin
- public Response tokenInfo() {
- return Response.ok(StpUtil.getTokenInfo());
- }
加上@SaCheckLogin則該接口必須處于登錄狀態(tài)才可通過(guò)校驗(yàn)。
這里核心攔截校驗(yàn)又是如何工作的呢?可以看下SaAnnotationInterceptor.java源碼,基于SpringMvc的攔截器實(shí)現(xiàn)的攔截校驗(yàn)。
實(shí)現(xiàn)功能如下:
- 驗(yàn)證登錄
- 驗(yàn)證角色
- 驗(yàn)證權(quán)限
實(shí)現(xiàn)流程原理如下:
- 獲取HttpRequest中的token
- 嘗試從request里讀取token
- 嘗試從請(qǐng)求體里面讀取token
- 嘗試從header里讀取token
- 嘗試從cookie里讀取token
- 判斷token
- 無(wú)效token
- 過(guò)期
- 被頂下線
- 被踢下線
- 自動(dòng)續(xù)期
權(quán)限和角色擴(kuò)展
直接實(shí)現(xiàn)StpInterface接口,覆寫getPermissionList和getRoleList方法即可。
- @Component
- public class StpInterfaceImpl implements StpInterface {
- /**
- * 返回一個(gè)賬號(hào)所擁有的權(quán)限碼集合
- */
- @Override
- public List<String> getPermissionList(Object loginId, String loginKey) {
- xxx
- }
- /**
- * 返回一個(gè)賬號(hào)所擁有的角色標(biāo)識(shí)集合
- */
- @Override
- public List<String> getRoleList(Object loginId, String loginKey) {
- xxx
- }
- }
集群環(huán)境
Sa-token默認(rèn)將會(huì)話數(shù)據(jù)保存在內(nèi)存中,此模式讀寫速度最快,且避免了序列化與反序列化帶來(lái)的性能消耗,但是此模式也有一些缺點(diǎn),比如:重啟后數(shù)據(jù)會(huì)丟失,無(wú)法在集群模式下共享數(shù)據(jù)。
為此,sa-token將數(shù)據(jù)持久操作全部抽象到 SaTokenDao 接口中,此設(shè)計(jì)可以保證開發(fā)者對(duì)框架進(jìn)行靈活擴(kuò)展,比如我們可以將會(huì)話數(shù)據(jù)存儲(chǔ)在 Redis、Memcached等專業(yè)的緩存中間件中,做到重啟數(shù)據(jù)不丟失,而且保證分布式環(huán)境下多節(jié)點(diǎn)的會(huì)話一致性。
除了框架內(nèi)部對(duì)SaTokenDao提供的基于內(nèi)存的默認(rèn)實(shí)現(xiàn),我們使用官網(wǎng)提供的Redis擴(kuò)展。
依賴導(dǎo)入
- <!-- 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序列化后可讀性強(qiáng),可靈活手動(dòng)修改
配置文件
- spring:
- # redis配置
- redis:
- # Redis數(shù)據(jù)庫(kù)索引(默認(rèn)為0)
- database: 0
- # Redis服務(wù)器地址
- host: 127.0.0.1
- # Redis服務(wù)器連接端口
- port: 6379
- # Redis服務(wù)器連接密碼(默認(rèn)為空)
- # password:
- # 連接超時(shí)時(shí)間(毫秒)
- timeout: 1000ms
- lettuce:
- pool:
- # 連接池最大連接數(shù)
- max-active: 200
- # 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒(méi)有限制)
- max-wait: -1ms
- # 連接池中的最大空閑連接
- max-idle: 10
- # 連接池中的最小空閑連接
- min-idle: 0
引入依賴和配置后,框架會(huì)自動(dòng)使用Redis存儲(chǔ)。
總結(jié)
初步嘗試還挺不錯(cuò)的,文檔和代碼示例都很全,基本功能都能滿足,源碼簡(jiǎn)單易懂,可以隨意二開,封裝度非常高,不理解原理的就很容易變成工具人了,其他的等用一段時(shí)間再評(píng)論。
參考:
http://sa-token.dev33.cn/
https://github.com/dromara/sa-token