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

