JWT的定義:
JWT是一種用於雙方之間傳遞安全信息的簡潔的、URL安全的表述性聲明規范。JWT作為一個開放的標准(RFC 7519),定義了一種簡潔的,自包含的方法用於通信雙方之間以Json對象的形式安全的傳遞信息。因為數字簽名的存在,這些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘鑰對進行簽名。
JWT特點:
簡潔(Compact): 可以通過URL,POST參數或者在HTTP header發送,因為數據量小,傳輸速度也很快
自包含(Self-contained):負載中包含了所有用戶所需要的信息,避免了多次查詢數據庫
JWT結構:
JWT主要包含Header 頭部 Payload 負載 Signature 簽名,順序是 header.payload.signature 三部分之間用英語句號’.'隔開
Header頭部
頭部包含了兩部分,token 類型(“JWT”)和采用的加密算法(HMAC SHA256或者RSA等等)
例如:
然后,用Base64對這個JSON編碼就得到JWT的第一部分
Payload負載
這部分就是我們存放信息的地方了,你可以把用戶 ID 等信息放在這里,JWT 規范里面對這部分有進行了比較詳細的介紹,常用的由 iss(簽發者),exp(過期時間),sub(面向的用戶),aud(接收方),iat(簽發時間)。
同樣的,它會使用 Base64 編碼組成 JWT 結構的第二部分。
Signature簽名
前面兩部分都是使用 Base64 進行編碼的,即前端可以解開知道里面的信息。Signature 需要使用Base64編碼后的 header 和 payload 以及我們提供的一個密鑰,然后使用 header 中指定的簽名算法(HS256)進行簽名。簽名的作用是保證 JWT 沒有被篡改過。三個部分通過.
連接在一起就是我們的 JWT 了,它可能長這個樣子,長度貌似和你的加密算法和私鑰有關系。
eyJhbGciOiJIUzI1NiIsIlR5cGUiOiJKd3QiLCJ0eXAiOiJKV1QifQ.eyJsb2dpblRpbWUiOiIyMDIxLTEwLTI3VDE1OjIxOjU4LjU3OCIsInVzZXJOYW1lIjoi5rGf5Y2XIiwiZXhwIjoxNjM1MzIxMTE4fQ.lUojZpIGx0a65s5FrgYlQio0vf3jOuqWOTP-DUjzGh0
JSON Web Tokens是如何工作的?
一般是在請求頭里加入Authorization
,並加上Bearer
標注:
fetch('api/user/1', { headers: { 'Authorization': 'Bearer ' + token } })
服務端會驗證token,如果驗證通過就會返回相應的資源。整個流程就是這樣的:
優點
- 因為json的通用性,所以JWT是可以進行跨語言支持的,像JAVA,JavaScript,NodeJS,PHP等很多語言都可以使用。
- 因為有了payload部分,所以JWT可以在自身存儲一些其他業務邏輯所必要的非敏感信息。
- 便於傳輸,jwt的構成非常簡單,字節占用很小,所以它是非常便於傳輸的。
- 它不需要在服務端保存會話信息, 所以它易於應用的擴展
安全相關
- 不應該在jwt的payload部分存放敏感信息,因為該部分是客戶端可解密的部分。
- 保護好secret私鑰,該私鑰非常重要。
- 如果可以,請使用https協議
最后附上Token工具類
import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.interfaces.DecodedJWT; import java.time.LocalDateTime; import java.util.Date; import java.util.HashMap; import java.util.Map; public class TokenUtil { /** * token過期時間 */ private static final long EXPIRE_TIME = 30 * 60 * 1000; /** * token秘鑰 */ private static final String TOKEN_SECRET = "secret"; /** * 生成token,30分鍾過期 * * @param userName 用戶名 * @param loginTime 登錄時間 * @return 生成的token */ public static String sign(String userName, LocalDateTime loginTime) { try { // 設置過期時間 Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); System.out.println(date); // 私鑰和加密算法 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); // 設置頭部信息 Map<String, Object> header = new HashMap<>(3); header.put("Type", "Jwt"); header.put("alg", "HS256"); // 返回token字符串 return JWT.create() .withHeader(header) // 設置token中需要加載的用戶信息 存儲自己想要留給前端的內容 .withClaim("userName", userName) .withClaim("loginTime", loginTime.toString()) .withExpiresAt(date) .sign(algorithm); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 檢驗token是否正確 */ public static boolean verify(String token) { try { //設置簽名的加密算法:HMAC256 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); JWTVerifier verifier = JWT.require(algorithm).build(); verifier.verify(token); return true; } catch (Exception e) { return false; } } /** * 獲取token中信息 userName */ public static String getUsername(String token) { try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("userName").asString(); } catch (JWTDecodeException e) { e.printStackTrace(); } return null; } }