APP使用者登入解決方案——JWT(java web token)生成Token
阿新 • • 發佈:2018-12-25
什麼是JSON Web Token?
JSON Web Token(JWT)是一個開放式標準(RFC 7519),它定義了一種緊湊且自包含的方式,用於在各方之間以JSON物件安全傳輸資訊。這些資訊可以通過數字簽名進行驗證和信任。可以使用祕密(使用HMAC演算法)或使用RSA的公鑰/私鑰對對JWT進行簽名。
為什麼使用JWT?
隨著技術的發展,分散式web應用的普及,通過session管理使用者登入狀態成本越來越高,因此慢慢發展成為token的方式做登入身份校驗,然後通過token去取redis中的快取的使用者資訊,隨著之後jwt的出現,校驗方式更加簡單便捷化,無需通過redis快取,而是直接根據token取出儲存的使用者資訊,以及對token可用性校驗,單點登入更為簡單。
JWT的優點:
- 體積小,因而傳輸速度更快
- 多樣化的傳輸方式,可以通過URL傳輸、POST傳輸、請求頭Header傳輸(常用)
- 簡單方便,服務端拿到jwt後無需再次查詢資料庫校驗token可用性,也無需進行redis快取校驗
- 在分散式系統中,很好地解決了單點登入問題
- 很方便的解決了跨域授權問題,因為跨域無法共享cookie
JWT的缺點:
- 因為JWT是無狀態的,因此服務端無法控制已經生成的Token失效,是不可控的,這一點對於是否使用jwt是需要重點考量的
- 獲取到jwt也就擁有了登入許可權,因此jwt是不可洩露的,網站最好使用https,防止中間攻擊偷取jwt
對於JWT安全性:
JWT被確實存在被竊取的問題,但是如果能得到別人的token,其實也就相當於能竊取別人的密碼,這其實已經不是JWT安全性的問題。網路是存在多種不安全性的,對於傳統的session登入的方式,如果別人能竊取登入後的sessionID,也就能模擬登入狀態,這和JWT是類似的。為了安全,https加密非常有必要,對於JWT有效時間最好設定短一點。
使用JWT部分後端程式碼:
maven依賴:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.3.0</version>
</dependency>
Java程式碼:
我的GitHub地址,歡迎Follow。import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; import com.alibaba.druid.util.StringUtils; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; /** * APP登入Token的生成和解析 * */ public class JwtToken { /** token祕鑰,請勿洩露,請勿隨便修改 backups:JKKLJOoasdlfj */ public static final String SECRET = "JKKLJOoasdlfj"; /** token 過期時間: 10天 */ public static final int calendarField = Calendar.DATE; public static final int calendarInterval = 10; /** * JWT生成Token.<br/> * * JWT構成: header, payload, signature * * @param user_id * 登入成功後用戶user_id, 引數user_id不可傳空 */ public static String createToken(Long user_id) throws Exception { Date iatDate = new Date(); // expire time Calendar nowTime = Calendar.getInstance(); nowTime.add(calendarField, calendarInterval); Date expiresDate = nowTime.getTime(); // header Map Map<String, Object> map = new HashMap<>(); map.put("alg", "HS256"); map.put("typ", "JWT"); // build token // param backups {iss:Service, aud:APP} String token = JWT.create().withHeader(map) // header .withClaim("iss", "Service") // payload .withClaim("aud", "APP").withClaim("user_id", null == user_id ? null : user_id.toString()) .withIssuedAt(iatDate) // sign time .withExpiresAt(expiresDate) // expire time .sign(Algorithm.HMAC256(SECRET)); // signature return token; } /** * 解密Token * * @param token * @return * @throws Exception */ public static Map<String, Claim> verifyToken(String token) { DecodedJWT jwt = null; try { JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build(); jwt = verifier.verify(token); } catch (Exception e) { // e.printStackTrace(); // token 校驗失敗, 丟擲Token驗證非法異常 } return jwt.getClaims(); } /** * 根據Token獲取user_id * * @param token * @return user_id */ public static Long getAppUID(String token) { Map<String, Claim> claims = verifyToken(token); Claim user_id_claim = claims.get("user_id"); if (null == user_id_claim || StringUtils.isEmpty(user_id_claim.asString())) { // token 校驗失敗, 丟擲Token驗證非法異常 } return Long.valueOf(user_id_claim.asString()); } }