WebAPI接口安全校验

Posted 性能、可用性、伸缩性、扩展性、安全性、可监控是网站架构最核心

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebAPI接口安全校验相关的知识,希望对你有一定的参考价值。

通过网上查看相关WebAPI接口验证的方法,整理了一下,直接上代码,功能不复杂,有问题留言,

//-----------------------------------------------------------------------
// <copyright file="ApiAuthenticationFilter.cs" company="FenSiShengHuo, Ltd.">
//     Copyright (c) 2018 , All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Principal;
using System.Threading;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace DotNet.WeChat.API.Attribute
{
    using DotNet.Business;
    using DotNet.Model;
    using DotNet.Utilities;

    /// <summary>
    /// ApiAuthenticationFilter
    /// 
    /// 2018-07-19 版本:1.0 JiShiYu 创建。    
    ///
    /// <author>
    ///        <name>JiShiYu</name>
    ///        <date>2018-07-19</date>
    /// </author> 
    /// </summary>
    public class ApiAuthenticationFilter : AuthorizationFilterAttribute
    {
        public override void OnAuthorization(HttpActionContext filterContext)
        {
            // 判断是否是不做授权检查的
            var apiNoAuthenticationFilter = filterContext.ActionDescriptor.GetCustomAttributes<ApiNoAuthenticationFilter>();
            if (apiNoAuthenticationFilter != null && apiNoAuthenticationFilter.Any())
            {
                return;
            }

            BaseResult result = null;
            var identity = ParseHeader(filterContext, out result); //取得資料內容
            if (identity == null)
            {
                ChallengeAuthRequest(filterContext, result); //回傳錯誤訊息
                return;
            }
            var genericPrincipal = new GenericPrincipal(identity, null);
            //  在认证成功后将认证身份设置给当前线程中Principal属性
            Thread.CurrentPrincipal = genericPrincipal;
            result = OnAuthorizeUser(identity, filterContext);
            if (!result.Status)
            {
                ChallengeAuthRequest(filterContext, result);
                return;
            }
        }

        /// <summary>
        /// 定义一个方法便校验,并将其修饰为虚方法,以免后续要添加其他有关用户数据
        /// </summary>
        /// <param name="appKey"></param>
        /// <param name="appSecret"></param>
        /// <param name="token"></param>
        /// <param name="actionContext"></param>
        /// <returns></returns>
        protected virtual BaseResult OnAuthorizeUser(BasicAuthenticationIdentity identity, HttpActionContext actionContext)
        {
            BaseResult result = new BaseResult();
            result.Status = false;
            result.StatusCode = Status.ParameterError.ToString();

            if (string.IsNullOrWhiteSpace(identity.SystemCode))
            {
                result.Status = false;
                result.StatusCode = Status.ParameterError.ToString();
                result.StatusMessage = "systemCode " + Status.ParameterError.ToDescription();
                return result;
            }
            // 判断参数
            if (identity.UserInfo != null)
            {
                // 防止伪造、判断用户的有效性
                if (!ServiceUtil.VerifySignature(identity.UserInfo))
                {
                    result.StatusCode = Status.SignatureError.ToString();
                    result.StatusMessage = "userInfo " + Status.SignatureError.ToDescription();
                    return result;
                }
                // 这里需要是已经登录的用户,不是已经被踢掉的用户
                if (!Utilities.ValidateOpenId(identity.UserInfo.Id, identity.UserInfo.OpenId))
                {
                    result.StatusCode = Status.SignatureError.ToString();
                    result.StatusMessage = "OpenId " + Status.SignatureError.ToDescription();
                    return result;
                }
            }
            else
            {
                string ipAddress = Utilities.GetIPAddress(true);
                // 检查是否为内部ip地址发送出去的手机短信  
                //if (onlyLocalIp)
                //{
                //    if (!IpHelper.IsLocalIp(ipAddress))
                //    {
                //        // 不是内网发出的, 也不是信任的ip列表里的,直接给拒绝发送出去
                //        result.Status = false;
                //        result.StatusCode = Status.ErrorIPAddress.ToString();
                //        result.StatusMessage = ipAddress + " " + Status.ErrorIPAddress.ToDescription();
                //    }
                //}

                if (string.IsNullOrWhiteSpace(identity.AppKey))
                {
                    result.StatusCode = Status.ParameterError.ToString();
                    result.StatusMessage = "appKey " + Status.ParameterError.ToDescription();
                    return result;
                }
                if (string.IsNullOrWhiteSpace(identity.Signature))
                {
                    result.StatusCode = Status.ParameterError.ToString();
                    result.StatusMessage = "signature " + Status.ParameterError.ToDescription();
                    return result;
                }

                if (string.IsNullOrWhiteSpace(identity.TimeSpan))
                {
                    result.StatusCode = Status.ParameterError.ToString();
                    result.StatusMessage = "TimeSpan " + Status.ParameterError.ToDescription();
                    return result;
                }
                else
                {
                    long unixTimeStamp;
                    if (long.TryParse(identity.TimeSpan, out unixTimeStamp))
                    {
                        DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
                        DateTime dtStart = startTime.AddSeconds(unixTimeStamp);
                        TimeSpan ts = DateTime.Now - dtStart;
                        if (ts.TotalMinutes > 5)
                        {
                            result.StatusCode = Status.ParameterError.ToString();
                            result.StatusMessage = "请求时间已过期,请检查请求服务器时间,传递当前时间戳";
                            return result;
                        }
                    }
                    else
                    {
                        result.StatusCode = Status.ParameterError.ToString();
                        result.StatusMessage = "TimeSpan时间戳参数错误,传递当前时间戳";
                        return result;
                    }
                }
                //  AppSecret 不应该传
                //if (string.IsNullOrWhiteSpace(identity.AppSecret))
                //{
                //    result.StatusCode = Status.ParameterError.ToString();
                //    result.StatusMessage = "appSecret " + Status.ParameterError.ToDescription();
                //    return result;
                //}
                // 取服务器上的
                var secret = BaseServicesLicenseManager.GetSecretByKeyByCache(identity.AppKey);
                if (string.IsNullOrWhiteSpace(secret))
                {
                    result.StatusCode = Status.ParameterError.ToString();
                    result.StatusMessage = "找不到appKey的密钥";
                    return result;
                }
                else
                {
                    string serverSignature = System.Web.HttpUtility.UrlEncode(SecretUtilitiesBase.md5(identity.AppKey + identity.TimeSpan + secret));
                    if (!string.Equals(identity.Signature, serverSignature))
                    {
                        result.StatusCode = Status.ParameterError.ToString();
                        result.StatusMessage = "Signature 签名不正确";
                        return result;
                    }
                }

                result = BaseServicesLicenseManager.CheckService(identity.AppKey, secret, true, false, 0, 0, identity.SystemCode, identity.PermissionCode);
                if (result.Status)
                {
                    // 从接口确定当前调用者
                    BaseUserEntity userEntity = BaseUserManager.GetObjectByCodeByCache(identity.AppKey, true);
                    if (userEntity != null)
                    {
                        BaseUserInfo userInfo = new BaseUserInfo();
                        userInfo.Id = userEntity.Id;
                        userInfo.Code = userEntity.Code;
                        userInfo.UserName = userEntity.UserName;
                        userInfo.NickName = userEntity.NickName;
                        userInfo.RealName = userEntity.RealName;
                        userInfo.CompanyId = userEntity.CompanyId;
                        userInfo.CompanyCode = userEntity.CompanyCode;
                        userInfo.CompanyName = userEntity.CompanyName;
                        userInfo.IPAddress = ipAddress;
                        identity.UserInfo = userInfo;
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 解析Headers
        /// </summary>
        /// <param name="filterContext"></param>
        /// <returns></returns>
        protected virtual BasicAuthenticationIdentity ParseHeader(HttpActionContext filterContext, out BaseResult result)
        {
            BasicAuthenticationIdentity authenticationIdentity = null;
            result = new BaseResult();
            BaseUserInfo userInfo = null;
            string systemCode = string.Empty;
            string appKey = string.Empty;
            string timeSpan = string.Empty;
            string signature = string.Empty;
            string permissionCode = string.Empty;
            string userInfoStr = string.Empty;
            try
            {
                var re = filterContext.Request;
                var headers = re.Headers;
                if (headers.Contains("systemCode"))
                {
                    systemCode = headers.GetValues("systemCode").First();
                }
                if (headers.Contains("appKey"))
                {
                    appKey = headers.GetValues("appKey").First();
                }
                if (headers.Contains("timeSpan"))
                {
                    timeSpan = headers.GetValues("timeSpan").First();
                }
                if (headers.Contains("signature"))
                {
                    signature = headers.GetValues("signature").First();
                }
                if (headers.Contains("permissionCode"))
                {
                    permissionCode = headers.GetValues("permissionCode").First();
                }
                if (headers.Contains("userInfoStr"))
                {
                    userInfoStr = headers.GetValues("userInfoStr").First();
                    userInfo = BaseUserInfo.Deserialize(userInfoStr);
                }

                authenticationIdentity = new BasicAuthenticationIdentity(systemCode, appKey, timeSpan, signature, userInfo, permissionCode);
                result.Status = true;
                result.StatusCode = Status.OK.ToString();
                result.StatusMessage = "构建成功";
            }
            catch (Exception ex)
            {
                NLogHelper.Warn(ex, "ParseHeader");
                result.Status = false;
                result.StatusCode = Status.Error.ToString();
                result.StatusMessage = "异常:" + ex.Message;
            }

            return authenticationIdentity;
        }

        /// <summary>
        /// 授权验证失败
        /// </summary>
        /// <param name="filterContext"></param>
        private static void ChallengeAuthRequest(HttpActionContext filterContext, BaseResult result)
        {
            var dnsHost = filterContext.Request.RequestUri.DnsSafeHost;
            //filterContext.Response = filterContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
            filterContext.Response = filterContext.Request.CreateResponse(HttpStatusCode.OK, result);
            filterContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\\"{0}\\"", dnsHost));
        }
    }

    /// <summary>
    /// 认证实体类
    /// </summary>
    public class BasicAuthenticationIdentity : GenericIdentity
    {
        /// <summary>
        /// 系统编号
        /// </summary>
        public string SystemCode { get; set; }

        /// <summary>
        /// AppKey
        /// </summary>
        public string AppKey { get; set; }

        /// <summary>
        /// 请求时间戳
        /// </summary>
        public string TimeSpan { get; set; }

        /// <summary>
        /// Signature 签名值
        /// </summary>
        public string Signature { get; set; }

        /// <summary>
        /// 用户信息
        /// </summary>
        public BaseUserInfo UserInfo { get; set; }

        /// <summary>
        /// 权限编号
        /// </summary>
        public string PermissionCode { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="systemCode"></param>
        /// <param name="appKey"></param>
        /// <param name="timeSpan"></param>
        /// <param name="signature"></param>
        /// <param name="userInfo"></param>
        /// <param name="permissionCode"></param>
        public BasicAuthenticationIdentity(string systemCode, string appKey, string timeSpan, string signature, BaseUserInfo userInfo, string permissionCode)
            : base(appKey, "Basic")
        {
            this.SystemCode = systemCode;
            this.AppKey = appKey;
            this.TimeSpan = timeSpan;
            this.Signature = signature;
            this.UserInfo = userInfo;
            this.PermissionCode = permissionCode;
        }
    }
}

不需要做校验的接口,加一个标签

//-----------------------------------------------------------------------
// <copyright file="ApiAuthenticationFilter.cs" company="FenSiShengHuo, Ltd.">
//     Copyright (c) 2018 , All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System.Web.Http.Filters;

namespace DotNet.WeChat.API.Attribute
{
    /// <summary>
    /// ApiNoAuthenticationFilter
    /// 不做授权检查
    /// 
    /// 2018-07-19 版本:1.0 JiShiYu 创建。    
    ///
    /// <author>
    ///        <name>JiShiYu</name>
    ///        <date>2018-07-19</date>
    /// </author> 
    /// </summary>
    public class ApiNoAuthenticationFilter : AuthorizationFilterAttribute
    { 
    }
}

全局校验,放在哪里,你懂的

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {




            // 接口授权检查
            config.Filters.Add(new ApiAuthenticationFilter());


    }
}

  

测试效果

 

以上是关于WebAPI接口安全校验的主要内容,如果未能解决你的问题,请参考以下文章

Web API统一异常处理 转载

VS Code WebApi系列——2jwt结合数据库校验

3Web Api 身份验证

webAPI过滤器添加参数签名

Android App 安全的HTTPS 通信

API安全-参数校验