使用簽名獲取Token
首先我們自定義appkey、appSecret。可用GUID隨機生成,AppSecret要不定期更換。然后放到配置文件中。
Appkey=1AF62C68-B970-46E7-B545-E5A5712249D4
AppSecret=DA2502F8-626A-405D-90DB-0351A086FE49
WebApI端MD5簽名
public class AuthorizationHelper { public static bool CheckPartner(HttpRequestBase request, string appSecret,out string msg) { NameValueCollection getCollection = request.Params;//此簽名要求Partner及Sign均通過QueryString傳遞 if (getCollection == null || getCollection.Count == 0) { msg = "調用失敗"; return false; } string appkey = getCollection["appkey"]; string sign = getCollection["sign"]; string timestamp = getCollection["timestamp"]; bool valid=!string.IsNullOrWhiteSpace(appkey)//必須包含partner && !string.IsNullOrWhiteSpace(sign)//必須包含sign && !string.IsNullOrWhiteSpace(timestamp)//必須包含timestamp && Regex.IsMatch(appkey, "^[0-9A-Za-z]{32}$")//appkey必須為32位 && Regex.IsMatch(timestamp, "^(((20[0-3][0-9]-(0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|(20[0-3][0-9]-(0[2469]|11)-(0[1-9]|[12][0-9]|30))) (20|21|22|23|[0-1][0-9]):[0-5][0-9]:[0-5][0-9])$")//timestamp格式必須是yyyy-MM-dd hh:mm:ss && Regex.IsMatch(sign, "^[0-9A-Za-z]{32}$");//sign必須為32位Md5摘要 if (valid) { if (!string.IsNullOrWhiteSpace(appSecret)) { //根據請求數據獲取MD5簽名 string vSign = Util.Md5(appkey, appSecret, timestamp); if (string.Equals(sign, vSign, StringComparison.OrdinalIgnoreCase)) { var requestDatetime=Convert.ToDateTime(timestamp); var span= DateTime.Now.Subtract(requestDatetime); if (Math.Abs(span.TotalMinutes) > 20)//超過20分鍾的請求就過期 { msg="非法調用,請求過期!"; return false; } msg = "調用成功"; return true; } } } msg = "調用失敗"; return false; } public static bool CheckPartner(string appkey,string sign,string timestamp, string appSecret, out string msg) { bool valid = !string.IsNullOrWhiteSpace(appkey)//必須包含partner && !string.IsNullOrWhiteSpace(sign)//必須包含sign && !string.IsNullOrWhiteSpace(timestamp)//必須包含timestamp && Regex.IsMatch(appkey, "^[0-9A-Za-z]{32}$")//appkey必須為32位 && Regex.IsMatch(timestamp, "^(((20[0-3][0-9]-(0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|(20[0-3][0-9]-(0[2469]|11)-(0[1-9]|[12][0-9]|30))) (20|21|22|23|[0-1][0-9]):[0-5][0-9]:[0-5][0-9])$")//timestamp格式必須是yyyy-MM-dd hh:mm:ss && Regex.IsMatch(sign, "^[0-9A-Za-z]{32}$");//sign必須為32位Md5摘要 if (valid) { if (!string.IsNullOrWhiteSpace(appSecret)) { //根據請求數據獲取MD5簽名 string vSign = Util.Md5(appkey, appSecret, timestamp); if (string.Equals(sign, vSign, StringComparison.OrdinalIgnoreCase)) { var requestDatetime=Convert.ToDateTime(timestamp); var span= DateTime.Now.Subtract(requestDatetime); if (Math.Abs(span.TotalMinutes) > 20)//超過10分鍾的請求就過期 { msg="非法調用,請求過期!"; return false; } msg = "調用成功"; return true; } } } msg = "調用失敗"; return false; } }
WebApi端生成token
public class BasePrincipal { /// <summary> /// 用戶Id /// </summary> public int Id { get; set; } /// <summary> /// 手機號 /// </summary> public string Phone { get; set; } /// <summary> /// token /// </summary> public string Token { get; set; } /// <summary> /// 有效期(秒) /// </summary> public int Expires { get; set; } /// <summary> /// 消息 /// </summary> public string Message { get; set; } } public class HomeController : Controller { /// <summary> /// Redis幫助類 /// </summary> static readonly RedisHelper redisHelper = new RedisHelper(); [AllowAnonymous] public JsonResult Login() { BasePrincipal result = new BasePrincipal(); string token = Request.Headers["token"]; var key = "Login:token:"; if (!string.IsNullOrEmpty(token)) //有token,取redis { key=$"{key}{token}"; result = redisHelper.StringGet<BasePrincipal>(key); if (result != null) { return new JsonResult() { Data = result, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } } var phone = Request.Params["phone"]; if (phone == null) { result.Message = "缺少手機號"; return new JsonResult(){Data = result, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } var msg = ""; var AppSecret = System.Configuration.ConfigurationManager.AppSettings["AppSecret"]; var isAuthorization = AuthorizationHelper.CheckPartner(Request, AppSecret, out msg); if (!isAuthorization) { result.Message = $"檢驗不成功,{0},msg"; return new JsonResult(){Data = result, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } //獲取用戶信息業務,各位根據業自己實現這行代碼 result = personService.LoginPersonInfo(phone); if (result == null) { result.Message = "用戶不存在"; return new JsonResult(){Data = result, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } token = Guid.NewGuid().ToString("N");//生成token result.Token = token; result.Expires = 24 * 60 * 60; //設置redis key = $"{key}{token}"; redisHelper.StringSet(key, result,TimeSpan.FromSeconds(result.Expires)); return new JsonResult(){Data = result, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } }
WebApi在Global.asax.cs端校驗token
public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } void Application_BeginRequest(object sender, EventArgs e) { //Cors跨域設置(這些配制放在HttpMethod=="OPTIONS"里面就調用出錯,放出來就沒事,不知原因) var response = HttpContext.Current.Response; response.AddHeader("Access-Control-Allow-Origin", "*"); //正式環境注意改成具體網站,*代表允許所有網站 response.AddHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS"); response.AddHeader("Access-Control-Allow-Headers", "Content-Type,token,Authorization");//Content-Type response.AddHeader("Access-Control-Max-Age", "3600");//設置跨域緩存,減少瀏覽器OPTIONS訪問次數 if (HttpContext.Current.Request.HttpMethod == "OPTIONS") { response.End(); } } protected void Application_Error(object sender, EventArgs e) { //獲取到HttpUnhandledException異常,這個異常包含一個實際出現的異常 Exception ex = Server.GetLastError(); //實際發生的異常 Exception innerException = ex.InnerException; if (innerException != null) ex = innerException; HttpContext.Current.Response.Write(string.Format("捕捉到未處理的異常:{0}<br/>", ex.GetType().ToString())); HttpContext.Current.Response.Write("Global已進行錯誤處理。<br/>"); HttpContext.Current.Response.Write(string.Format("Exception:{0}", ex)); Server.ClearError(); } void Application_PostAuthenticateRequest(object sender, EventArgs e) { // 如果是header中token認證 if (Request.Headers["token"] != null) { var token = Request.Headers["token"]; if (!string.IsNullOrEmpty(token)) { var key = $"Login:token:{token}"; RedisHelper redisHelper = new RedisHelper(); var user = redisHelper.StringGet<BasePrincipal>(key); if (user != null) { HttpContext.Current.User = new PersonPrincipal(user); return; } } } HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName]; if (authCookie != null) { var url = HttpContext.Current.Request.Url.ToString(); if (!string.IsNullOrEmpty(url) && url.StartsWith("https")) { authCookie.Secure = true; } BasePrincipal clientUserData = null; try { FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value); if (authTicket != null) clientUserData = JsonConvert.DeserializeObject<BasePrincipal>(authTicket.UserData); } catch { // ignored } if (HttpContext.Current != null && clientUserData != null) { HttpContext.Current.User = new PersonPrincipal(clientUserData); } } } protected void Application_End() { } }
WebApi 配置文件中加入authentication等節點
<system.web> <authentication mode="Forms"> <forms name=".testToken" loginUrl="url" timeout="30" protection="All" defaultUrl="index.html" /> </authentication> <compilation debug="true" targetFramework="4.6.2" /> <httpRuntime targetFramework="4.5" maxRequestLength="2097151" executionTimeout="3600" /> <customErrors mode="Off" /> <globalization culture="zh-cn" uiCulture="zh-CHS" /> <sessionState mode="Off"></sessionState> <httpCookies httpOnlyCookies="true" requireSSL="true" /> </system.web>
客戶端簽名后調用WebApi Login方法獲取token
public BasePrincipal GetToken(string phone) { var appkey = System.Configuration.ConfigurationManager.AppSettings["Appkey"]; var appSecret = System.Configuration.ConfigurationManager.AppSettings["AppSecret"]; var webApi_url = System.Configuration.ConfigurationManager.AppSettings["WebApi_url"]; var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); #region sign string[] inputs = new string[] { appkey, appSecret, timestamp }; Array.Sort(inputs);//排序 string tmpStr = string.Join("", inputs); var md5Hash = new System.Security.Cryptography.MD5CryptoServiceProvider(); var data = md5Hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(tmpStr)); var sBuilder = new System.Text.StringBuilder(); foreach (var t in data) { sBuilder.Append(t.ToString("x2")); } var sign = sBuilder.ToString(); #endregion var url = string.Format(webApi_url + "/Home/Login?appkey={0}×tamp={1}&sign={2}&phone={4}", appkey, timestamp, sign, phone); var result = HttpClientManager.GetResponse<BasePrincipal>(url); if (result != null) { return result; } return null; }
將獲取到的token保存在客戶端中,在調用webapi接口時,把token放到header中
function GetPerson(phone) { let token = localStorage.getItem("token"); $.ajax({ url: 'http://*******/api/Person/GetPerson, data: { phone: phone }, beforeSend: function (request) { request.setRequestHeader("token", token); }, dataType: 'JSON', async: false,//請求是否異步,默認為異步 type: 'GET', success: function (list) { }, error: function () { } }); }