系統(tǒng)開發(fā)來講,安全驗證永遠是最重要的沉删,從最原始的session渐尿、cookie驗證方式,到符合restful風(fēng)格矾瑰、滿足前后端分離需求砖茸、啟用https請求,各方面都在不斷變化中殴穴。
概念
JWT是一種用于雙方之間傳遞安全信息的簡潔的凉夯、URL安全的表述性聲明規(guī)范。JWT作為一個開放的標準( RFC 7519 )采幌,定義了一種簡潔的劲够,自包含的方法用于通信雙方之間以Json對象的形式安全的傳遞信息。因為數(shù)字簽名的存在休傍,這些信息是可信的征绎,JWT可以使用HMAC算法或者是RSA的公私秘鑰對進行簽名。
簡潔(Compact): 可以通過URL磨取,POST參數(shù)或者在HTTP header發(fā)送人柿,因為數(shù)據(jù)量小柴墩,傳輸速度也很快
自包含(Self-contained):負載中包含了所有用戶所需要的信息,避免了多次查詢數(shù)據(jù)庫
JWT的主要應(yīng)用場景
身份認證
在這種場景下凫岖,一旦用戶完成了登陸拐邪,在接下來的每個請求中包含JWT,可以用來驗證用戶身份以及對路由隘截,服務(wù)和資源的訪問權(quán)限進行驗證扎阶。由于它的開銷非常小,可以輕松的在不同域名的系統(tǒng)中傳遞婶芭,所有目前在單點登錄(SSO)中比較廣泛的使用了該技術(shù)东臀。
信息交換
在通信的雙方之間使用JWT對數(shù)據(jù)進行編碼是一種非常安全的方式,由于它的信息是經(jīng)過簽名的犀农,可以確保發(fā)送者發(fā)送的信息是沒有經(jīng)過偽造的惰赋。
JWT的結(jié)構(gòu)
加密后jwt信息如下所示,是由.分割的三部分組成呵哨,分別為Header赁濒、Payload、Signature孟害。
其結(jié)構(gòu)看起來是這樣的
xxxxx.yyyyy.zzzzz
Header
Header包含兩部分信息拒炎,alg指加密類型,可選值為HS256挨务、RSA等等击你,typ=JWT為固定值,表示token的類型谎柄。
{
"alg": "HS256",
"typ": "JWT"
}
Payload
Payload是指簽名信息以及內(nèi)容丁侄,一般包括iss (發(fā)行者), exp (過期時間), sub(用戶信息), aud (接收者),以及其他信息,詳細介紹請參考官網(wǎng)朝巫。
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
Signature
Signature則為對Header鸿摇、Payload的簽名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
如何使用JWT劈猿?
在身份鑒定的實現(xiàn)中拙吉,傳統(tǒng)方法是在服務(wù)端存儲一個session,給客戶端返回一個cookie糙臼,而使用JWT之后庐镐,當用戶使用它的認證信息登陸系統(tǒng)之后恩商,會返回給用戶一個JWT变逃,用戶只需要本地保存該token(通常使用local storage,也可以使用cookie)即可怠堪。
當用戶希望訪問一個受保護的路由或者資源的時候揽乱,通常應(yīng)該在 Authorization 頭部使用 Bearer 模式添加JWT名眉,其內(nèi)容看起來是下面這樣:
Authorization: Bearer <token>
因為用戶的狀態(tài)在服務(wù)端的內(nèi)存中是不存儲的,所以這是一種 無狀態(tài) 的認證機制凰棉。服務(wù)端的保護路由將會檢查請求頭 Authorization 中的JWT信息损拢,如果合法,則允許用戶的行為撒犀。由于JWT是自包含的福压,因此減少了需要查詢數(shù)據(jù)庫的需要。
JWT的這些特性使得我們可以完全依賴其無狀態(tài)的特性提供數(shù)據(jù)API服務(wù)或舞,甚至是創(chuàng)建一個下載流服務(wù)荆姆。因為JWT并不使用Cookie的,所以你可以使用任何域名提供你的API服務(wù)而不需要擔(dān)心跨域資源共享問題(CORS)映凳。
在jwt官網(wǎng)胆筒,可以看到有不同語言的實現(xiàn)版本,這里使用的是java版的jjwt诈豌。話不多說仆救,直接看代碼,加解密都很簡單:
/**
* 創(chuàng)建 jwt
* @param id
* @param subject
* @param ttlMillis
* @return
* @throws Exception
*/
public String createJWT(String id, String subject, long ttlMillis) throws Exception {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256 ;
long nowMillis = System. currentTimeMillis();
Date now = new Date( nowMillis);
SecretKey key = generalKey();
JwtBuilder builder = Jwts. builder()
.setId(id)
.setIssuedAt(now)
.setSubject(subject)
.signWith(signatureAlgorithm, key);
if (ttlMillis >= 0){
long expMillis = nowMillis + ttlMillis;
Date exp = new Date( expMillis);
builder.setExpiration( exp);
}
return builder.compact();
}
/**
* 解密 jwt
* @param jwt
* @return
* @throws Exception
*/
public Claims parseJWT(String jwt) throws Exception{
SecretKey key = generalKey();
Claims claims = Jwts. parser()
.setSigningKey( key)
.parseClaimsJws( jwt).getBody();
return claims;
}
加解密的key是通過固定字符串轉(zhuǎn)換而生成的矫渔;subject為用戶信息的json字符串彤蔽;ttlMillis是指token的有效期,時間較短庙洼,需要定時更新铆惑。
這里要介紹的token刷新方式,是在生成token的同時生成一個有效期較長的refreshToken送膳,后續(xù)由客戶端定時根據(jù)refreshToken來獲取最新的token员魏。瀏覽器與服務(wù)端之間建立sse(server send event)請求,來實現(xiàn)刷新叠聋。
參考資料:
- 1.jwt官方網(wǎng)站:https://jwt.io/
- 2.jjwt項目:https://github.com/jwtk/jjwt
- 3.Introduction to JSON Web Tokens:https://jwt.io/introduction/
- 4.How to Create and verify JWTs in Java: https://stormpath.com/blog/jwt-java-create-verify
個人介紹:
高廣超 :多年一線互聯(lián)網(wǎng)研發(fā)與架構(gòu)設(shè)計經(jīng)驗撕阎,擅長設(shè)計與落地高可用、高性能互聯(lián)網(wǎng)架構(gòu)碌补。目前就職于美團網(wǎng)虏束,負責(zé)核心業(yè)務(wù)研發(fā)工作。