使用签名获取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 () { } }); }