Spring?Boot?集成JWT實現前后端認證的示例代碼
前言
小程序、H5應用的快速發(fā)展,使得前后端分離已經成為了趨勢,然而系統(tǒng)認證卻是系統(tǒng)的重要一部分,本文將講解JWT如何實現前后端認證。
JWT簡介
JWT(全稱:Json Web Token)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用于作為JSON對象在各方之間安全地傳輸信息。
為什么要用JWT
傳統(tǒng)session認證存在那些弊端?
每個用戶的登錄信息都會保存到服務器的Session中,隨著用戶的增多,服務器開銷會明顯增大。
Session的信息存放在服務器的內存中,對于分布式應用會導致失效,雖然可以將session的信息統(tǒng)一存放在Redis的緩存中,但這樣可能增加了復雜性。
由于Session認證是基于Cookie實現,而針對于非瀏覽器端和手機的移動端都不適用。
前后端分離系統(tǒng),由于前后端存在跨域,而Cookie信息無法跨越,所以采用Session認證也是無法繼續(xù)寧跨域認證。
JWT認證的優(yōu)勢
簡潔:JWT Token數據量小,傳輸速度也很快。
跨語言: JWT Token是以JSON加密形式保存在客戶端的,所以JWT是跨語言的,任何web形式都支持。 跨平臺:不依賴于cookie和session,無需將session信息存放在服務端,非常適合于分布式應用,應用于擴展。
JWT的數據結構

Header
JWT第一部分是頭部分,它是一個描述JWT元數據的Json對象,通常如下所示。
{
"alg": "HS256",
"typ": "JWT"
}alg屬性表示簽名使用的算法,默認為HMAC SHA256(寫為HS256),typ屬性表示令牌的類型,JWT令牌統(tǒng)一寫為JWT。
Payload
JWT第二部分是Payload,也是一個Json對象,除了包含需要傳遞的數據,還有七個默認的字段供選擇。 iss:發(fā)行人 exp:到期時間 sub:主題 aud:用戶 nbf:在此之前不可用 iat:發(fā)布時間 jti:JWT ID用于標識該JWT
{
//默認字段
"sub":"主題123",
//自定義字段
"name":"java",
"isAdmin":"true",
"loginTime":"2021-12-05 12:00:03"
}需要注意的是,默認情況下JWT是未加密的,任何人都可以解讀其內容,因此如果一些敏感信息不要存放在此,以防信息泄露。JSON對象也使用Base64 URL算法轉換為字符串保存。
Signature
簽名哈希部分是對上面兩部分數據簽名,需要使用base64編碼后的header和payload數據,通過指定的算法生成哈希,以確保數據不會被篡改。
Spring Boot集成JWT
引入Jwt包
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
編寫jwt工具類
public class JwtUtil
{
//創(chuàng)建jwt
public static String createJWT(String subject, String issue, Object claim,
long ttlMillis)
{
//當前時間
long nowMillis = System.currentTimeMillis();
//過期時間
long expireMillis = nowMillis + ttlMillis;
String result = Jwts.builder()
.setSubject(subject) //設置主題
.setIssuer(issue) //發(fā)行者
.setId(issue)//jwtID
.setExpiration(new Date(expireMillis)) //設置過期日期
.claim("user", claim)//主題,可以包含用戶信息
.signWith(getSignatureAlgorithm(), getSignedKey())//加密算法
.compressWith(CompressionCodecs.DEFLATE).compact();//對載荷進行壓縮
return result;
}
// 解析jwt
public static Jws<Claims> pareseJWT(String jwt)
{
Jws<Claims> claims;
try
{
claims = Jwts.parser().setSigningKey(getSignedKey())
.parseClaimsJws(jwt);
}
catch (Exception ex)
{
claims = null;
}
return claims;
}
//獲取主題信息
public static Claims getClaims(String jwt)
{
Claims claims;
try
{
claims = Jwts.parser().setSigningKey(getSignedKey())
.parseClaimsJws(jwt).getBody();
}
catch (Exception ex)
{
claims = null;
}
return claims;
}
}
/**
* 獲取密鑰
*
* @return Key
*/
private static Key getSignedKey()
{
byte[] apiKeySecretBytes = DatatypeConverter
.parseBase64Binary(getAuthKey());
Key signingKey = new SecretKeySpec(apiKeySecretBytes,
getSignatureAlgorithm().getJcaName());
return signingKey;
}
private static SignatureAlgorithm getSignatureAlgorithm()
{
return SignatureAlgorithm.HS256;
}
//獲取密鑰,可以動態(tài)配置
public static String getAuthKey()
{
String auth = "123@#1234";
}Token認證攔截器
Component
public class TokenInterceptor extends HandlerInterceptorAdapter
{
public static Log logger = LogManager.getLogger(TokenInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception
{
String uri = request.getRequestURI();
logger.info("start TokenInterceptor preHandle.." + uri);
//需要過濾特殊請求
if (SystemUtil.isFree(uri) || SystemUtil.isProtected(uri))
{
return true;
}
String metohd=request.getMethod().toString();
logger.info("TokenInterceptor request method:"+metohd);
//options 方法需要過濾
if("OPTIONS".equals(metohd))
{
return true;
}
//是否開啟token認證
boolean flag = SystemUtil.getVerifyToken();
ResponseResult result = new ResponseResult();
//從請求的head信息中獲取token
String token = request.getHeader("X-Token");
if (flag)
{
if(StringUtils.isEmpty(token))
{
token=request.getParameter("X-Token");
}
// token不存在
if (StringUtils.isEmpty(token))
{
result.setCode(ResultCode.NEED_AUTH.getCode());
result.setMsg(ResultCode.NEED_AUTH.getMsg());
WebUtil.writeJson(result, response);
return false;
}
else
{
Claims claims = JwtUtil.getClaims(token);
String subject = "";
if (claims != null)
{
subject = claims.getSubject();
// 驗證主題
if (StringUtils.isEmpty(subject))
{
result.setCode(ResultCode.INVALID_TOKEN.getCode());
result.setMsg(ResultCode.INVALID_TOKEN.getMsg());
WebUtil.writeJson(result, response);
return false;
}
}
else
{
result.setCode(ResultCode.INVALID_TOKEN.getCode());
result.setMsg(ResultCode.INVALID_TOKEN.getMsg());
WebUtil.writeJson(result, response);
return false;
}
}
}
return true;
}
}配置攔擊器
@Configuration
public class WebConfig implements WebMvcConfigurer
{
@Resource
private TokenInterceptor tokenInterceptor;
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(tokenInterceptor).addPathPatterns("/**");
}
}登錄驗證流程

示例代碼
@RequestMapping("login")
public Result login(HttpServletResponse response)
{
Map<String, Object> map = new HashMap<>();
//
Result result = loginAuth(user);
int code = result.getCode();
//登錄認證成功
if (code ==ResultCode.SUCCESS)
{
//默認為7天
Long ttlMillis = 7*1000 * 60 * 60 * 24;
//過期時間
long expreTime = System.currentTimeMillis() + ttlMillis;
String tokenKey = UUID.randomUUID().toString();
String tokenId = JwtUtil.createJWT(user.getUserId(), tokenKey,
user.getPassword(), expreTime);
map.put("expreTime", expreTime);
map.put("tokenId", tokenId);
}
else
{
logger.error("login error:" +FastJsonUtil.toJSONString(result));
}
return result;
}總結
時代在進步,技術也在不斷更新,以前采用Session+Redis實現單點登錄,現在可以替換為jwt+Redis實現,我們只有不斷的更新自己的技術棧,才能避免被無情的淘汰,關于使用注解方式實現和token刷新,將在后續(xù)文章中進行講解。
到此這篇關于Spring Boot 集成JWT實現前后端認證的示例代碼的文章就介紹到這了,更多相關SpringBoot JWT前后端認證內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- 基于Springboot實現JWT認證的示例代碼
- SpringBoot+Vue+JWT的前后端分離登錄認證詳細步驟
- SpringBoot集成Spring security JWT實現接口權限認證
- Springboot WebFlux集成Spring Security實現JWT認證的示例
- Springboot集成Spring Security實現JWT認證的步驟詳解
- 利用Springboot實現Jwt認證的示例代碼
- SpringBoot使用Jwt處理跨域認證問題的教程詳解
- Springboot+SpringSecurity+JWT實現用戶登錄和權限認證示例
- springboot+jwt實現token登陸權限認證的實現
- Vue+Jwt+SpringBoot+Ldap完成登錄認證的示例代碼
相關文章
spring boot基于DRUID實現數據源監(jiān)控過程解析
這篇文章主要介紹了spring boot基于DRUID實現數據源監(jiān)控過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-12-12
SpringBoot 整合 Shiro 密碼登錄與郵件驗證碼登錄功能(多 Realm 認證)
這篇文章主要介紹了SpringBoot 整合 Shiro 密碼登錄與郵件驗證碼登錄(多 Realm 認證),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02
java javax.annotation.Resource注解的詳解
這篇文章主要介紹了javax.annotation.Resource注解的詳解的相關資料,需要的朋友可以參考下2016-10-10
解決weblogic部署springboot項目步驟及可能會出現的問題
這篇文章主要介紹了解決weblogic部署springboot項目步驟及可能會出現的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07

