Spring Boot詳解整合JWT教程
1、概述
JWT 簡稱 JSON Web Token,也就是通過JSON形式作為Web應(yīng)用中的令牌,用于各方之間安全地將信息作為JSON對象傳輸,在數(shù)據(jù)傳輸?shù)倪^程中還可以完成數(shù)據(jù)加密、簽名等相關(guān)處理。

2、優(yōu)勢所在
在JavaWeb階段,經(jīng)常使用session來存儲,以方便用來判斷用戶是否操作等等。但這又恰巧暴露了一個問題,用session存儲是需要占用服務(wù)器內(nèi)存的。當(dāng)用戶只有一兩個的時候沒什么問題,但是當(dāng)用戶成千上萬的話,服務(wù)器就很難招架得住了。并且session是基于cookie來實現(xiàn)的,因此數(shù)據(jù)很容易被截獲,遭遇CSRF跨域偽造攻擊,因此不太安全。這時JWT的優(yōu)勢就突顯出來的:
- 簡潔(Compact): 可以通過URL,POST參數(shù)或者在HTTP header發(fā)送,因為數(shù)據(jù)量小,傳輸速度也很快
- 自包含(Self-contained):負(fù)載中包含了所有用戶所需要的信息,避免了多次查詢數(shù)據(jù)庫
- 因為Token是以JSON加密的形式保存在客戶端的,所以JWT是跨語言的,原則上任何web形式都支持。
- 不需要在服務(wù)端保存會話信息,特別適用于分布式微服務(wù)。
3、結(jié)構(gòu)組成
JWT 中的 token 組成主要有三部分:
- 標(biāo)頭(Header)
- 有效載荷(Payload)
- 簽名(Signature)
3.1、標(biāo)頭(Header)
標(biāo)頭通常由兩部分組成:令牌的類型 ( 即JWT ) 和所使用的簽名算法,例如HMAC SHA256(默認(rèn))或RSA。它會使用Base64編碼組成JWT結(jié)構(gòu)的第一部分。值得注意的是,Base64是一種編碼,也就是說,它是可以被翻譯回原來的樣子的,它并不是一種加密過程。
{
"alg": "HS256",
"typ": "JWT"
}
3.2、有效負(fù)載(Payload)
有效負(fù)荷是包含聲明,通常將需要傳遞的數(shù)據(jù)存放在Payload中,同樣的是使用Base64編碼,但這同時說明這一塊是可以被反編譯的,這是什么意思呢?就是太敏感的信息存放在這里的話存在被捕獲的可能,因此官方推薦在Payload中不要存放敏感的信息,例如女生的年齡。
3.3、簽名(Signature)
簽名是由編碼后的標(biāo)頭和有效負(fù)荷以及我們提供的一個密鑰,然后使用標(biāo)頭規(guī)定的簽名算法進(jìn)行簽名。因此,簽名的主要作用是保證JWT沒有被篡改過,保證 token 的合法性。
這里特別注意的是,密鑰 secret 是驗證 token 是否合法的重要憑證,如果用于驗證的密鑰被外界所獲取到的話,我們所建立的驗證防線將如同虛設(shè)!
4、Spring boot整合JWT 導(dǎo)入依賴
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.0</version>
</dependency>
編寫JwtUtils,提高代碼的重用率
public class JwtUtils {
/** 過期時間,單位:秒,默認(rèn)半小時過期 **/
private static final long EXPIRATION = 60 * 30L;
/** 密鑰,一般長度較長,內(nèi)容較復(fù)雜 **/
private static final String SECRET = "my_secret";
/**
* @description 創(chuàng)建token
* @author xBaozi
* @date 20:49 2022/3/31
**/
public static String createToken(Map<String, String> claimMap) {
// 當(dāng)前時間戳加上設(shè)定的毫秒數(shù)(1秒 == 1000毫秒)
Date expiration = new Date(System.currentTimeMillis() + EXPIRATION * 1000);
// 設(shè)置JWT頭部
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
// 創(chuàng)建token
JWTCreator.Builder builder = JWT.create();
//使用Lambda創(chuàng)建payload
claimMap.forEach((k,v)->{
builder.withClaim(k,v);
});
// 添加頭部,可省略保持默認(rèn),默認(rèn)即map中的鍵值對
return builder.withHeader(map)
// 設(shè)置過期時間
.withExpiresAt(expiration)
// 設(shè)置簽名解碼算法
.sign(Algorithm.HMAC256(SECRET));
}
/**
* @description 驗證token
* @author xBaozi
* @date 23:36 2022/3/31
**/
public static DecodedJWT verifyToken(String token) {
return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
}
}編寫過濾器,這是為了使得不用在每一個controller前端控制器都寫一遍獲取token,將token放在請求體中請求,從而簡化開發(fā)操作
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Map<String, Object> map = new HashMap<>();
//獲取請求頭中令牌
String token = request.getHeader("token");
try {
//驗證令牌
JwtUtils.verifyToken(token);
//驗證成功,放行請求
return true;
} catch (SignatureVerificationException e) {
e.printStackTrace();
map.put("msg", "無效簽名!");
} catch (TokenExpiredException e) {
e.printStackTrace();
map.put("msg", "token過期!");
} catch (AlgorithmMismatchException e) {
e.printStackTrace();
map.put("msg", "token算法不一致!");
} catch (Exception e) {
e.printStackTrace();
map.put("msg", "token無效!!");
}
//設(shè)置狀態(tài)
map.put("state", false);
//將map轉(zhuǎn)為json
String json = new ObjectMapper().writeValueAsString(map);
// 相應(yīng)json數(shù)據(jù)
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}配置過濾器
@Configuration
public class JwtConfig implements WebMvcConfigurer{
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JwtInterceptor())
//添加攔截路徑
.addPathPatterns("/**")
//添加放行路徑
.excludePathPatterns("/user/login");
}
}編寫前端控制器,這里以兩個為例
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
// 這里只做演示,就不寫Service層了
// @Autowired
// private UserService userService;
/**
* @description 登錄功能
* @author xBaozi
* @date 0:02 2022/4/1
* @param user user對象,默認(rèn)有值
**/
@GetMapping("/login")
public Map<String,Object> login(User user){
log.info("用戶名: [{}]",user.getName());
log.info("密碼: [{}]",user.getPassword());
Map<String, Object> map = new HashMap<>();
try{
Map<String,String> payload = new HashMap<>();
payload.put("id",user.getId());
payload.put("name",user.getName());
// 生成JWT的令牌
String token = JwtUtils.createToken(payload);
map.put("state",true);
map.put("msg","認(rèn)證成功");
// 響應(yīng)token
map.put("token",token);
}catch (Exception e){
map.put("state",false);
map.put("msg",e.getMessage());
}
return map;
}
@PostMapping("/other")
public Map<String,Object> test(HttpServletRequest request){
Map<String, Object> map = new HashMap<>();
//處理自己業(yè)務(wù)邏輯
String token = request.getHeader("token");
DecodedJWT verify = JwtUtils.verifyToken(token);
log.info("用戶id: [{}]",verify.getClaim("id").asString());
log.info("用戶name: [{}]",verify.getClaim("name").asString());
map.put("state",true);
map.put("msg","請求成功!");
return map;
}
}postman測試


IDEA控制臺查看信息

搞完收工,這里的token交由前端進(jìn)行管理保存?。?!溜了~
到此這篇關(guān)于Spring Boot詳解整合JWT教程的文章就介紹到這了,更多相關(guān)Spring Boot JWT內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot實現(xiàn)自定義指標(biāo)監(jiān)控功能
本文主要介紹了SpringBoot實現(xiàn)自定義指標(biāo)監(jiān)控功能的實現(xiàn),,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,感興趣的小伙伴跟著著小編來一起來學(xué)習(xí)吧2024-01-01
java應(yīng)用開發(fā)之JVM運行時內(nèi)存分析
這篇文章主要介紹了java應(yīng)用開發(fā)之JVM運行時內(nèi)存,文中附含圖文示例內(nèi)容分析非常簡要,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-09-09
Mybatis中l(wèi)ike搭配concat的寫法詳解
這篇文章主要介紹了Mybatis中l(wèi)ike搭配concat的寫法詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
解決mybatis三表連接查詢數(shù)據(jù)重復(fù)的問題
這篇文章主要介紹了解決mybatis三表連接查詢數(shù)據(jù)重復(fù)的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01
Spring Cloud Gateway全局異常處理的方法詳解
這篇文章主要給大家介紹了關(guān)于Spring Cloud Gateway全局異常處理的相關(guān)資料,需要的朋友可以參考下2018-10-10

