Web API的接口访问安全性

使用签名获取Token

首先我们自定义appkey、appSecret。可用GUID随机生成,AppSecret要不定期更换。然后放到配置文件中。

Appkey=1AF62C68-B970-46E7-B545-E5A5712249D4

AppSecret=DA2502F8-626A-405D-90DB-0351A086FE49

WebApIMD5签名

    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}&timestamp={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 () {
      }
    });
  }

猜你喜欢

转载自www.cnblogs.com/lizhenhong/p/10696409.html