1. 程式人生 > >APP使用者登入解決方案——JWT(java web token)生成Token

APP使用者登入解決方案——JWT(java web token)生成Token

什麼是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程式碼:

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());
	}
}
我的GitHub地址,歡迎Follow。