JWT全面解讀和詳細(xì)使用步驟
前言
定義:JSON Web Token(縮寫 JWT)是目前最流行的跨域認(rèn)證解決方案
由于HTTP協(xié)議是無狀態(tài)的,這意味著如果我們想判定一個(gè)接口是否被認(rèn)證后訪問,就需要借助cookie或者session會(huì)話機(jī)制進(jìn)行判定,但是由于現(xiàn)在的系統(tǒng)架構(gòu)大部分都不止一臺(tái)服務(wù)器,此時(shí)又要借助數(shù)據(jù)庫或者全局緩存 做存儲(chǔ),這種方案顯然受限太多。
那么我們可不可以讓認(rèn)證令牌的發(fā)布者自己去識(shí)別這個(gè)令牌是不是我曾經(jīng)發(fā)布的令牌呢(JWT核心思想),這是JWT最大的優(yōu)點(diǎn)也是最大的缺點(diǎn),優(yōu)點(diǎn)是簡單快捷、不需要依賴任何第三方操作就能實(shí)現(xiàn)身份認(rèn)證,缺點(diǎn)就是對(duì)于任何擁有用戶發(fā)布令牌的請(qǐng)求都會(huì)認(rèn)證通過。
JWT的數(shù)據(jù)結(jié)構(gòu)
正常的JWT數(shù)據(jù)結(jié)構(gòu)應(yīng)該如下
它是一個(gè)很長的字符串,中間用點(diǎn)(.)分隔成三個(gè)部分
JWT的三個(gè)部分依次: Header - 頭部 、Payload - 負(fù)載 、Signature(簽名)
即:Header.Payload.Signature
Header
Header 部分是一個(gè) JSON 對(duì)象,描述 JWT 的元數(shù)據(jù),通常是下面的樣子。
{ "alg": "HS256", "typ": "JWT" }
alg
屬性表示簽名的算法(algorithm),默認(rèn)是 HMAC SHA256(寫成 HS256);typ
屬性表示這個(gè)令牌(token)的類型(type),JWT 令牌統(tǒng)一寫為JWT
Payload
Payload 部分也是一個(gè) JSON 對(duì)象,用來存放實(shí)際需要傳遞的數(shù)據(jù)。JWT 規(guī)定了7個(gè)官方字段,供選用。
- iss (issuer):簽發(fā)人
- exp (expiration time):過期時(shí)間
- sub (subject):主題
- aud (audience):受眾
- nbf (Not Before):生效時(shí)間
- iat (Issued At):簽發(fā)時(shí)間
- jti (JWT ID):編號(hào)
除了官方字段,你還可以在這個(gè)部分定義私有字段
{ "sub": "1234567890", "name": "John Doe", "age": "19" }
注意:JWT默認(rèn)是明文展示,任何人都可以讀取到,所以此處不要放私密信息
這個(gè) JSON 對(duì)象也要使用 Base64URL 算法轉(zhuǎn)成字符串。
Signature
Signature 部分是對(duì)前兩部分的簽名,防止數(shù)據(jù)篡改。
首先,需要指定一個(gè)密鑰(secret)。這個(gè)密鑰只有服務(wù)器才知道,不能泄露給用戶。然后,使用 Header 里面指定的簽名算法(默認(rèn)是 HMAC SHA256),按照下面的公式產(chǎn)生簽名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出簽名以后,把 Header、Payload、Signature 三個(gè)部分拼成一個(gè)字符串,每個(gè)部分之間用"點(diǎn)"(.)分隔,就可以返回給用戶。
Base64URL
前面提到,Header 和 Payload 串型化的算法是 Base64URL。這個(gè)算法跟 Base64 算法基本類似,但有一些小的不同。
JWT 作為一個(gè)令牌(token),有些場合可能會(huì)放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個(gè)字符+、/和=,在 URL 里面有特殊含義,所以要被替換掉:=被省略、+替換成-,/替換成_ 。這就是 Base64URL 算法
JWT的實(shí)現(xiàn)
Maven依賴
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.5.0</version> </dependency>
JWT簽名發(fā)布和驗(yàn)證代碼
public class TokenUtil { //Token的過期時(shí)間 private static final long EXPIRE_TIME = 30 * 60 * 1000; //Token的私鑰 private static final String TOKEN_SECRET = "jytoken_secret"; /** * 生成簽名,30分鐘過期 * @param **userInfo** 用戶信息 用戶姓名 * @param **other** 用戶其他信息 用戶id * @return */ public static String sign(String userInfo, String other) { try { // 設(shè)置過期時(shí)間 Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); //私鑰和加密算法 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); // 設(shè)置頭部信息 Map<String, Object> header = new HashMap<>(2); header.put("Type", "Jwt"); header.put("alg", "HS256"); // 返回token字符串 return JWT.create() .withHeader(header) .withClaim("userInfo", userInfo) .withClaim("other", other) .withExpiresAt(date) .sign(algorithm); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 生成簽名,30分鐘過期 * @param **userInfo** 用戶信息 用戶姓名 * @param **other** 用戶其他信息 用戶id * @return */ public static String sign(String userInfo, String other,long expire) { try { // 設(shè)置過期時(shí)間 Date date = new Date(System.currentTimeMillis() + expire); //私鑰和加密算法 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); // 設(shè)置頭部信息 Map<String, Object> header = new HashMap<>(2); header.put("Type", "Jwt"); header.put("alg", "HS256"); // 返回token字符串 return JWT.create() .withHeader(header) .withClaim("userInfo", userInfo) .withClaim("other", other) .withExpiresAt(date) .sign(algorithm); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 檢驗(yàn)token是否正確 * @param **token** * @return */ public static boolean verify(String token){ try { Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); JWTVerifier verifier = JWT.require(algorithm).build(); verifier.verify(token);//未驗(yàn)證通過會(huì)拋出異常 return true; } catch (Exception e){ return false; } } /** * 從token中獲取info信息 * @param **token** * @return */ public static String getUserName(String token,String info){ try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim(info).asString(); } catch (JWTDecodeException e){ e.printStackTrace(); } return null; } }
攔截器配置無需認(rèn)證的請(qǐng)求
@Configuration public class InterceptorConfig extends WebMvcConfigurationSupport { @Autowired private TokenHandler tokenHandler; @Override public void addInterceptors(InterceptorRegistry registry) { List<String> excludePath = new ArrayList<>(); String checkLogin = "/pushlogin/checkIsCanLogin"; String login = "/pushlogin/login"; String getVerifyCode = "/common/send"; String verfifyMethod = "/common/validationCode"; excludePath.add(checkLogin); excludePath.add(login); excludePath.add(getVerifyCode); excludePath.add(verfifyMethod); registry.addInterceptor(tokenHandler).excludePathPatterns(excludePath); } }
Token統(tǒng)一攔截器代碼
@Component @Slf4j public class TokenHandler implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authentication"); if (token != null){ boolean result = TokenUtil.verify(token); if(result){ log.info("通過攔截器"); return true; } } log.info("認(rèn)證失敗"); return false; } }
用戶登錄時(shí)驗(yàn)證用戶信息后,返回Token信息
@Override public UserDTO selectIsExistUserInfo(String phone) { //TODO 偽代碼 驗(yàn)證用戶信息 UserDTO info = 查詢用戶信息 if (info != null) { String token = TokenUtil.sign(info.getUsername(), info.getUserId(), 6 * 60 * 60 * 1000); info.setToken(token); } return info; }
到此這篇關(guān)于JWT全面解讀和詳細(xì)使用步驟的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java代碼如何判斷l(xiāng)inux系統(tǒng)windows系統(tǒng)
這篇文章主要介紹了Java代碼如何判斷l(xiāng)inux系統(tǒng)windows系統(tǒng)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01Spring中的AutowireCandidateResolver的具體使用詳解
這篇文章主要介紹了Spring中的AutowireCandidateResolver的具體使用詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04Logger.getLogger()與LogFactory.getLog()的區(qū)別詳解
LogFactory來自common-logging包。如果用LogFactory.getLog,你可以用任何實(shí)現(xiàn)了通用日志接口的日志記錄器替換log4j,而程序不受影響2013-09-09如何修改json字符串中某個(gè)key對(duì)應(yīng)的value值
這篇文章主要介紹了如何修改json字符串中某個(gè)key對(duì)應(yīng)的value值操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-11-11詳解Java如何應(yīng)對(duì)常見的安全威脅和攻擊類型
隨著信息技術(shù)的快速發(fā)展,網(wǎng)絡(luò)安全問題日益突出,本文將以Java開發(fā)語言為例,深入探討網(wǎng)絡(luò)協(xié)議的安全性問題,通過分析常見的安全威脅和攻擊類型,設(shè)計(jì)和實(shí)施安全協(xié)議等主題,為讀者提供一些有益的思路和方法,需要的朋友可以參考下2023-11-11Idea設(shè)置全局highlighting?level為Syntax問題
這篇文章主要介紹了Idea設(shè)置全局highlighting?level為Syntax問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04