實現原理:
在access_token里加入refresh_token標識,給access_token設置短時間的期限(例如一天),給refresh_token設置長時間的期限(例如七天)。當活動用戶(擁有access_token)發起request時,在權限驗證里,對於requeset的header包含的access_token、refresh_token分別進行驗證:
1、access_token沒過期,即通過權限驗證;
2、access_token過期,refresh_token沒過期,則返回權限驗證失敗,並在返回的response的header中加入標識狀態的key,在request方法的catch中通過webException來獲取標識的key,獲取新的token(包含新的access_token和refresh_token),再次發起請求,並返回給客戶端請求結果以及新的token,再在客戶端更新公共靜態token模型;
3、access_token過期,refresh_token過期即權限驗證失敗。
下面展示一下關鍵代碼:
一、登錄生成token的時候加入refresh標識
public TOKEN GetToken(string username, string password) { TOKEN token = new TOKEN(); U_USER model = new BLLU_USER().Token(username, password); if (model != null) { string pwd = new SDDMD().MD5_jie(model.USERPWD); string md5 = new SDDMD().MD5_jia(model.USERID +"&"+ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") +"&"+ pwd+"&refresh"+ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); token.access_token = md5; token.token_type = "Authorization"; token.expires_in = DateTime.Now.ToString("yyyyMMddHHmmss"); //token.refresh_token = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "&refresh"; } return token; }
二、在權限驗證環節,對於access_token、refresh_token設置不同時間的期限。再根據判斷結果返回狀態。
/// <summary> /// 校驗token是否有效 /// </summary> /// <param name="encryptTicket">用戶提交的Token值</param> /// <returns></returns> private int ValidateTicket(string encryptTicket) { try { var strTicket = new DBUtility.SDDMD().MD5_jie(encryptTicket); string[] TicketMsg = strTicket.Split('&'); if (TicketMsg.Length == 4) { string username = TicketMsg[0];//用戶名 string passwrod = TicketMsg[2];//密碼 U_USER model = new BLL.BLLU_USER().Token(username, passwrod);//登陸驗證 if (model != null) { _userModel = model; //定義Access_Token有效期為1天,精確到秒 DateTime dt = Convert.ToDateTime(TicketMsg[1]).AddDays(1);
//定義Refresh_Token有效期為1天,精確到秒
DateTime dtNew= Convert.ToDateTime(TicketMsg[3].Substring(7)).AddDays(7);// if (dt > DateTime.Now) return 1; //1:access_token沒過期 else { if (dtNew > DateTime.Now) return 2; //2:access_token過期,refresh_token沒過期 else return 3; //3:access_token過期,refresh_token過期 } } else return 3; } else return 3; } catch { return 3; } }
三、根據反饋的狀態執行不同的方法,“2”(access_token過期,refresh_token沒過期)狀態下,給返回失敗的response的header中加入識別的key值。
/// <summary> /// 定義Access_token驗證過期但Reflesh_token有效返回請求刷新token的信息 /// </summary> /// <param name="actionContext"></param> protected void RefreshToken(HttpActionContext actionContext) { var content = "refreshtoken"; base.HandleUnauthorizedRequest(actionContext); var response = actionContext.Response; response.StatusCode = HttpStatusCode.Forbidden; response.Content = new StringContent(content, Encoding.UTF8, "application/json"); response.Headers.Add("token", "refresh");//加入識別的key值 }
四、request方法中通過Catch捕獲webException對象獲取Key值,並獲取新的token(包含新的access_token和refresh_token),再次發起請求,並返回給客戶端請求結果以及新的token。
/// <summary> /// Get head中攜帶token發送請求 /// </summary> /// <param name="url">WebAPI訪問路徑</param> /// <param name="tokentype">token驗證方式 《Authorization》</param> /// <param name="tokenvalue">token 《"Bearer " + Token》</param> /// <returns></returns> public string GetRequest(string url, string tokentype, string tokenvalue) { try { string responseStr = string.Empty; WebRequest request = WebRequest.Create(http + url); request.Timeout = 60000; request.Method = "Get"; request.Headers.Add(tokentype, tokenvalue); var response = request.GetResponse(); Stream ReceiveStream = response.GetResponseStream(); using (StreamReader stream = new StreamReader(ReceiveStream, Encoding.UTF8)) { responseStr = stream.ReadToEnd(); } return responseStr; } catch (WebException e) { using (WebResponse response = e.Response) { string result=response.Headers.Get("token");//獲取識別的KEY if (result == "refresh")//驗證是否為狀態“2”方法所加的key值 { var strTicket = new SDDMD().MD5_jie(tokenvalue.Substring(6)); string[] TicketMsg = strTicket.Split('&'); string username = TicketMsg[0];//用戶名 string passwrod = TicketMsg[2];//密碼 string responseStr = GetRequest("CYUMS/Token/" + username + "/" + passwrod);//獲取新的token TOKEN tokenmodel = JsonConvert.DeserializeObject<TOKEN>(responseStr); string secondResponseStr= GetRequest(url, tokenmodel.token_type, "Haval " + tokenmodel.access_token);//再次發起請求 return secondResponseStr + "|" + responseStr;//返回請求結果以及新的token } else throw e; } } }
五、客戶端識別token是否更新,如果更新,就更新公共靜態token模型中的access_token的值;
string user = new HttpHelper().GetRequest(getUserModel, tokenmodel.token_type, "Haval " + tokenmodel.access_token); string[] str = user.Split('|'); if (str.Length == 2)//判斷是否有token更新 { TOKEN tokens = JsonConvert.DeserializeObject<TOKEN>(str[1]); tokenmodel.access_token = tokens.access_token; } U_USER users = JsonConvert.DeserializeObject<U_USER>(str[0]); //business code...