1. 程式人生 > >token驗證的機理和簡單實現

token驗證的機理和簡單實現

摘要:token驗證,機制實現

一、cookie驗證和token驗證的比較

    cookie驗證的機制是通過在客戶端生成cookie,在伺服器端生成session,然後每次請求時通過核對前端傳來的cookie和伺服器端session是否一致來管理使用者的狀態。當我們關閉瀏覽器的時候session會被釋放,而cookie也可以自定義失效時間使其在一定時間內失效。

    token的驗證機制放棄了session,為使用者生成包含各類資訊的一個集合,將其編碼為一個令牌(token),將這個令牌發給前端,前端每次需要驗證身份時攜帶令牌,伺服器檢查令牌的合法性以驗證身份。簡化了伺服器的儲存,但是要比session+cookie的驗證方式更加消耗計算。是一種以時間換空間的方式。

相較於cookie驗證的幾點好處:

  1. 支援跨域:cookie是不支援跨域的,token則可以做到。這一點對於我們專案深有感觸,前端和後臺是分離開發和測試,前端的測試常常需要跨域測試,沒有token之前總是統一彙總測試,很麻煩,也想當拖節奏。
  2. 無狀態:因為cookie驗證是依賴於session的狀態的,而token則完全不用在session中儲存任何資料,所以可以做到無狀態,只要有token並且合法就可以。

   二、token的組成和簡單實現

token主要有三個部分組成:頭部,載荷,簽名。

頭部和載荷使用的是base64編碼的,簽名則是使用的HS256。當時不是很明白這個HS256,實際上就是HMAC using SHA-256。一種帶金鑰的加密。base64可以利用java有自帶的工具Base64Encoder。

具體參考下面的部落格:

我們通過一個類來實現這個token的功能

此處使用了匯入了Jackson的jar包,方便將物件轉化為json,不然需要手動的轉化。

import java.util.Date;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.thoughtworks.xstream.core.util.Base64Encoder;
import com.zhiku.util.Data;

public class Token {
	
	//JWT的header,實際工作中從配置檔案中獲得
	public static String Header = "{\"typ\": \"JWT\",\"alg\": \"HS256\"}";
	//金鑰
	public static String SECRET_KEY = "secret";
	//base64編碼工具
	public static Base64Encoder be = new Base64Encoder();
	
	public static String getToken(Data message) throws Exception{
		String header = setHeader();
		String payload = setPayload(message);
		String signature = setSignature(header + "." + payload);
		return header + "." + payload + "." + signature;
		
	}

	/**
	 * 設定JWT的荷載payload,荷載中包含主要的資訊
	 * @param payload 一個可以格式化為json的物件
	 * @return
	 * @throws Exception
	 */
	public static String setPayload(Object payload) throws Exception{
		String base64_payload = om.writeValueAsString(payload);
		return be.encode(base64_payload.getBytes());
	}
	
	public static Object getPayload(String base64_payload) throws Exception{
		byte[] h = be.decode(base64_payload);
		return om.readValue(h, Object.class);
	}
	
	/**
	 * 設定header
	 * 獲取已有的header,然後生成base64編碼
	 * @return
	 */
	public static String setHeader(){
		return be.encode(Header.getBytes());
	}
	
	/**
	 * 解析header
	 * @param base64_Header	base64編碼的header部分
	 * @return	返回解碼後的header,以一個data的形式返回對應的json
	 * @throws Exception	在將json轉變為data物件時可能出現異常
	 */
	public static Object getHeader(String base64_Header) throws Exception{
		byte[] h = be.decode(base64_Header);
		return om.readValue(h, Object.class);
	}
	
	/**
	 * 將header和payload使用HS256加密
	 * 然後對加密資訊進行base64編碼
	 * 生成JWT的簽名
	 * @param message	header.payload
	 * @return	JWT的簽名
	 * @throws Exception
	 */
	public static String setSignature(String message) throws Exception{
		Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
	    SecretKeySpec secret_key = new SecretKeySpec(SECRET_KEY.getBytes(), "HmacSHA256");
	    sha256_HMAC.init(secret_key);

	    String hash = be.encode(sha256_HMAC.doFinal(message.getBytes()));
	    return hash;
	}
	
	/**
	 * 驗證token的正確性
	 * @param token	待驗證token
	 * @return	token是否正確
	 */
	public static boolean testSign(String token){
		boolean equal = false;
		String fore_message = token.substring(0,token.lastIndexOf('.'));
		String sign = token.substring(token.lastIndexOf('.')+1);
		try{
			if(sign.equals(setSignature(fore_message))){
				equal = true;
			}else{
				equal = false;
			}
		}catch(Exception e){
			equal = false;
		}
		return equal;
	}
	
	
}

注意:在經過base64轉碼之後有些時候會包含“+”,在前端傳給後臺的時候,“+”會變成“ ”(加號變成了空格)。這個時候就會影響token的驗證,可以使用字串將其替換。