zoukankan      html  css  js  c++  java
  • 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 () {
          }
        });
      }
  • 相关阅读:
    基于SAAJ的客户端
    SOAP消息的结构
    服务端的思考
    最简单的Web Service实现
    PLSQL的注释技巧
    复杂分支图示
    Tomcat常见错误
    maven常见错误
    SpringMvc参数传递中乱码问题
    springmvc常遇到的错误
  • 原文地址:https://www.cnblogs.com/lizhenhong/p/10696409.html
Copyright © 2011-2022 走看看