JWT(JSON Web Tokens)是一種用于在雙方之間安全傳輸信息的簡潔的、URL安全的令牌標準。以下是關于JWT的結構、作用、優點以及可能出現的問題的詳細解答:
一、JWT的結構
JWT的結構由三個部分組成,它們通過.
(點)分隔:
- 頭部(Header):頭部通常由兩部分組成,第一部分是令牌的類型,即
JWT
;第二部分是使用的加密算法,比如HMAC SHA256或RSA等。頭部信息經過Base64編碼后形成JWT的第一部分。 - 載荷(Payload):載荷部分包含了要傳輸的聲明(claims)。聲明是關于實體(通常是用戶)和其他數據的聲明。聲明分為三種類型:注冊聲明(如
iss
發行人、exp
過期時間等)、公共聲明(自定義的聲明,如user_id
、username
等)和私有聲明(不建議定義與注冊聲明和公共聲明相同的名稱)。載荷也使用Base64編碼進行序列化。 - 簽名(Signature):簽名部分是對頭部和載荷進行簽名生成的,用于驗證消息的完整性和來源。簽名的生成需要使用密鑰,只有擁有密鑰的一方才能對消息進行簽名和驗證。簽名部分同樣經過Base64編碼。
二、JWT的作用
JWT的主要作用是作為一種安全的令牌,在網絡應用的各方之間傳遞信息,確保信息的完整性和真實性。具體來說,JWT的作用包括:
- 身份驗證:JWT可以作為身份驗證的憑證,用于驗證用戶的身份,確保用戶有權訪問受保護的資源或執行特定操作。
- 信息交換:JWT可以在各方之間安全地傳輸信息,無需在服務器端保存會話信息,簡化了服務器的架構并提高了系統的可擴展性。
- 授權:JWT可以包含用戶的角色、權限等信息,用于授權用戶對特定資源或操作的訪問權限。
- 單點登錄(SSO):JWT可以用于實現單點登錄系統,用戶只需在一個系統上登錄,就可以在其他所有相互信任的系統上獲得授權而無需重新登錄。
三、JWT的優點
JWT的優點主要包括:
- 簡單輕量:JWT使用簡單的JSON格式,易于理解和使用。它是一種輕量級的身份驗證和授權方案,適用于不同類型的應用程序。
- 分布式和無狀態:由于JWT包含了所有必要的信息,服務器不需要在數據庫中存儲會話信息,也無需在集群中共享會話狀態。這使得應用程序可以輕松地進行水平擴展。
- 跨域支持:JWT可以通過HTTP頭或URL參數發送,因此可以輕松地跨域傳輸,并且沒有額外的設置或配置需要。
- 安全性:JWT使用數字簽名或加密機制來驗證數據的完整性和安全性。服務器可以使用密鑰對令牌進行簽名,并在每次請求中進行驗證,確保令牌不被篡改或偽造。
- 可擴展性:JWT是基于標準的JSON格式,因此可以通過添加自定義字段來輕松地擴展令牌的功能,以滿足特定的應用程序需求。
四、JWT可能出現的問題
盡管JWT具有許多優點,但在使用過程中也可能會出現一些問題,主要包括:
- 令牌體積大:如果JWT中包含了大量的聲明信息,那么生成的令牌體積可能會相對較大,這可能會增加網絡傳輸的負擔。需要權衡Token大小和網絡傳輸性能,并可能考慮使用壓縮算法或分段傳輸等方式來減小Token的大小。
- 令牌安全性問題:
-
- 敏感信息泄露:由于JWT在客戶端可以被解碼(盡管簽名部分無法偽造),因此不建議在JWT中存儲敏感信息。如果必須存儲敏感信息,應確保使用HTTPS等安全協議來保護JWT的傳輸。
- 令牌篡改:如果JWT被中間人截獲并篡改,那么服務器在驗證JWT時可能會受到欺騙。因此,應確保使用安全的算法和密鑰來生成和驗證JWT。
- 密鑰管理:密鑰的安全管理是JWT安全性的關鍵。如果密鑰被泄露,那么攻擊者可以偽造JWT進行攻擊。因此,需要采取適當的密鑰管理措施來保護密鑰的安全。
- 令牌無法撤銷:JWT一旦生成并發送給客戶端,就無法被撤銷。如果令牌被盜取或泄露,攻擊者可以一直使用該令牌進行訪問,直到令牌過期。為了解決這個問題,可以設置較短的過期時間,并在服務器端記錄Token的黑名單或使用JWT撤銷列表等方式來實現注銷。
- 性能問題:在高負載系統中,驗證和解析JWT可能會消耗大量內存和CPU資源。為了優化性能,可以考慮對JWT的驗證和解析過程進行優化,例如使用緩存機制來存儲已驗證的JWT等。
- CSRF攻擊:在某些情況下,JWT可能會被用于認證CSRF(跨站請求偽造)攻擊。為了防止此類攻擊,需要采取適當的CSRF保護措施
五、demo
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;import java.util.Calendar;
import java.util.Map;/*** Jwt 工具類*/
public class JwtUtils {//自定義密鑰private static final String SIGN = "qwer";/*** 生成 Token* @param map 自定義的載荷數據* @return 返回 Token*/public static String createToken(Map<String, String> map) {//1.設置過期時間(默認 1 天過期)Calendar instance = Calendar.getInstance();instance.add(Calendar.MINUTE, 1);//2.創建 jwt builder,添加自定義的載荷數據JWTCreator.Builder builder = JWT.create();for(Map.Entry<String, String> entry : map.entrySet()) {builder.withClaim(entry.getKey(), entry.getValue());}//3.生成 TokenString token = builder.withExpiresAt(instance.getTime()) //過期時間.sign(Algorithm.HMAC256(SIGN));// signreturn token;}/*** 驗證 Token 合法性* @param token*/public static boolean checkToken(String token) {try {JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 獲取 Token 信息* @param token* @return*/public static DecodedJWT getTokenInfo(String token) {DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);return verify;}}
@GetMapping("/login")@ResponseBodypublic String login(String name) {//2.校驗密碼User dbUser = dbMap.get(name);if (dbUser == null) {return "not found user";}HashMap<String, String> map = new HashMap<>();map.put("name", name);String token = JwtUtils.createToken(map);cache.put(token, dbUser);return token;}@GetMapping("/checkToken")@ResponseBodypublic Boolean checkToken(String token) {System.out.println("checkToken---------------token" + token);return JwtUtils.checkToken(token);}@GetMapping("/logout")@ResponseBodypublic void logout(String token) {System.out.println("logout---------------token" + token);User user = cache.get(token);cache.remove(token);}