日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

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

利用Springboot實現Jwt認證的示例代碼

瀏覽:30日期:2023-04-03 16:45:53

JSON Web Token是目前最流行的跨域認證解決方案,,適合前后端分離項目通過Restful API進行數據交互時進行身份認證

利用Springboot實現Jwt認證的示例代碼

關于Shiro整合JWT,可以看這里:Springboot實現Shiro+JWT認證

概述

由于概念性內容網上多的是,所以就不詳細介紹了

具體可以看這里:阮一峰大佬的博客

我總結幾個重點:

JWT,全稱Json Web Token,是一種令牌認證的方式

長相:

利用Springboot實現Jwt認證的示例代碼

頭部:放有簽名算法和令牌類型(這個就是JWT) 載荷:你在令牌上附帶的信息:比如用戶的id,用戶的電話號碼,這樣以后驗證了令牌之后就可以直接從這里獲取信息而不用再查數據庫了 簽名:用來加令牌的

安全性:由于載荷里的內容都是用BASE64處理的,所以是沒有保密性的(因為BASE64是對稱的),但是由于簽名認證的原因,其他人很難偽造數據。不過這也意味著,你不能把敏感信息比如密碼放入載荷中,畢竟這種可以被別人直接看到的,但是像用戶id這種就無所謂了

工作流程登錄階段

用戶首次登錄,通過賬號密碼比對,判定是否登錄成功,如果登錄成功的話,就生成一個jwt字符串,然后放入一些附帶信息,返回給客戶端。

利用Springboot實現Jwt認證的示例代碼

這個jwt字符串里包含了有用戶的相關信息,比如這個用戶是誰,他的id是多少,這個令牌的有效時間是多久等等。下次用戶登錄的時候,必須把這個令牌也一起帶上。

認證階段

這里需要和前端統一約定好,在發起請求的時候,會把上次的token放在請求頭里的某個位置一起發送過來,后端接受到請求之后,會解析jwt,驗證jwt是否合法,有沒有被偽造,是否過期,到這里,驗證過程就完成了。

利用Springboot實現Jwt認證的示例代碼

不過服務器同樣可以從驗證后的jwt里獲取用戶的相關信息,從而減少對數據庫的查詢。

比如我們有這樣一個業務:“通過用戶電話號碼查詢用戶余額”

如果我們在jwt的載荷里事先就放有電話號碼這個屬性,那么我們就可以避免先去數據庫根據用戶id查詢用戶電話號碼,而直接拿到電話號碼,然后執行接下里的業務邏輯。

關于有效期

由于jwt是直接給用戶的,只要能驗證成功的jwt都可以被視作登錄成功,所以,如果不給jwt設置一個過期時間的話,用戶只要存著這個jwt,就相當于永遠登錄了,而這是不安全的,因為如果這個令牌泄露了,那么服務器是沒有任何辦法阻止該令牌的持有者訪問的(因為拿到這個令牌就等于隨便冒充你身份訪問了),所以往往jwt都會有一個有效期,通常存在于載荷部分,下面是一段生成jwt的java代碼:

return JWT.create().withAudience(userId) .withIssuedAt(new Date()) <---- 發行時間 .withExpiresAt(expiresDate) <---- 有效期 .withClaim('sessionId', sessionId) .withClaim('userName', userName) .withClaim('realName', realName) .sign(Algorithm.HMAC256(userId+'HelloLehr'));

在實際的開發中,令牌的有效期往往是越短越安全,因為令牌會頻繁變化,即使有某個令牌被別人盜用,也會很快失效。但是有效期短也會導致用戶體驗不好(總是需要重新登錄),所以這時候就會出現另外一種令牌—refresh token刷新令牌。刷新令牌的有效期會很長,只要刷新令牌沒有過期,就可以再申請另外一個jwt而無需登錄(且這個過程是在用戶訪問某個接口時自動完成的,用戶不會感覺到令牌替換),對于刷新令牌的具體實現這里就不詳細講啦(其實因為我也沒深入研究過XD…)

對比Session

在傳統的session會話機制中,服務器識別用戶是通過用戶首次訪問服務器的時候,給用戶一個sessionId,然后把用戶對應的會話記錄放在服務器這里,以后每次通過sessionId來找到對應的會話記錄。這樣雖然所有的數據都存在服務器上是安全的,但是對于分布式的應用來說,就需要考慮session共享的問題了,不然同一個用戶的sessionId的請求被自動分配到另外一個服務器上就等于失效了

而Jwt不但可以用于登錄認證,也把相應的數據返回給了用戶(就是載荷里的內容),通過簽名來保證數據的真實性,該應用的各個服務器上都有統一的驗證方法,只要能通過驗證,就說明你的令牌是可信的,我就可以從你的令牌上獲取你的信息,知道你是誰了,從而減輕了服務器的壓力,而且也對分布式應用更為友好。(畢竟就不用擔心服務器session的分布式存儲問題了)

整合Springboot導入java-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中央倉庫地址點這里

工具類的編寫

代碼如下:

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層上的每個方法上,可以使用這些注解,來決定訪問這個方法是否需要攜帶token,由于默認是全部檢查,所以對于某些特殊接口需要有免驗證注解

免驗證注解

@PassToken:跳過驗證,通常是入口方法上用這個,比如登錄接口

import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * @author Lehr * @create: 2020-02-03 */@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface PassToken { boolean required() default true;}攔截器的編寫

配置類

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @author lehr*/@Configurationpublic class JwtInterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { //默認攔截所有路徑 registry.addInterceptor(authenticationInterceptor()) .addPathPatterns('/**'); } @Bean public JwtAuthenticationInterceptor authenticationInterceptor() { return new JwtAuthenticationInterceptor(); }}

攔截器

import com.auth0.jwt.interfaces.Claim;import com.imlehr.internship.annotation.PassToken;import com.imlehr.internship.dto.AccountDTO;import com.imlehr.internship.exception.NeedToLogin;import com.imlehr.internship.exception.UserNotExist;import com.imlehr.internship.service.AccountService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.lang.reflect.Method;import java.util.Map;/** * @author Lehr * @create: 2020-02-03 */public class JwtAuthenticationInterceptor implements HandlerInterceptor { @Autowired AccountService accountService; @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { // 從請求頭中取出 token 這里需要和前端約定好把jwt放到請求頭一個叫token的地方 String token = httpServletRequest.getHeader('token'); // 如果不是映射到方法直接通過 if (!(object instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod = (HandlerMethod) object; Method method = handlerMethod.getMethod(); //檢查是否有passtoken注釋,有則跳過認證 if (method.isAnnotationPresent(PassToken.class)) { PassToken passToken = method.getAnnotation(PassToken.class); if (passToken.required()) { return true; } } //默認全部檢查 else { System.out.println('被jwt攔截需要驗證'); // 執行認證 if (token == null) { //這里其實是登錄失效,沒token了 這個錯誤也是我自定義的,讀者需要自己修改 throw new NeedToLogin(); } // 獲取 token 中的 user Name String userId = JwtUtils.getAudience(token); //找找看是否有這個user 因為我們需要檢查用戶是否存在,讀者可以自行修改邏輯 AccountDTO user = accountService.getByUserName(userId); if (user == null) { //這個錯誤也是我自定義的 throw new UserNotExist(); } // 驗證 token JwtUtils.verifyToken(token, userId) //獲取載荷內容 String userName = JwtUtils.getClaimByName(token, 'userName').asString(); String realName = JwtUtils.getClaimByName(token, 'realName').asString(); //放入attribute以便后面調用 request.setAttribute('userName', userName); request.setAttribute('realName', realName); return true; } return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { }}

這段代碼的執行邏輯大概是這樣的:

目標方法是否有注解?如果有PassToken的話就不用執行后面的驗證直接放行,不然全部需要驗證 開始驗證:有沒有token?沒有?那么返回錯誤 從token的audience中獲取簽發對象,查看是否有這個用戶(有可能客戶端造假,有可能這個用戶的賬戶被凍結了),查看用戶的邏輯就是調用Service方法直接比對即可 檢驗Jwt的有效性,如果無效或者過期了就返回錯誤 Jwt有效性檢驗成功:把Jwt的載荷內容獲取到,可以在接下來的controller層中直接使用了(具體使用方法看后面的代碼)接口的編寫

這里設計了兩個接口:登錄和查詢名字,來模擬一個迷你業務,其中后者需要登錄之后才能使用,大致流程如下:

利用Springboot實現Jwt認證的示例代碼

登錄代碼

/** * 用戶登錄:獲取賬號密碼并登錄,如果不對就報錯,對了就返回用戶的登錄信息 * 同時生成jwt返回給用戶 * * @return * @throws LoginFailed 這個LoginFailed也是我自定義的 */ @PassToken @GetMapping(value = '/login') public AccountVO login(String userName, String password) throws LoginFailed{ try{ service.login(userName,password); } catch (AuthenticationException e) { throw new LoginFailed(); } //如果成功了,聚合需要返回的信息 AccountVO account = accountService.getAccountByUserName(userName); //給分配一個token 然后返回 String jwtToken = JwtUtils.createToken(account); //我的處理方式是把token放到accountVO里去了 account.setToken(jwtToken); return account; }

業務代碼

這里列舉一個需要登錄,用來測試用戶名字的接口(其中用戶的名字來源于jwt的載荷部分)

@GetMapping(value = '/username') public String checkName(HttpServletRequest req) { //之前在攔截器里設置好的名字現在可以取出來直接用了 String name = (String) req.getAttribute('userName'); return name; }

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

標簽: Spring
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
欧美另类中文字幕| 黄色成人91| 亚洲成人不卡| 日韩一区二区三区免费视频| 国产成人精品一区二区免费看京| 国产亚洲亚洲| 粉嫩av一区二区三区四区五区 | 久久国产免费| 日韩 欧美一区二区三区| 你懂的国产精品永久在线| 一本色道久久精品| 黄色网一区二区| 视频国产精品| 亚洲二区免费| 高清久久精品| 97se亚洲| 欧美日韩少妇| 成人高清一区| 国产午夜一区| 在线精品视频一区| 一区二区小说| 成人午夜在线| 欧美精品福利| 在线精品一区| 国产精品av久久久久久麻豆网| 国产精品成人3p一区二区三区| 麻豆精品91| 久久久久国产精品一区二区| 久久精品资源| 日韩和欧美一区二区三区| 国产伊人精品| 在线手机中文字幕| 久久伊人亚洲| 91成人小视频| 亚洲v天堂v手机在线| 欧美在线资源| 香蕉视频亚洲一级| 91一区二区三区四区| 国产精品一区二区三区av| 9色精品在线| 久久青草久久| 色婷婷亚洲mv天堂mv在影片| 国产精品久av福利在线观看| 日本免费一区二区视频| 先锋影音国产一区| 黄色成人在线网址| 午夜av一区| 久久免费黄色| 亚洲日本网址| 久久毛片亚洲| 韩日一区二区| 你懂的网址国产 欧美| 日韩精品欧美精品| 蜜臀av在线播放一区二区三区| 亚洲www啪成人一区二区| 成人黄色av| 国产专区精品| 久久精品国产一区二区| 麻豆国产精品| 麻豆一区二区99久久久久| 国产精品www994| 国产精品xxxav免费视频| 国产精品网在线观看| 国产精品一区二区av交换| 国产日韩欧美一区二区三区| 欧美在线首页| 国产精品一级在线观看| 国产精品嫩草影院在线看| 久久激情五月激情| 国产亚洲欧美日韩精品一区二区三区 | 麻豆高清免费国产一区| 国产精品1区| 国产日产精品_国产精品毛片 | 亚洲精品在线a| 午夜精品福利影院| 日韩二区三区四区| 国产欧美久久一区二区三区| 国产精品115| 美女国产一区二区三区| 高清一区二区| 秋霞影院一区二区三区| 午夜电影亚洲| 男人的天堂亚洲一区| 一二三区精品| 91大神在线观看线路一区| 国产精品亚洲综合在线观看| 国产高清精品二区| 国产一区二区三区日韩精品| 免费观看亚洲| 在线国产一区二区| 天堂av一区| 久久三级中文| 色老板在线视频一区二区| 99热精品在线| 午夜亚洲福利| 精品精品99| 激情欧美一区| 亚洲另类黄色| 国产精品国码视频| 日韩免费高清| 美女精品在线观看| 国产情侣久久| 在线天堂中文资源最新版| 蜜臀久久99精品久久一区二区| 天堂成人国产精品一区| 国产精品一区二区美女视频免费看 | 麻豆精品91| 欧美日韩视频免费看| 国产一区二区三区精品在线观看| 久久精品91| 四虎国产精品免费久久| 麻豆精品久久| 亚洲韩日在线| 日韩综合一区二区| 国产91在线精品| 亚洲欧美日韩一区在线观看| 国产亚洲一区| 99久久99视频只有精品| 亚洲欧美日韩精品一区二区| 国产乱码精品| 亚洲国产综合在线看不卡| 日韩精品一级| 日韩黄色大片网站| 亚洲一区二区三区中文字幕在线观看| 国产精品66| 九九久久电影| 国产精品一区二区av交换| 私拍精品福利视频在线一区| 中文字幕av亚洲精品一部二部| 久久一区亚洲| 美女久久一区| 精品久久91| 亚洲一区有码| 久久精品福利| 日韩专区在线视频| 精品一区二区三区亚洲| 久久国产99| 精品九九久久| 中文字幕一区二区精品区| 日韩久久精品| 欧美日韩亚洲一区三区| 欧美一区二区三区激情视频| 国产精品久久久久av蜜臀| 怡红院精品视频在线观看极品| 麻豆高清免费国产一区| 蜜桃av一区二区在线观看| а√在线中文在线新版| 日韩精品一区二区三区中文在线| 中文字幕在线免费观看视频| 日韩av在线播放中文字幕| 午夜av一区| 开心激情综合| 日韩午夜视频在线| 免费成人网www| 国产精品99视频| 日韩av字幕| 日韩中文字幕1| 亚洲a在线视频| 国产成人1区| 国产午夜久久av| 亚洲精品精选| 国产视频一区在线观看一区免费| 黄色aa久久| 国产精品任我爽爆在线播放| 视频一区二区三区入口| 999久久久亚洲| 国产精品99久久精品| 久久精品超碰| 免费视频最近日韩| 欧美二区视频| 日韩深夜视频| 久久精品国产福利| 国产一卡不卡| 亚洲三级观看| 国产一区二区精品| 久久久噜噜噜| 久久电影tv| 国产一区二区亚洲| 国产剧情在线观看一区| 综合激情在线| 日韩一级精品| 国产精品99免费看| 日韩在线看片| 国产中文欧美日韩在线| 国产日韩免费| 青青国产91久久久久久| 一区二区高清| 日韩精品一二三四| 亚洲欧美久久久| 五月天综合网站| 亚洲成人二区| 韩国精品主播一区二区在线观看| bbw在线视频| 国产一区二区三区视频在线| 国产精品**亚洲精品| 欧美欧美黄在线二区| 日韩av三区| 日韩福利在线观看| 日韩av黄色在线| 欧美日韩一区二区高清|