电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

Springboot實現Shiro整合JWT的示例代碼

瀏覽:10日期:2023-04-03 15:42:32
寫在前面

之前想嘗試把JWT和Shiro結合到一起,但是在網上查了些博客,也沒太有看懂,所以就自己重新研究了一下Shiro的工作機制,然后自己想了個(傻逼)辦法把JWT和Shiro整合到一起了

另外接下來還會涉及到JWT相關的內容,我之前寫過一篇博客,可以看這里:Springboot實現JWT認證

Shiro的Session機制

由于我的方法是改變了Shiro的默認的Session機制,所以這里先簡單講一下Shiro的機制,簡單了解Shiro是怎么確定每次訪問的是哪個用戶的

Servlet的Session機制

Shiro在JavaWeb中使用到的就是默認的Servlet的Session機制,大致流程如下:

Springboot實現Shiro整合JWT的示例代碼

1.用戶首次發請求

2.服務器接收到請求之后,無論你有沒有權限訪問到資源,在返回響應的時候,服務器都會生成一個Session用來儲存該用戶的信息,然后生成SessionId作為對應的Key

3.服務器會在響應中,用jsessionId這個名字,把這個SessionId以Cookie的方式發給客戶(就是Set-Cookie響應頭)

4.由于已經設置了Cookie,下次訪問的時候,服務器會自動識別到這個SessionId然后找到你上次對應的Session

Shiro帶來的變化

而結合Shiro之后,上面的第二步和第三步會發生小變化:

2.—>服務器不但會創建Session,還會創建一個Subject對象(就是Shiro中用來代表當前用戶的類),也用這個SessionId作為Key綁定

3.—>第二次接受到請求的時候,Shiro會從請求頭中找到SessionId,然后去尋找對應的Subject然后綁定到當前上下文,這時候Shiro就能知道來訪的是誰了

我的思路

由于這個是我自己想出來的,所以可能會存在一定的問題,還請大佬指點

主要思想是:用JWT Token來代替Shiro原本返回的Session

Springboot實現Shiro整合JWT的示例代碼

工作流程:

用戶登錄 若成功則shiro會默認生成一個SessionId用來匹配當前Subject對象,則我們將這個SessionId放入JWT中 返回JWT 用戶第二次攜帶JWT來訪問接口 服務器解析JWT,獲得SessionId 服務器把SessionId交給Shiro執行相關認證代碼實現導入JWT相關包

導入java-jwt包:

這個包里實現了一系列jwt操作的api(包括上面講到的怎么校驗,怎么生成jwt等等)

如果你是Maven玩家:

pom.xml里寫入

<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt --><dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.8.3</version></dependency>

如果你是Gradle玩家:

build.gradle里寫入

compile group: ’com.auth0’, name: ’java-jwt’, version: ’3.8.3’

如果你是其他玩家:

maven中央倉庫地址點這里

JWT工具類

JwtUtils,代碼如下:

import com.auth0.jwt.JWT;import com.auth0.jwt.JWTVerifier;import com.auth0.jwt.algorithms.Algorithm;import com.auth0.jwt.exceptions.JWTDecodeException;import com.auth0.jwt.interfaces.Claim;import com.auth0.jwt.interfaces.DecodedJWT;import java.io.Serializable;import java.util.Calendar;import java.util.Date;/** * @author Lehr * @create: 2020-02-04 */public class JwtUtils { /** 簽發對象:這個用戶的id 簽發時間:現在 有效時間:30分鐘 載荷內容:暫時設計為:這個人的名字,這個人的昵稱 加密密鑰:這個人的id加上一串字符串 */ public static String createToken(String userId,String realName, String userName) { Calendar nowTime = Calendar.getInstance(); nowTime.add(Calendar.MINUTE,30); Date expiresDate = nowTime.getTime(); return JWT.create().withAudience(userId) //簽發對象.withIssuedAt(new Date()) //發行時間.withExpiresAt(expiresDate) //有效時間.withClaim('userName', userName) //載荷,隨便寫幾個都可以.withClaim('realName', realName).sign(Algorithm.HMAC256(userId+'HelloLehr')); //加密 } /** * 檢驗合法性,其中secret參數就應該傳入的是用戶的id * @param token * @throws TokenUnavailable */ public static void verifyToken(String token, String secret) throws TokenUnavailable { DecodedJWT jwt = null; try { JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret+'HelloLehr')).build(); jwt = verifier.verify(token); } catch (Exception e) { //效驗失敗 //這里拋出的異常是我自定義的一個異常,你也可以寫成別的 throw new TokenUnavailable(); } } /** * 獲取簽發對象 */ public static String getAudience(String token) throws TokenUnavailable { String audience = null; try { audience = JWT.decode(token).getAudience().get(0); } catch (JWTDecodeException j) { //這里是token解析失敗 throw new TokenUnavailable(); } return audience; } /** * 通過載荷名字獲取載荷的值 */ public static Claim getClaimByName(String token, String name){ return JWT.decode(token).getClaim(name); }}

一點小說明:

關于jwt生成時的加密和驗證方法:

jwt的驗證其實就是驗證jwt最后那一部分(簽名部分)。這里在指定簽名的加密方式的時候,還傳入了一個字符串來加密,所以驗證的時候不但需要知道加密算法,還需要獲得這個字符串才能成功解密,提高了安全性。我這里用的是id來,比較簡單,如果你想更安全一點,可以把用戶密碼作為這個加密字符串,這樣就算是這段業務代碼泄露了,也不會引發太大的安全問題(畢竟我的id是誰都知道的,這樣令牌就可以被偽造,但是如果換成密碼,只要數據庫沒事那就沒人知道)

關于獲得載荷的方法:

可能有人會覺得奇怪,為什么不需要解密不需要verify就能夠獲取到載荷里的內容呢?原因是,本來載荷就只是用Base64處理了,就沒有加密性,所以能直接獲取到它的值,但是至于可不可以相信這個值的真實性,就是要看能不能通過驗證了,因為最后的簽名部分是和前面頭部和載荷的內容有關聯的,所以一旦簽名驗證過了,那就說明前面的載荷是沒有被改過的。

Controller層

登錄邏輯

/** * 用戶登錄 * @param userName * @param password * @param req * @return * @throws Exception */ @SneakyThrows @PostMapping(value = '/login') public AccountVO login(String userName, String password, HttpServletRequest req){ //嘗試登錄 Subject subject = SecurityUtils.getSubject(); try { subject.login(new UsernamePasswordToken(userName, password)); } catch (Exception e) { throw new LoginFailed(); } AccountVO account = accountService.getAccountByUserName(userName); String id = account.getId(); //生成jwtToken String jwtToken = JwtUtils.createToken(id, account.getRealName(),account.getUserName(), subject.getSession().getId().toString()); //設置好token,后來會在全局處理的時候放入響應里 req.setAttribute('token', jwtToken); return account; }

主要是:在登錄成功之后把這個Subject的SessionId放入JWT然后生成token:

String jwtToken = JwtUtils.createToken(id,account.getRealName(),account.getUserName(),subject.getSession().getId().toString());

以后我們就可以通過解析JWT來獲取SessionId了,而不是每次把SessionId作為Cookie返回

退出邏輯

首先,由于JWT令牌本身就會失效,所以如果JWT令牌失效,也就相當與退出了

然后我們還可以同樣實現Shiro中傳統的手動登出:

public String logout(HttpServletRequest req) { SecurityUtils.getSubject().logout(); return '用?粢丫?踩?淺?; }

這樣的話Realm中的用戶狀態就變成未認證了,就算JWT沒過期也需要重新登錄了

自定義SessionManager

先上代碼:

package com.imlehr.internship.shiroJwt;import com.imlehr.internship.exception.TokenUnavailable;import lombok.SneakyThrows;import org.apache.shiro.session.mgt.SessionKey;import org.apache.shiro.web.servlet.ShiroHttpServletRequest;import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;import org.apache.shiro.web.util.WebUtils;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.Serializable;import java.util.UUID;/** * @author Lehr * @create: 2020-02-10 */public class CustomSessionManager extends DefaultWebSessionManager { //這里我為了省事用了lombok的標簽 @SneakyThrows @Override protected Serializable getSessionId(ServletRequest request, ServletResponse response) { String token = WebUtils.toHttp(request).getHeader('token'); System.out.println('會話管理器得到的token是:' + token); if (token == null || token.length()<1) { return UUID.randomUUID().toString(); } //在這里驗證一下jwt了,雖然我知道這樣不好 String userId = JwtUtils.getAudience(token); JwtUtils.verifyToken(token, userId); String sessionId = JwtUtils.getClaimByName(token, 'sessionId').asString(); if (sessionId == null) { return new TokenUnavailable(); }request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, 'header'); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, isSessionIdUrlRewritingEnabled()); return sessionId; }}

之前的Session的獲取,就是在DefaultWebSessionManager里實現的,所以我們現在只需要重寫這個類,把我們如何獲取Session的邏輯寫進去就好了

這里說兩個方法:

getSessionId(SessionKey key)

這個方法是在DefaultWebSessionManager的,這里并沒有重寫,我們上面重寫的是后面第二個同名方法,只是想在這里談談,讀者可以直接跳過這段也不影響

源碼邏輯

在Shiro想要獲取SessionId的時候,首先會調用的就是這個方法,而不是那個傳入httpRequest的方法

在DefaultWebSessionManager中,他是這樣做的

@Overridepublic Serializable getSessionId(SessionKey key) { Serializable id = super.getSessionId(key); if (id == null && WebUtils.isWeb(key)) { ServletRequest request = WebUtils.getRequest(key); ServletResponse response = WebUtils.getResponse(key); //調用第二個同名方法 id = getSessionId(request, response); } return id;} 如果沒能找到id,就調用第二個同名方法 如果有,就返回

這里需要注意的是,這個方法會在整個驗證過程中多次被反復調用,而在服務器接受到用戶請求的時候,只會調用一次的方法是下面這個,也就是我們重寫的這個

getSessionId(ServletRequest request, ServletResponse response)

這個才是真正涉及到服務器接受到請求的時候獲取Session邏輯,從用戶的請求報文中獲取SessionId

所以我們要重寫的就是這一步

原版中的邏輯是:從Cookie里找到sessionId的值

我們只需要把邏輯該為:從Header中找出JWT(也就是從請求頭的’token’頭中找),然后解析JWT,獲取到我們存放在其中的SessionId屬性即可

ShiroConfiguration

我們只需要把自己寫的SessionManager配置進去就好了

首先配好:

public DefaultWebSessionManager sessionManager(){ CustomSessionManager customSessionManager = new CustomSessionManager(); return customSessionManager;}

然后放入SecurityManager

public SecurityManager securityManager(MyRealm myRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myRealm); return securityManager;}

完成🎉

測試登錄

Springboot實現Shiro整合JWT的示例代碼

我們獲取到了JWT,JWT里面就帶有SessionId

后續請求不帶token

Springboot實現Shiro整合JWT的示例代碼

顯然,沒過認證,我們看下后臺:

Springboot實現Shiro整合JWT的示例代碼

因為不能獲得token所以無法得到該用戶對應的sessionId,所以被授權攔截了

后面那個JSESSIONID是因為沒得到sessionId新生成的,所以對應了一個沒有登錄的用戶,自然就會被拒絕

只有帶上之前的token,shiro才會認為我們是之前那個已經登錄過的用戶

后續請求帶token

Springboot實現Shiro整合JWT的示例代碼

后臺:

Springboot實現Shiro整合JWT的示例代碼

成功!

另外,因為JWT本身就適合RESTful API服務,所以,如果把Shiro和Redis整合起來做成分布式的,那么效果會更好

到此這篇關于Springboot實現Shiro整合JWT的示例代碼的文章就介紹到這了,更多相關Springboot Shiro整合JWT內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
主站蜘蛛池模板: 奇酷教育-Python培训|UI培训|WEB大前端培训|Unity3D培训|HTML5培训|人工智能培训|JAVA开发的教育品牌 | 蔡司三坐标-影像测量机-3D扫描仪-蔡司显微镜-扫描电镜-工业CT-ZEISS授权代理商三本工业测量 | 上海阳光泵业制造有限公司 -【官方网站】 | 升降机-高空作业车租赁-蜘蛛车-曲臂式伸缩臂剪叉式液压升降平台-脚手架-【普雷斯特公司厂家】 | 生态板-实木生态板-生态板厂家-源木原作生态板品牌-深圳市方舟木业有限公司 | 全自动变压器变比组别测试仪-手持式直流电阻测试仪-上海来扬电气 | 经济师考试_2025中级经济师报名时间_报名入口_考试时间_华课网校经济师培训网站 | 网络推广公司_网络营销方案策划_企业网络推广外包平台-上海澜推网络 | 高空重型升降平台_高空液压举升平台_高空作业平台_移动式升降机-河南华鹰机械设备有限公司 | 石磨面粉机|石磨面粉机械|石磨面粉机组|石磨面粉成套设备-河南成立粮油机械有限公司 | 帽子厂家_帽子工厂_帽子定做_义乌帽厂_帽厂_制帽厂_帽子厂_浙江高普制帽厂 | 信阳网站建设专家-信阳时代网联-【信阳网站建设百度推广优质服务提供商】信阳网站建设|信阳网络公司|信阳网络营销推广 | 哈尔滨京科脑康神经内科医院-哈尔滨治疗头痛医院-哈尔滨治疗癫痫康复医院 | 今日娱乐圈——影视剧集_八卦娱乐_明星八卦_最新娱乐八卦新闻 | 北京康百特科技有限公司-分子蒸馏-短程分子蒸馏设备-实验室分子蒸馏设备 | 河南正规膏药生产厂家-膏药贴牌-膏药代加工-修康药业集团官网 | 柔性输送线|柔性链板|齿形链-上海赫勒输送设备有限公司首页[输送机] | 广州展台特装搭建商|特装展位设计搭建|展会特装搭建|特装展台制作设计|展览特装公司 | 意大利Frascold/富士豪压缩机_富士豪半封闭压缩机_富士豪活塞压缩机_富士豪螺杆压缩机 | 超高频感应加热设备_高频感应电源厂家_CCD视觉检测设备_振动盘视觉检测设备_深圳雨滴科技-深圳市雨滴科技有限公司 | 12cr1mov无缝钢管切割-15crmog无缝钢管切割-40cr无缝钢管切割-42crmo无缝钢管切割-Q345B无缝钢管切割-45#无缝钢管切割 - 聊城宽达钢管有限公司 | 杭州货架订做_组合货架公司_货位式货架_贯通式_重型仓储_工厂货架_货架销售厂家_杭州永诚货架有限公司 | 缠绕机|缠绕膜包装机|缠绕包装机-上海晏陵智能设备有限公司 | 高铝砖-高铝耐火球-高铝耐火砖生产厂家-价格【荣盛耐材】 | 电机修理_二手电机专家-河北豫通机电设备有限公司(原石家庄冀华高压电机维修中心) | 智能监控-安防监控-监控系统安装-弱电工程公司_成都万全电子 | 家德利门业,家居安全门,别墅大门 - 安徽家德利门业有限公司 | 皮带式输送机械|链板式输送机|不锈钢输送机|网带输送机械设备——青岛鸿儒机械有限公司 | 正压密封性测试仪-静态发色仪-导丝头柔软性测试仪-济南恒品机电技术有限公司 | 武汉天安盾电子设备有限公司 - 安盾安检,武汉安检门,武汉安检机,武汉金属探测器,武汉测温安检门,武汉X光行李安检机,武汉防爆罐,武汉车底安全检查,武汉液体探测仪,武汉安检防爆设备 | 打造全球沸石生态圈 - 国投盛世| 色油机-色母机-失重|称重式混料机-称重机-米重机-拌料机-[东莞同锐机械]精密计量科技制造商 | 欧美日韩国产一区二区三区不_久久久久国产精品无码不卡_亚洲欧洲美洲无码精品AV_精品一区美女视频_日韩黄色性爱一级视频_日本五十路人妻斩_国产99视频免费精品是看4_亚洲中文字幕无码一二三四区_国产小萍萍挤奶喷奶水_亚洲另类精品无码在线一区 | 天津市能谱科技有限公司-专业的红外光谱仪_红外测油仪_紫外测油仪_红外制样附件_傅里叶红外光谱技术生产服务厂商 | 包头市鑫枫装饰有限公司| 冷却塔风机厂家_静音冷却塔风机_冷却塔电机维修更换维修-广东特菱节能空调设备有限公司 | 创富网-B2B网站|供求信息网|b2b平台|专业电子商务网站 | 横河变送器-横河压力变送器-EJA变送器-EJA压力变送器-「泉蕴仪表」 | 磁棒电感生产厂家-电感器厂家-电感定制-贴片功率电感供应商-棒形电感生产厂家-苏州谷景电子有限公司 | pH污水传感器电极,溶解氧电极传感器-上海科蓝仪表科技有限公司 | 郑州水质检测中心_井水检测_河南废气检测_河南中环嘉创检测 |