zoukankan      html  css  js  c++  java
  • WebApi服务Uri加密及验证的两种方式

    最近的一个项目要求服务端与UI层分离,业务层以WebApi方式向外提供所有业务服务,服务在数据保密性方面提出了要求,主要包括:

    1:客户端认证;

    2:服务请求超时(默认5分钟);

    3:服务Get请求的参数密文传输。

    以上三个需求为一般的网络服务比较常见的简单要求,在WebApi项目中也比较容易实现,以下是我采用的两种实现方式(任意一种即可):

    一:继承HttpClient来加入数据(Uri)加密,加入验证Header,继承ApiController来对Header进行合法性验证,并解密Uri

    客户端的关键代码包括对HttpClient继承实现:

     1     public class MyHttpClient : HttpClient
     2     {
     3         private static MyHttpClient _myClient;
     4 
     5         public static MyHttpClient Instance
     6         {
     7             get
     8             {
     9                 if(_myClient!=null)return _myClient;
    10                 _myClient = new MyHttpClient();
    11                 _myClient.DefaultRequestHeaders.Add("Mac", MacMd5);
    12                 return _myClient;
    13             }
    14         }
    15 
    16         private MyHttpClient()
    17         {
    18         }
    19 
    20         private static readonly IAuthorize _authorize = new MacAuthorize();
    21         private static string _macMd5 = string.Empty;
    22 
    23         private static string MacMd5
    24         {
    25             get
    26             {
    27                 if (!string.IsNullOrEmpty(_macMd5)) return _macMd5;
    28                 var macAddress = _authorize.UniqueId;
    29                 if (string.IsNullOrEmpty(macAddress)) return string.Empty;
    30                 _macMd5 = macAddress.GetMD5();
    31                 return _macMd5;
    32             }
    33         }
    34 
    35         private static string TimeAes
    36         {
    37             get
    38             {
    39                 var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    40                 var aesTime = time.AesEncrypt();
    41                 return aesTime;
    42             }
    43         }
    44 
    45         private static void AddHeader(ref string url)
    46         {
    47             var segs = url.Split('/');
    48             var para = segs[segs.Length - 1];
    49             if (para.Contains("="))
    50             {
    51                 var aes = para.AesEncrypt();
    52                 url = url.Replace(para, aes.GetMD5());
    53                 IEnumerable<string> p = new List<string>();
    54                 if (_myClient.DefaultRequestHeaders.TryGetValues("Param", out p))
    55                     _myClient.DefaultRequestHeaders.Remove("Param");
    56                 _myClient.DefaultRequestHeaders.Add("Param",aes);
    57             }
    58 
    59             IEnumerable<string> time = new List<string>();
    60             if (_myClient.DefaultRequestHeaders.TryGetValues("Time", out time))
    61                 _myClient.DefaultRequestHeaders.Remove("Time");
    62             _myClient.DefaultRequestHeaders.Add("Time", TimeAes);
    63         }
    64 
    65         public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
    66             System.Threading.CancellationToken cancellationToken)
    67         {
    68             var url = request.RequestUri.AbsoluteUri;
    69             AddHeader(ref url);
    70             return base.SendAsync(request, cancellationToken);
    71         }
    72 
    73         public override int GetHashCode()
    74         {
    75             return _myClient.GetHashCode();
    76         }
    77 
    78     }
    View Code

    其中时间戳为了简便,未转为ms格式进行验证,仅作为字符串进行了验证。

    在客户端进行调用时,通过继承的MyHttpClient发送服务请求即可:

    1 var client = MyHttpClient.Instance;
    2 var response = client.GetAsync(urlBase.AbsoluteUri + "api/test/GetUser/id=123123&name=jiakai").Result;
    View Code

    服务端的关键代码包括对ApiController的继承实现:

     1     public class BaseApiController : ApiController
     2     {
     3         protected override void Initialize(HttpControllerContext controllerContext)
     4         {
     5             base.Initialize(controllerContext);
     6             //获取请求头信息
     7             var requestHeader = controllerContext.Request.Headers;
     8             IEnumerable<string> mac = new List<string>();
     9             requestHeader.TryGetValues("Mac", out mac);
    10             IEnumerable<string> time = new List<string>();
    11             requestHeader.TryGetValues("Time", out time);
    12             IEnumerable<string> para = new List<string>();
    13             requestHeader.TryGetValues("Param", out para);
    14             var paramList = controllerContext.Request.RequestUri.AbsoluteUri.Split('/');
    15             if (mac == null || time == null || para == null)
    16             {
    17                 var resp = Request.CreateResponse<string>(HttpStatusCode.Forbidden, "非法请求", "application/json");
    18                 throw new HttpResponseException(resp);
    19             }
    20             //验证机器MAC地址
    21             if (mac.Any()&&!string.IsNullOrEmpty(mac.First()))
    22             {
    23                 //TODO:验证机器MAC地址是否为已注册MAC地址
    24             }
    25             //验证时间戳
    26             if (time.Any() && !string.IsNullOrEmpty(time.First()))
    27             {
    28                 var t = Convert.ToDateTime(time.First().AesDecrypt());
    29                 if (t.AddMinutes(5) < DateTime.Now)
    30                 {
    31                     var resp = Request.CreateResponse<string>(HttpStatusCode.RequestTimeout, "请求过期", "application/json");
    32                     throw new HttpResponseException(resp);
    33                 }
    34             }
    35             //验证参数MD5
    36             if (para.Any() && !string.IsNullOrEmpty(para.First()))
    37             {
    38                 var newMd5 = paramList[paramList.Length - 1];
    39                 if (para.First().GetMD5() != newMd5)
    40                 {
    41                     var resp = Request.CreateResponse<string>(HttpStatusCode.Forbidden, "参数MD5验证失败", "application/json");
    42                     throw new HttpResponseException(resp);
    43                 }
    44             }
    45         }
    46     }
    View Code

    同样,在业务实现的Controller中,继承改为BaseApiController即可:

    1 public class TestController : BaseApiController
    2 {
    3     //TODO:业务服务具体实现
    4 }
    View Code

    二:通过对WebApi消息处理框架中HttpMessageHandler的自定义实现,并分别注入到Request及Response的消息处理阶段,来实现加密/解密的功能

    客户端HttpClient中HttpMessageHandler的实现:

     1     public class RequestHandler : DelegatingHandler
     2     {
     3         private static string TimeAes
     4         {
     5             get
     6             {
     7                 var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
     8                 var aesTime = time.AesEncrypt();
     9                 return aesTime;
    10             }
    11         }
    12 
    13         protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
    14             System.Threading.CancellationToken cancellationToken)
    15         {
    16             var uri = request.RequestUri.AbsoluteUri;
    17             var port = request.RequestUri.Port;
    18             var para = uri.Split('/').Last();
    19             if (para.Contains("="))
    20             {
    21                 var aes = para.AesEncrypt();
    22                 uri = uri.Replace(para, aes.GetMD5());
    23                 IEnumerable<string> p = new List<string>();
    24                 if (request.Headers.TryGetValues("Param", out p))
    25                     request.Headers.Remove("Param");
    26                 request.Headers.Add("Param", aes);
    27             }
    28             IEnumerable<string> time = new List<string>();
    29             if (request.Headers.TryGetValues("Time", out time))
    30                 request.Headers.Remove("Time");
    31             request.Headers.Add("Time", TimeAes);
    32             request.RequestUri = new Uri(uri);
    33 
    34             return base.SendAsync(request, cancellationToken);
    35         }
    36     }
    View Code

    在实现了自定义的HttpMessageHandler后,实现一个HttpClient工厂,对外提供一个注入了该HttpMessageHandler的HttpClient单例对象:

     1     public class AtmHttpClientFactory
     2     {
     3         private static HttpClient _atmClient;
     4         private readonly static HttpClientHandler ClientHandler = new HttpClientHandler();
     5 
     6         public static HttpClient GetClient()
     7         {
     8             if (_atmClient != null) return _atmClient;
     9             _atmClient = new HttpClient(new RequestHandler() {InnerHandler = ClientHandler });
    10             _atmClient.DefaultRequestHeaders.Add("Mac", MacMd5);
    11             return _atmClient;
    12         }
    13 
    14         private AtmHttpClientFactory()
    15         {
    16         }
    17 
    18         private static readonly IAuthorize _authorize = new MacAuthorize();
    19         private static string _macMd5 = String.Empty;
    20 
    21         private static string MacMd5
    22         {
    23             get
    24             {
    25                 if (!String.IsNullOrEmpty(_macMd5)) return _macMd5;
    26                 var macAddress = _authorize.UniqueId;
    27                 if (String.IsNullOrEmpty(macAddress)) return String.Empty;
    28                 _macMd5 = macAddress.GetMD5();
    29                 return _macMd5;
    30             }
    31         }
    32     }
    View Code

    这样,在客户端的调用加密过程就完全被封装,简化为如下:

    1      //调用实现1
    2      var myClient = AtmHttpClientFactory.GetClient();
    3      var myResponse = myClient.GetAsync("http://api.ezbatm.com:7777/api/test/GetUser/id=123123&name=jiakai").Result;
    View Code

    服务端也是类似的方法,在app的config中注入我们自己实现的服务端HttpMessageHandler即可。response的HttpMessageHandler实现:

     1     public class ResponseHandler : DelegatingHandler
     2     {
     3         protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
     4             System.Threading.CancellationToken cancellationToken)
     5         {
     6             //获取请求头信息
     7             IEnumerable<string> mac = new List<string>();
     8             request.Headers.TryGetValues("Mac", out mac);
     9             IEnumerable<string> time = new List<string>();
    10             request.Headers.TryGetValues("Time", out time);
    11             IEnumerable<string> para = new List<string>();
    12             request.Headers.TryGetValues("Param", out para);
    13             var paramList = request.RequestUri.AbsoluteUri.Split('/');
    14             //验证Header是否完整
    15             if (mac == null || time == null || para == null)
    16             {
    17                 return RequestNotAcceptable();
    18             }
    19             //验证机器MAC地址
    20             if (mac.Any() && !string.IsNullOrEmpty(mac.First()))
    21             {
    22                 //TODO:验证机器MAC地址是否为已注册MAC地址
    23             }
    24             //验证时间戳
    25             if (time.Any() && !string.IsNullOrEmpty(time.First()))
    26             {
    27                 var t = Convert.ToDateTime(time.First().AesDecrypt());
    28                 if (t.AddMinutes(5) < DateTime.Now)
    29                 {
    30                     return RequestTimeOut();
    31                 }
    32             }
    33             //验证参数MD5
    34             if (para.Any() && !string.IsNullOrEmpty(para.First()))
    35             {
    36                 var newMd5 = paramList[paramList.Length - 1];
    37                 //if (para.First().GetMD5() != newMd5)
    38                 {
    39                     return RequestForbidden();
    40                 }
    41             }
    42             return base.SendAsync(request, cancellationToken);
    43         }
    44 
    45         private static Task<HttpResponseMessage> RequestForbidden()
    46         {
    47             return Task.Factory.StartNew(() =>
    48             {
    49                 return new HttpResponseMessage(HttpStatusCode.Forbidden)
    50                 {
    51                     Content = new StringContent("请求未通过认证")
    52                 };
    53             });
    54         }
    55 
    56         private static Task<HttpResponseMessage> RequestTimeOut()
    57         {
    58             return Task.Factory.StartNew(() =>
    59             {
    60                 return new HttpResponseMessage(HttpStatusCode.RequestTimeout)
    61                 {
    62                     Content = new StringContent("请求已超时")
    63                 };
    64             });
    65         }
    66 
    67         private static Task<HttpResponseMessage> RequestNotAcceptable()
    68         {
    69             return Task.Factory.StartNew(() =>
    70             {
    71                 return new HttpResponseMessage(HttpStatusCode.NotAcceptable)
    72                 {
    73                     Content = new StringContent("请求为非法请求")
    74                 };
    75             });
    76         }
    77     }    
    View Code

    之后,在(owin框架)Startup中注入该自定义HttpMessageHandler即可:

     1         public void Configuration(IAppBuilder app)
     2         {            
     3             // Configure Web API for self-host. 
     4             var config = new HttpConfiguration();
     5             config.Routes.MapHttpRoute(
     6                 name: "DefaultApi",
     7                 routeTemplate: "api/{controller}/{action}/{param}",
     8                 defaults: new { id = RouteParameter.Optional }
     9             );
    10             //注入response handler
    11             config.MessageHandlers.Add(new ResponseHandler());
    12 
    13             app.UseWebApi(config);
    14         }
    View Code

    完成了以上客户端+服务器端的工作后,即可按照我们的实际需求对Request及Response消息进行自定义的处理。

    总结:以上两种方法中,客户端及服务器端的方案都是解耦的,也就是是,你可以采用方法一的客户端方案+方法二的服务器端方案,等等以此类推。

  • 相关阅读:
    Solidity safesub防止溢出
    Solidity字符串拼接实现oraclize动态查询
    Solidity mapping循环
    Solidity 合约调用合约
    Solidity string to uint
    Solidity智能合约升级解决方案
    Solidity部署问题
    linux 安装xwiki
    linux 安装 java
    linux 安装tomcat
  • 原文地址:https://www.cnblogs.com/you-you-111/p/4915286.html
Copyright © 2011-2022 走看看