【SSO】JWT協議示例

JSON Web Token是什么

JSON Web Token (JWT)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用于作為JSON對象在各方之間安全地傳輸信息。該信息可以被驗證和信任,因為它是數字簽名的。

什么時候你應該用JSON Web Tokens

下列場景中使用JSON Web Token是很有用的:

Authorization (授權) : 這是使用JWT的最常見場景。一旦用戶登錄,后續每個請求都將包含JWT,允許用戶訪問該令牌允許的路由、服務和資源。單點登錄是現在廣泛使用的JWT的一個特性,因為它的開銷很小,并且可以輕松地跨域使用。

Information Exchange (信息交換) : 對于安全的在各方之間傳輸信息而言,JSON Web Tokens無疑是一種很好的方式。因為JWTs可以被簽名,例如,用公鑰/私鑰對,你可以確定發送人就是它們所說的那個人。另外,由于簽名是使用頭和有效負載計算的,您還可以驗證內容沒有被篡改。

JSON Web Token的結構是什么樣的

JSON Web Token由三部分組成,它們之間用圓點(.)連接。這三部分分別是:

Header
Payload
Signature

因此,一個典型的JWT看起來是這個樣子的:

1
xxxxx.yyyyy.zzzzz

接下來,具體看一下每一部分:

Header

header典型的由兩部分組成:token的類型(“JWT”)和算法名稱(比如:HMAC SHA256或者RSA等等)。

例如:

1
{"typ":"JWT","alg":"HS512"}

然后,用Base64對這個JSON編碼就得到JWT的第一部分

Payload

JWT的第二部分是payload,它包含聲明(要求)。聲明是關于實體(通常是用戶)和其他數據的聲明。聲明有三種類型: registered, public 和 private。

Registered claims : 這里有一組預定義的聲明,它們不是強制的,但是推薦。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。

Public claims : 可以隨意定義。

Private claims : 用于在同意使用它們的各方之間共享信息,并且不是注冊的或公開的聲明。

下面是一個例子:

1
{"sub":"026a564bbfd84861ac4b65393644beef","iat":1558597028,"exp":1559201828}

對payload進行Base64編碼就得到JWT的第二部分

注意,不要在JWT的payload或header中放置敏感信息,除非它們是加密的。

Signature

為了得到簽名部分,你必須有編碼過的header、編碼過的payload、一個秘鑰,簽名算法是header中指定的那個,然對它們簽名即可。

例如:

HMACSHA512(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)

簽名是用于驗證消息在傳遞過程中有沒有被更改,并且,對于使用私鑰簽名的token,它還可以驗證JWT的發送方是否為它所稱的發送方。

登錄示例

登錄過程:

  1. 驗證用戶名和密碼
  2. 生成token
  3. 返回token和過期時間

驗證用戶名密碼要根據業務來。

生成token方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public String generateToken(String userId) {
Date nowDate = new Date();
//過期時間
Date expireDate = new Date(nowDate.getTime() + expire * 1000);

return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(userId)//主題,也差不多是個人的一些信息
.setIssuedAt(nowDate) //創建時間
.setExpiration(expireDate)//添加Token過期時間
//.setAudience(audience) //個人簽名
//.setIssuer(issuer) //發送誰
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}

生成token結果:

1
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIwMjZhNTY0YmJmZDg0ODYxYWM0YjY1MzkzNjQ0YmVlZiIsImlhdCI6MTU1ODU5NzAyOCwiZXhwIjoxNTU5MjAxODI4fQ.ePnFkWzqRYA_XeP1lOsbr_ZKJtXFIIBqofszn3A47t1uyZYvFmhOIJI3r7ziAKjFwIml-f9zX50YbhOWIrWOPA

用base64解密后如下:

header每次的token都一樣,如下:

{“typ”:”JWT”,”alg”:”HS512”}

body每次會變,因為iat創建時間和exp過期時間每次會變:

{“sub”:”026a564bbfd84861ac4b65393644beef”,”iat”:1558597028,”exp”:1559201828}

訪問驗證

token在生成返回給客戶端,以后客戶端每個請求要在http header上帶上,所有需要被驗證的接口都從header上取出token,用如下方法驗證:

1
2
3
4
5
6

//驗證token
Claims claims = jwtUtils.getClaimByToken(token);
if(claims == null || jwtUtils.isTokenExpired(claims.getExpiration())){
throw new MyException("憑證失效,請重新登錄");
}
1
2
3
4
5
6
7
8
9
10
11
public Claims getClaimByToken(String token) {
try {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}catch (Exception e){
logger.debug("token驗證錯誤,請重新登陸 ", e);
return null;
}
}

核心邏輯

token的核心在于簽名,是通過下面設置實現的:

1
signWith(SignatureAlgorithm.HS512, secret)

這里有的hamc,再加個密碼鹽。

對一個字符串,用hamc(string+secret)得到的hash值為簽名。

因為secret這個字符串只有自己知道,所以別人是生成不了一個字符串的簽名的。

這種簽名邏輯就保證了token只有自己發出去的才有效,別人偽造不了。

安全問題

因為token是在請求中明文傳的,所以如果不用https協議,別人可以獲取到。

這樣別人就可以用token訪問服務了。

所以token和傳輸一定要保證安全,不加密一定用https。

体彩25选5走势图