token驗證的機理和簡單實現
阿新 • • 發佈:2018-12-25
摘要:token驗證,機制實現
一、cookie驗證和token驗證的比較
cookie驗證的機制是通過在客戶端生成cookie,在伺服器端生成session,然後每次請求時通過核對前端傳來的cookie和伺服器端session是否一致來管理使用者的狀態。當我們關閉瀏覽器的時候session會被釋放,而cookie也可以自定義失效時間使其在一定時間內失效。
token的驗證機制放棄了session,為使用者生成包含各類資訊的一個集合,將其編碼為一個令牌(token),將這個令牌發給前端,前端每次需要驗證身份時攜帶令牌,伺服器檢查令牌的合法性以驗證身份。簡化了伺服器的儲存,但是要比session+cookie的驗證方式更加消耗計算。是一種以時間換空間的方式。
相較於cookie驗證的幾點好處:
- 支援跨域:cookie是不支援跨域的,token則可以做到。這一點對於我們專案深有感觸,前端和後臺是分離開發和測試,前端的測試常常需要跨域測試,沒有token之前總是統一彙總測試,很麻煩,也想當拖節奏。
- 無狀態:因為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的驗證,可以使用字串將其替換。