SpringBoot整合JWT(JSON?Web?Token)生成token與驗證的流程及示例
JWT
什么是JWT
JWT(JSON Web Token)是是目前最流行的跨域認證解決方案。它通常被用于對用戶進行身份驗證和授權。JWT由三部分組成,每個部分之間使用"."進行分隔,這三部分分別是:
- Header: 包含了聲明類型和JWT的加密算法
- Payload: 負載,存放有效信息的地方。這些有效信息包含三個部分:標準中注冊的聲明、公共的聲明 和 私有的聲明。
- Signature: 簽名信息。
官網(wǎng)地址:https://jwt.io/introduction
JWT使用流程
確定要傳遞的信息:
- 首先,確定您想在JWT中傳遞的信息。這通常包括用戶的唯一標識符(如用戶ID)、角色、用戶名等。
生成JWT:
- 生成Header:Header是JWT的第一部分,它描述了JWT的類型(
"typ"
)和加密算法("alg"
)。 - 生成Payload:Payload是JWT的第二部分,包含了編碼后的信息。在Payload中,通常還會添加一個
"iat"
字段,表示JWT的簽發(fā)時間(Issued At) - 生成Signature:使用一個密鑰(Secret Key)對Header和Payload進行簽名,以確保它們的完整性和真實性。簽名是通過指定的算法(如HMAC SHA256)生成的。
- 將Base64編碼后的Header、Payload和Signature用點號(.)連接起來,形成一個完整的JWT字符串。
- 例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWT傳輸:
- 服務器將生成的JWT發(fā)送給客戶端,通常通過在HTTP響應的頭部設置Authorization字段的值為
Bearer JWT
。
客戶端保存JWT:
- 客戶端在接收到JWT后,將其保存在本地,通常存儲在Cookie、LocalStorage或SessionStorage中。
客戶端發(fā)送JWT:
- 客戶端在每次請求服務器時,將JWT帶在請求的頭部,通過Authorization字段將JWT發(fā)送給服務器。
- 請求頭的格式通常為:
Authorization: Bearer JWT
服務器驗證JWT:
- 服務器接收到請求后,從請求的頭部中獲取JWT,并進行驗證。
- 驗證的過程包括解析JWT、驗證簽名的完整性和真實性、檢查JWT是否過期等。
- 如果驗證通過,服務器會進一步解析Payload中的數(shù)據(jù),獲取用戶的相關信息。
服務器響應:
- 如果JWT驗證通過,服務器會繼續(xù)處理請求,并返回相應的數(shù)據(jù)。
- 如果JWT驗證失敗,服務器會返回相應的錯誤信息。
通過以上流程,JWT實現(xiàn)了一種安全、緊湊、自包含的令牌傳遞機制,用于在客戶端和服務器之間安全地傳輸用戶信息。
springboot引入jwt相關依賴
要在Spring Boot項目中引入jjwt
,你需要在你的pom.xml
(如果你使用Maven)或build.gradle
(如果你使用Gradle)文件中添加相應的依賴。
maven:
<!--引入jwt相關包來生成token--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency>
Gradle:
dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.11.2' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.2' }
Token的使用示例:
工具類
先寫一個創(chuàng)建Token的方法,再寫一個驗證token的方法
import com.certificateManage.common.R; import com.certificateManage.controller.exception.TokenException; import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.SignatureException; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Date; //用于生成token的類 public class JwtTokenUtil { private static final String SECRET_KEY = "abcdefgabcdefghijklmnopqrstuvwxyz"; // 密鑰 private static final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;//加密方式 //ttMillis是token持續(xù)時間 public static String createToken(String id, long ttlMillis) { // 簽名密鑰 byte[] secretKeyBytes = SECRET_KEY.getBytes(StandardCharsets.UTF_8); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, signatureAlgorithm.getJcaName()); // 設置JWT的簽發(fā)時間和過期時間 Date now = new Date(); Date expiration = new Date(now.getTime() + ttlMillis); // 使用指定的密鑰和算法生成JWT return Jwts.builder() .setSubject(id)//設置id .setIssuedAt(now) // 設置簽發(fā)時間 .setExpiration(expiration) // 設置過期時間 .signWith(secretKeySpec,signatureAlgorithm) // 設置簽名密鑰和簽名算法 .compact(); // 生成JWT字符串 } //驗證token如果正確返回用戶id public static R checkToken(String token){ try { // 解析token Claims claims = Jwts.parser() .setSigningKey(Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8))) // 設置密鑰 .parseClaimsJws(token) // 解析token .getBody(); // 獲取負載 // 驗證負載中的信息 String subject = claims.getSubject(); // 獲取用戶ID或其他信息 Date expiration = claims.getExpiration(); // 獲取過期時間 System.out.println(expiration.toString()); // 驗證token是否過期 if (expiration.before(new Date())) { throw new TokenException("token失效"); } return R.success(subject); } catch (ExpiredJwtException e) { // 當token過期時,會捕獲到ExpiredJwtException異常 return R.error("Token已過期"); } catch (UnsupportedJwtException e) { // 當token不受支持時,會捕獲到UnsupportedJwtException異常 return R.error("Token不受支持"); } catch (MalformedJwtException e) { // 當token格式錯誤時,會捕獲到MalformedJwtException異常 return R.error("Token格式錯誤"); } catch (SignatureException e) { // 當token簽名錯誤時,會捕獲到SignatureException異常 return R.error("Token簽名錯誤"); } catch (IllegalArgumentException e) { // 當token為空或非法時,會捕獲到IllegalArgumentException異常 return R.error("Token為空或非法"); } catch (TokenException e) { // 處理TokenException return R.error("Token驗證失敗: " + e.getMessage()); } catch (Exception e) { // 處理其他異常 return R.error("發(fā)生未知錯誤: " + e.getMessage()); } } } // TokenException類,用于處理與Token相關的異常 public class TokenException extends Exception { public TokenException(String message) { super(message); } } }
R結果集
創(chuàng)建一個R結果集用來封裝結果
//統(tǒng)一返回為結果集R 1為成功,0為失敗 public class R<T> { private Integer code; //編碼:1成功,0和其它數(shù)字為失敗 private String msg; //錯誤信息 private T data; //數(shù)據(jù) //靜態(tài)方法返回成功時候,R的屬性 public static <T> R<T> success(T object) { R<T> r = new R<>(); r.data = object; r.code = 1; return r; } //靜態(tài)方法返回失敗時傳入消息 public static <T> R error(String msg) { R r = new R(); r.msg = msg; r.code = 0; return r; } // getter和setter方法省略... }
返回一個生成的token
在用戶發(fā)送登錄請求后如果驗證通過就返回一個生成的token
String token=JwtTokenUtil.createToken(String.valueOf(user.getId()),3600000L);//生成token返回前端 return R.success(token);
接下來就是攔截所有請求并且驗證響應頭的token是否正確
- 請求頭的格式通常為:
Authorization: Bearer JWT
線程工具類
在每次請求時在當前線程進行存儲信息
public class BaseContext { // 使用ThreadLocal來存儲用戶ID private static final ThreadLocal<String> userIdThreadLocal = new ThreadLocal<>(); // 設置用戶ID public static void setUserId(String userId) { userIdThreadLocal.set(userId); } // 獲取用戶ID public static String getUserId() { return userIdThreadLocal.get(); } // 清除用戶ID(通常在請求處理完畢后調用) public static void clearUserId() { userIdThreadLocal.remove(); } }
創(chuàng)建攔截器
import com.certificateManage.common.BaseContext; import com.certificateManage.common.R; import com.certificateManage.util.tokenUtil.JwtTokenUtil; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 從請求頭中獲取JWT String token = request.getHeader("Authorization"); if ("OPTIONS".equalsIgnoreCase(request.getMethod())){ System.out.println("OPTIONS請求,放行"); return true; } if (token == null || !token.startsWith("Bearer ")) { // 如果沒有JWT或者格式不正確,返回錯誤 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "token令牌不存在或請求頭格式錯誤"); return false; } // 去除"Bearer "前綴,獲取真正的JWT token = token.substring(7); try { // 驗證JWT R r=JwtTokenUtil.checkToken(token); // JWT驗證成功,繼續(xù)處理請求 if (r.getCode()==1) { // 將用戶ID存儲在ThreadLocal中 BaseContext.setUserId(String.valueOf(r.getData())); return true; }else { // JWT驗證失敗,返回錯誤 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token驗證失敗"); return false; } } catch (Exception e) { // JWT驗證失敗,返回錯誤 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token驗證失敗"); return false; } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 在請求處理完畢后清除用戶ID BaseContext.clearUserId(); } }
這樣就可以在每次請求驗證Token,并把token中存儲的用戶id存到當前線程.方便我們對發(fā)送請求的用戶進行操作
總結
到此這篇關于SpringBoot整合JWT(JSON Web Token)生成token與驗證的文章就介紹到這了,更多相關SpringBoot整合JWT生成token與驗證內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java實現(xiàn)根據(jù)地址智能識別省市區(qū)縣
這篇文章主要為大家詳細介紹了如何編寫一個Java工具類,可以根據(jù)身份證地址或用戶輸入的地址,智能識別并提取出詳細的省市區(qū)縣信息,感興趣的小伙伴可以了解下2025-03-03java 字符串轉化為字符數(shù)組的3種實現(xiàn)案例
這篇文章主要介紹了java 字符串轉化為字符數(shù)組的3種實現(xiàn)案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10Spring?Boot?+?Spring?Batch?實現(xiàn)批處理任務的詳細教程
這篇文章主要介紹了Spring?Boot+Spring?Batch實現(xiàn)批處理任務,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08Java異常中toString()和getMessage()區(qū)別
在java異常體系中,要打印異常信息,可以通過:e.getMessage() 、 e.toString() e.printStackTrace() 等方法打印,本文主要介紹了Java異常中toString()和getMessage()區(qū)別,具有一定的參考價值,感興趣的可以了解一下2024-01-01Java Swing JSlider滑塊的實現(xiàn)示例
這篇文章主要介紹了Java Swing JSlider滑塊的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12