在實際的網站設計中咱們常常會遇到用戶數據的驗證和加密的問題,若是實現單點,若是保證數據準確,如何放着重放,如何防止CSRF等等。redis
其中,在全部的服務設計中,都不可避免的涉及到Token的設計。算法
目前,基於Token的生成方,咱們把Token生成分爲兩種類型。瀏覽器
一、基於用戶/網站,可見的加密請求方式服務器
二、基於服務器間通信的不可見加密請求方式(API Token)session
其中,基於非服務器端的請求,咱們要根據實際的應用場景進行必定的自定義加工。
在本次討論中咱們把非服務的請求分爲了兩部分。app
兩種請求中:網站
一、非登陸態請求要求用戶訪問頁面時會隨機生成惟一且有時效性的token,該token在每次請求時都是不一樣。改方法用於當不須要登陸界面的請求多且沒法使用靜態頁面的時候使用,Token會在必定的時間內失效,以此來防止被機器爬取不需登陸的界面加密
二、登陸狀態中,token會保存必定的時間,頁面中的token會做爲用戶身份識別,同時登陸態的Token須要利用session和頁面信息來防止被利用。spa
雖然說二者做用有必定的區別,可是實現的原理是相同的。設計
登陸態的Token通常在用戶登陸以後由服務器產生,並保存在瀏覽器中,過時時間較長,用於保存用戶的登陸狀態。
同時,咱們也能夠在該Token中加入必定的校驗元素,例如瀏覽器信息,ip,獲取是Cookies
Token=Encode(MD5({session}+{用戶信息摘要}+{Timestamp})+TimeStamp)
若是對時效性較強的頁面登陸訪問,咱們能夠加入session的校驗,設置session的有效時間,可以實現自動退出的校驗功能。
TimeStamp用來校驗Token的生成時間。
同時,你用redis的Hset能夠實現多點登陸和單點登陸的功能。
服務端驗證Token:
sign+TimeStamp=Decode(Token)
if(sign===MD5({session}+{用戶信息摘要}+{Timestamp})){ // XXXX }
在常規的API Token體系中,咱們常使用短時過時Token(Oauth Token除外)。
一般 APIToken 是使用非對稱加密來實現token的生成,可是世界生產中,Token 的祕鑰會簡化成app_id,app_key等簡單的加鹽參數,不過只要祕鑰保存合理,Token基本沒法被破解。
/** * 生成token * @param $user_info string * @param $app_key string app_key * @param $app_id int app_id * @return string */ public function generate_access_token($user_info , $app_key, $app_id) { $time = time(); $sign = sha1($time . $advertiser_id . $app_key); $token = base64_encode("{$time},{$user_info },{$app_id},{$sign}");// 分隔符建議替換成其餘的字符 return $token; }
其中 app_key和app_id是一對公鑰和私鑰,惟一且互相對應。同時app_key通常做爲私鑰保存。通常服務系統會提供修改app_key的功能,來解決app_key不當心被泄露的問題。
/** * 解析token * @param $access_token * @return array */ public function analysis_access_token($access_token) { $token_array = base64_decode($access_token); $token_array = explode(',', $token_array);// 分隔符由Token生成算法決定 $time = $token_array[0]; $user_info = $token_array[1]; $app_id = $token_array[2];// 暴露在外的公鑰 $sign = $token_array[3]; if ($time < (time() - 60) || $time > (time() + 60)) { // 校驗時間能夠自定義 call_back(1101, 'Access Token expire !token=' . $access_token); } global $third_platform_app_key;// app_id-app_key對應表 if (!isset($third_platform_app_key[$app_id])) { call_back(1101, 'Access Token App id Error!token=' . $access_token); } $app_key = $third_platform_app_key[$app_id]; $local_sign = sha1($time . $user_info . $app_key);// 利用私鑰進行簽名,驗證有效性 if ($local_sign === $sign) { return [ 'access_token' => $access_token, 'user_info' => $user_info, 'time' => $time, 'app_id' => $app_id, 'app_key' => $app_key, ]; } else { call_back(1101, 'Access Token Sign Error!token=' . $access_token); } }
該Token方式要求每次請求都須要生成新的token來確保請求的時效性。
另外:爲了增強API接口請求的完整性,咱們也會對請求內容進行字段排序後摘要驗證。(詳情參考:https://open.taobao.com/docV2.htm?docId=101617&docType=1)