利用JWT進行鑒權的思路
- 使用者發起登入請求。
- 伺服器使用私鑰建立一個jwt字元串,作為token;
- 伺服器将這個token傳回給浏覽器;
- 在後續請求中,token作為請求頭的内容,發給服務端。
- 服務端拿到token之後進行解密,正确解密表示此次請求合法,驗證通過;解密失敗說明Token無效或者已過期
- 傳回響應的資源給浏覽器
使用JWT生成Token進行接口鑒權實作方法利用JWT進行鑒權的思路JWT介紹使用的工具類參考
JWT介紹
什麼是JWT
JSON Web令牌(JWT)是一種開放标準(RFC 7519),它定義了一種緊湊且自包含的方式,以JSON對象的形式在通信雙方之間安全地傳輸資訊。由于該資訊是數字簽名的,是以可以對其進行驗證和信任。jwt可以使用散列消息驗證碼(使用HMAC算法),也可以使用RSA或ECDSA公鑰/私鑰對簽名。
應用場景
- 授權: 這是使用JWT最常見的場景。一旦使用者登入,每個後續請求将包括JWT,允許使用者通路該令牌允許的路由、服務和資源。單點登入SSO是目前JWT廣泛使用的一個特性,因為它的開銷很小,而且可以輕松地跨不同的領域使用。
- 資訊交換:JSON Web Tokens 是在各方之間安全地傳輸資訊的一種好方法。因為JWT可以簽名(例如,使用公鑰/私鑰對),是以可以确定發送方就是他們所說的那個人。此外,由于使用頭和有效負載計算簽名,您還可以驗證内容沒有被篡改。
優點
- 簡潔(Compact): 可以通過GET參數、POST參數、HTTP header發送,因為資料量小,傳輸速度也很快
- 自包含(Self-contained):負載中包含了所有使用者所需要的資訊,避免了多次查詢資料庫
- 因為Token是以JSON加密的形式儲存在用戶端的,是以JWT是跨語言的,原則上任何web形式都支援。
- 不需要在服務端儲存會話資訊,特别适用于分布式微服務。
JWT的結構
JWT是由三段資訊構成的,将這三段資訊文本用**.**連接配接一起就構成了JWT字元串。
JWT通常長成這樣:xxxx.yyyy.zzzzz
實際示例如下:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiLlvKDkuIkiLCJleHAiOjE2MzI0NzQxMzQsInVzZXJuYW1lIjoi5byg5LiJIn0.r1UipPk6_69r5Cd7kPoz6rM4NDKDF37su0YOWVGOZ6U
JWT包含了三部分:
- Header 頭部: 通常由兩部分組成:令牌的類型,即JWT,以及使用的簽名算法,如HMAC SHA256或RSA。
- Payload 負載: 有效負載,通信雙方要交換的内容
- Signature 簽名/簽證
Header
通常由兩部分組成:令牌的類型,即JWT,以及使用的簽名算法,如HMAC SHA256或RSA。
{
"alg": "HS256",
"typ": "JWT"
}
加密算法是單向函數雜湊演算法,常見的有MD5、SHA、HAMC。
- *MD5(message-digest algorithm 5)資訊-摘要算法:廣泛用于加密和解密技術,常用于檔案校驗。不管檔案多大,經過MD5後都能生成唯一的MD5值
- SHA (Secure Hash Algorithm)安全雜湊演算法,數字簽名等密碼學應用中重要的工具,安全性高于MD5
- HMAC (Hash Message Authentication Code)散列消息驗證碼,基于密鑰的Hash算法的認證協定。用公開函數和密鑰産生一個固定長度的值作為認證辨別,用這個辨別鑒别消息的完整性。常用于接口簽名驗證
Payload
有效負載就是存放通信雙方要交換的内容的地方
有效資訊包含三個部分
- Registered claims 标準中注冊的聲明
- Public claims 公共的聲明
- Private claims 私有的聲明
标準中注冊的聲明:這些是一組預定義的聲明,不是強制性的,但推薦使用這些聲明,以提供一組有用的、可互操作的聲明。
- iss: jwt簽發者
- sub: 面向的使用者(jwt所面向的使用者)
- aud: 接收jwt的一方
- exp: 過期時間戳(jwt的過期時間,這個過期時間必須要大于簽發時間)
- nbf: 定義在什麼時間之前,該jwt都是不可用的.
- iat: jwt的簽發時間
- jti: jwt的唯一身份辨別,主要用來作為一次性token,進而回避重播攻擊。
公共的聲明 :
公共的聲明可以添加任何的資訊,一般添加使用者的相關資訊或其他業務需要的必要資訊. 但不建議添加敏感資訊,因為該部分在用戶端可解密.
私有的聲明 :
私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感資訊,因為base64是對稱解密的,意味着該部分資訊可以歸類為明文資訊。
Signature
jwt的第三部分是一個簽證資訊
這個部分需要base64加密後的header和base64加密後的payload使用.連接配接組成的字元串,然後通過header中聲明的加密方式進行加鹽secret組合加密,然後就構成了jwt的第三部分。
密鑰secret是儲存在服務端的,服務端會根據這個密鑰進行生成token和進行驗證,是以需要保護好。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
使用的工具類
導入依賴
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.11.0</version>
</dependency>
工具類源碼
public class JwtUtil {
public static void main(String[] args) {
String secret = "123456";
String username = "張三";
String token = sign(username, secret);
printMsg("token:%s", token);
printMsg("username:%s", getUsername(token));
printMsg("verify:%s", verify(token, username, secret));
}
private static void printMsg(String template, Object... args) {
System.out.println(String.format(template, args));
}
public static final long EXPIRE_TIME = 1800000L;
public JwtUtil() {
}
public static boolean verify(String token, String username, String secret) {
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
verifier.verify(token);
return true;
} catch (Exception var6) {
return false;
}
}
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException var2) {
return null;
}
}
public static String sign(String username, String secret) {
Date date = new Date(System.currentTimeMillis() + 1800000L);
Algorithm algorithm = Algorithm.HMAC256(secret);
return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);
}
public static String getUserNameByToken(HttpServletRequest request) throws JeecgBootException {
String accessToken= request.getParameter("token");
if (accessToken== null) {
accessToken= request.getHeader("X-Access-Token");
}
String username = getUsername(accessToken);
if (oConvertUtils.isEmpty(username)) {
throw new JeecgBootException("未擷取到使用者");
} else {
return username;
}
}
}
參考
JWT官網
JWT(Java版)的github