SpringBoot整合JWT的入門指南
1.JWT
JWT(JSON Web Token),為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標(biāo)準(zhǔn)。將用戶信息加密到token中,服務(wù)器不保存任何用戶信息,服務(wù)器通過保存的密鑰驗證token的正確性。
優(yōu)點
1.簡介:可以通過 URL POST 參數(shù)或者在 HTTP header 發(fā)送,因為數(shù)據(jù)量小,傳輸速度也很快;
2.自包含:負(fù)載中可以包含用戶所需要的信息,避免了多次查詢數(shù)據(jù)庫;
3.因為 Token 是以 JSON 加密的形式保存在客戶端的,所以 JWT 是跨語言的,原則上任何 web 形式都支持;
4.不需要再服務(wù)端保存會話信息,特別適用于分布式微服務(wù);
缺點
1.無法作廢已經(jīng)發(fā)布的令牌;
2.不易應(yīng)對數(shù)據(jù)過期;
2.JWT登錄執(zhí)行流程圖

3.為什么使用JWT?
- 在傳統(tǒng)的用戶登錄認(rèn)證中,因為http是無狀態(tài)的,所以都是采用session+cokokie,用戶登錄成功,服務(wù)端會保存一個session,并給客戶端一個sessionId,客戶端會把sessionId保存在cookie中,每次請求都會攜帶sessionId。cookie+session模式是保存在內(nèi)存中,在分布式服務(wù)中會面臨session共享問題,隨著用戶量得增加,開銷就越來越大。而JWT不需要在服務(wù)端保存會話信息,特別適合分布式微服務(wù)。
- 簡潔:可以用過URL,POST參數(shù)或者在HTTP Header發(fā)送,因為數(shù)據(jù)量小,傳輸速度也快
- JWT支持跨語言
- 自包含: 載荷中包含了所有用戶所需要的信息,避免了多次查詢數(shù)據(jù)庫。
4.JWT的組成
JWT由三部分構(gòu)成,類似于如下:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJscyIsImV4cCI6MTYyNDU5Nzc5Nn0.4kwT1elZCb_k2D7AxbuFHM35VmBK4PcmLaqHhcHEq4_wVe8GVO8ODypGSKksTs-hraBopBCm2IC9EC2rO-GHng
第一部分為頭部(header),承載兩部分信息
聲明類型(JWT)
聲明加密算法,默認(rèn)為HMAC SHA256
{
"typ","JWT",
"alg","HS256"
}
使用Base64加密后
第二部分為payload(載荷),存放有效信息的地方,這些有效信息分為三部分:
- 標(biāo)準(zhǔn)中注冊的聲明
- 公共的聲明
- 私有的聲明
其中,標(biāo)準(zhǔn)中注冊的聲明 (建議但不強(qiáng)制使用)包括如下幾個部分 :
- iss: jwt簽發(fā)者;
- sub: jwt所面向的用戶;
- aud: 接收jwt的一方;
- exp: jwt的過期時間,這個過期時間必須要大于簽發(fā)時間;
- nbf: 定義在什么時間之前,該jwt都是不可用的;
- iat: jwt的簽發(fā)時間;
- jwt的唯一身份標(biāo)識,主要用來作為一次性token,從而回避重放攻擊
公共的聲明部分:
公共的聲明可以添加任何信息,一般添加用戶的相關(guān)信息或其他業(yè)務(wù)需要的必要信息,但不建議添加銘感信息,因為在客戶端可解密。
私有的聲明部分:
私有的聲明是提供者和消費者共同定義的聲明,不建議存放敏感信息,因為base64是對稱解密的,意味著該部分信息可以歸類為明文信息。
第三部分為signature(簽證)
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); //密鑰就是我們定義的secret,加密后得到簽證 var signature = HMACSHA256(encodedString, '密鑰');
5.SpringBoot整合JWT
引入依賴
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<!--StringUtils工具包-->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!--ConfigurationProperties出現(xiàn)異常-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
配置application.yml文件
server:
port: 8888
spring:
jwt:
#過期時間
expireTime: 1800000
#加密密鑰
secret: lsyyp5201314
#token返回頭部
header: LOGINTOKEN
JWT工具類
package org.best.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Date;
@ConfigurationProperties(prefix = "spring.jwt")
public class JWTUtils {
public static String header;
private static String secret;
private static String expireTime;
/**
* 生成token
* @param sub 用戶唯一信息
* @return token
*/
public static String createToken(String sub){
return JWT
.create()
.withSubject(sub)
.withExpiresAt(new Date(System.currentTimeMillis()+Long.parseLong(expireTime)))
.sign(Algorithm.HMAC512(secret));
}
/**
* 根據(jù)token獲取用戶信息
* @param token
* @return 用戶信息
*/
public static String validateToken(String token){
return JWT
.require(Algorithm.HMAC512(secret))
.build()
.verify(token)
.getSubject();
}
/**
* 檢驗Token是否需要刷新
* @param token
* @return
*/
public static boolean refreshToken(String token) {
Date expireDate = null;
try {
expireDate = JWT
.require(Algorithm.HMAC512(secret))
.build()
.verify(token)
.getExpiresAt();
} catch (TokenExpiredException e) {
return true;
}
return (expireDate.getTime()-System.currentTimeMillis())<0;
}
public void setHeader(String header) {
this.header = header;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getExpireTime() {
return expireTime;
}
public void setExpireTime(String expireTime) {
this.expireTime = expireTime;
}
}
自定義攔截器
package org.best.config;
import org.apache.commons.lang.StringUtils;
import org.best.common.TokenIsNullException;
import org.best.common.TokenValidateException;
import org.best.util.JWTUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String logintoken = request.getHeader("LOGINTOKEN");
//如果token為空
if (StringUtils.isBlank(logintoken)){
throw new TokenIsNullException("請登錄");
}
//校驗Token
String sub = JWTUtils.validateToken(logintoken);
if (StringUtils.isBlank(sub)){
throw new TokenValidateException("token校驗失敗");
}
//更新token有效期(生產(chǎn)新token)
if (JWTUtils.refreshToken(logintoken)){
String token = JWTUtils.createToken(sub);
response.setHeader(JWTUtils.header,token);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
注冊攔截器
package org.best.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry
.addInterceptor(new LoginInterceptor())
.addPathPatterns("/find")
.excludePathPatterns("/login");
}
}
自定義異常
package org.best.common;
/**
* Token校驗異常
*/
public class TokenValidateException extends RuntimeException {
public TokenValidateException() {
}
public TokenValidateException(String message) {
super(message);
}
}
package org.best.common;
/**
* Token為空異常
*/
public class TokenIsNullException extends RuntimeException{
public TokenIsNullException() {
}
public TokenIsNullException(String message) {
super(message);
}
}
自定義Controller
package org.best.controller;
import org.best.pojo.User;
import org.best.util.JWTUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
@RestController
public class LoginController {
@GetMapping("/login")
public String login(User user, HttpServletResponse response){
//這里就不做數(shù)據(jù)庫查詢了
//根據(jù)用戶id生成token
String token = JWTUtils.createToken(String.valueOf(user.getId()));
//將token存入HTTP響應(yīng)頭中
response.setHeader(JWTUtils.header,token);
return user.toString();
}
@GetMapping("/find")
public String find(){
return "success";
}
}
測試
登錄接口

我們來測試下find 接口 ,不帶token會出現(xiàn)啥情況

帶上token

總結(jié)
到此這篇關(guān)于SpringBoot整合JWT的文章就介紹到這了,更多相關(guān)SpringBoot整合JWT內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis-config.xml文件中的mappers標(biāo)簽使用
在MyBatis配置中,<mapper>標(biāo)簽關(guān)鍵用于指定SQL?Mapper的XML文件路徑,主要有三種指定方式:resource、url和class,Resource方式從類的根路徑開始,適合放在項目內(nèi)部保障移植性,URL方式指定絕對路徑,移植性差,適用于外部路徑2024-10-10
Java為何需要平衡方法調(diào)用與內(nèi)聯(lián)
這篇文章主要介紹了Java為何需要平衡方法調(diào)用與內(nèi)聯(lián),幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2021-01-01
IntelliJ IDEA 2020 安裝和常用配置(推薦)
這篇文章主要介紹了IntelliJ IDEA 2020 安裝和常用配置(推薦),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
SpringBoot中REST API 接口傳參的實現(xiàn)
我們在開發(fā)?REST API?的過程中,經(jīng)常需要傳遞參數(shù),本文主要介紹了SpringBoot中REST API 接口傳參的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下2023-12-12
springsecurity實現(xiàn)登錄驗證以及根據(jù)用戶身份跳轉(zhuǎn)不同頁面
Spring?Security是一種基于Spring框架的安全技術(shù),用于實現(xiàn)身份驗證和訪問控制,本文介紹了如何使用Spring?Security,結(jié)合session和redis來存儲用戶信息,并通過編寫特定的登錄處理類和Web配置,實現(xiàn)用戶登錄和注銷功能2024-09-09
Java并發(fā)編程示例(六):等待線程執(zhí)行終止
這篇文章主要介紹了Java并發(fā)編程示例(六):等待線程執(zhí)行終止,在本節(jié),示例程序演示等待初始化方法完成后,再去執(zhí)行其他任務(wù),需要的朋友可以參考下2014-12-12

