WebApi服务Uri加密及验证的两种方式
最近的一个项目要求服务端与UI层分离,业务层以WebApi方式向外提供所有业务服务,服务在数据保密性方面提出了要求,主要包括:
1:客户端认证;
2:服务请求超时(默认5分钟);
3:服务Get请求的参数密文传输。
以上三个需求为一般的网络服务比较常见的简单要求,在WebApi项目中也比较容易实现,以下是我采用的两种实现方式(任意一种即可):
一:继承HttpClient来加入数据(Uri)加密,加入验证Header,继承ApiController来对Header进行合法性验证,并解密Uri
客户端的关键代码包括对HttpClient继承实现:
public class MyHttpClient : HttpClient { private static MyHttpClient _myClient; public static MyHttpClient Instance { get { if(_myClient!=null)return _myClient; _myClient = new MyHttpClient(); _myClient.DefaultRequestHeaders.Add("Mac", MacMd5); return _myClient; } } private MyHttpClient() { } private static readonly IAuthorize _authorize = new MacAuthorize(); private static string _macMd5 = string.Empty; private static string MacMd5 { get { if (!string.IsNullOrEmpty(_macMd5)) return _macMd5; var macAddress = _authorize.UniqueId; if (string.IsNullOrEmpty(macAddress)) return string.Empty; _macMd5 = macAddress.GetMD5(); return _macMd5; } } private static string TimeAes { get { var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); var aesTime = time.AesEncrypt(); return aesTime; } } private static void AddHeader(ref string url) { var segs = url.Split('/'); ]; if (para.Contains("=")) { var aes = para.AesEncrypt(); url = url.Replace(para, aes.GetMD5()); IEnumerable<string> p = new List<string>(); if (_myClient.DefaultRequestHeaders.TryGetValues("Param", out p)) _myClient.DefaultRequestHeaders.Remove("Param"); _myClient.DefaultRequestHeaders.Add("Param",aes); } IEnumerable<string> time = new List<string>(); if (_myClient.DefaultRequestHeaders.TryGetValues("Time", out time)) _myClient.DefaultRequestHeaders.Remove("Time"); _myClient.DefaultRequestHeaders.Add("Time", TimeAes); } public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { var url = request.RequestUri.AbsoluteUri; AddHeader(ref url); return base.SendAsync(request, cancellationToken); } public override int GetHashCode() { return _myClient.GetHashCode(); } }
其中时间戳为了简便,未转为ms格式进行验证,仅作为字符串进行了验证。
在客户端进行调用时,通过继承的MyHttpClient发送服务请求即可:
var client = MyHttpClient.Instance; var response = client.GetAsync(urlBase.AbsoluteUri + "api/test/GetUser/id=123123&name=jiakai").Result;
服务端的关键代码包括对ApiController的继承实现:
public class BaseApiController : ApiController { protected override void Initialize(HttpControllerContext controllerContext) { base.Initialize(controllerContext); //获取请求头信息 var requestHeader = controllerContext.Request.Headers; IEnumerable<string> mac = new List<string>(); requestHeader.TryGetValues("Mac", out mac); IEnumerable<string> time = new List<string>(); requestHeader.TryGetValues("Time", out time); IEnumerable<string> para = new List<string>(); requestHeader.TryGetValues("Param", out para); var paramList = controllerContext.Request.RequestUri.AbsoluteUri.Split('/'); if (mac == null || time == null || para == null) { var resp = Request.CreateResponse<string>(HttpStatusCode.Forbidden, "非法请求", "application/json"); throw new HttpResponseException(resp); } //验证机器MAC地址 if (mac.Any()&&!string.IsNullOrEmpty(mac.First())) { //TODO:验证机器MAC地址是否为已注册MAC地址 } //验证时间戳 if (time.Any() && !string.IsNullOrEmpty(time.First())) { var t = Convert.ToDateTime(time.First().AesDecrypt()); ) < DateTime.Now) { var resp = Request.CreateResponse<string>(HttpStatusCode.RequestTimeout, "请求过期", "application/json"); throw new HttpResponseException(resp); } } //验证参数MD5 if (para.Any() && !string.IsNullOrEmpty(para.First())) { ]; if (para.First().GetMD5() != newMd5) { var resp = Request.CreateResponse<string>(HttpStatusCode.Forbidden, "参数MD5验证失败", "application/json"); throw new HttpResponseException(resp); } } } }
同样,在业务实现的Controller中,继承改为BaseApiController即可:
public class TestController : BaseApiController { //TODO:业务服务具体实现 }
二:通过对WebApi消息处理框架中HttpMessageHandler的自定义实现,并分别注入到Request及Response的消息处理阶段,来实现加密/解密的功能
客户端HttpClient中HttpMessageHandler的实现:
public class RequestHandler : DelegatingHandler { private static string TimeAes { get { var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); var aesTime = time.AesEncrypt(); return aesTime; } } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { var uri = request.RequestUri.AbsoluteUri; var port = request.RequestUri.Port; var para = uri.Split('/').Last(); if (para.Contains("=")) { var aes = para.AesEncrypt(); uri = uri.Replace(para, aes.GetMD5()); IEnumerable<string> p = new List<string>(); if (request.Headers.TryGetValues("Param", out p)) request.Headers.Remove("Param"); request.Headers.Add("Param", aes); } IEnumerable<string> time = new List<string>(); if (request.Headers.TryGetValues("Time", out time)) request.Headers.Remove("Time"); request.Headers.Add("Time", TimeAes); request.RequestUri = new Uri(uri); return base.SendAsync(request, cancellationToken); } }
在实现了自定义的HttpMessageHandler后,实现一个HttpClient工厂,对外提供一个注入了该HttpMessageHandler的HttpClient单例对象:
public class AtmHttpClientFactory { private static HttpClient _atmClient; private readonly static HttpClientHandler ClientHandler = new HttpClientHandler(); public static HttpClient GetClient() { if (_atmClient != null) return _atmClient; _atmClient = new HttpClient(new RequestHandler() {InnerHandler = ClientHandler }); _atmClient.DefaultRequestHeaders.Add("Mac", MacMd5); return _atmClient; } private AtmHttpClientFactory() { } private static readonly IAuthorize _authorize = new MacAuthorize(); private static string _macMd5 = String.Empty; private static string MacMd5 { get { if (!String.IsNullOrEmpty(_macMd5)) return _macMd5; var macAddress = _authorize.UniqueId; if (String.IsNullOrEmpty(macAddress)) return String.Empty; _macMd5 = macAddress.GetMD5(); return _macMd5; } } }
这样,在客户端的调用加密过程就完全被封装,简化为如下:
//调用实现1 var myClient = AtmHttpClientFactory.GetClient(); var myResponse = myClient.GetAsync("http://api.ezbatm.com:7777/api/test/GetUser/id=123123&name=jiakai").Result;
服务端也是类似的方法,在app的config中注入我们自己实现的服务端HttpMessageHandler即可。response的HttpMessageHandler实现:
public class ResponseHandler : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { //获取请求头信息 IEnumerable<string> mac = new List<string>(); request.Headers.TryGetValues("Mac", out mac); IEnumerable<string> time = new List<string>(); request.Headers.TryGetValues("Time", out time); IEnumerable<string> para = new List<string>(); request.Headers.TryGetValues("Param", out para); var paramList = request.RequestUri.AbsoluteUri.Split('/'); //验证Header是否完整 if (mac == null || time == null || para == null) { return RequestNotAcceptable(); } //验证机器MAC地址 if (mac.Any() && !string.IsNullOrEmpty(mac.First())) { //TODO:验证机器MAC地址是否为已注册MAC地址 } //验证时间戳 if (time.Any() && !string.IsNullOrEmpty(time.First())) { var t = Convert.ToDateTime(time.First().AesDecrypt()); ) < DateTime.Now) { return RequestTimeOut(); } } //验证参数MD5 if (para.Any() && !string.IsNullOrEmpty(para.First())) { ]; //if (para.First().GetMD5() != newMd5) { return RequestForbidden(); } } return base.SendAsync(request, cancellationToken); } private static Task<HttpResponseMessage> RequestForbidden() { return Task.Factory.StartNew(() => { return new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = new StringContent("请求未通过认证") }; }); } private static Task<HttpResponseMessage> RequestTimeOut() { return Task.Factory.StartNew(() => { return new HttpResponseMessage(HttpStatusCode.RequestTimeout) { Content = new StringContent("请求已超时") }; }); } private static Task<HttpResponseMessage> RequestNotAcceptable() { return Task.Factory.StartNew(() => { return new HttpResponseMessage(HttpStatusCode.NotAcceptable) { Content = new StringContent("请求为非法请求") }; }); } }
之后,在(owin框架)Startup中注入该自定义HttpMessageHandler即可:
public void Configuration(IAppBuilder app) { // Configure Web API for self-host. var config = new HttpConfiguration(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{param}", defaults: new { id = RouteParameter.Optional } ); //注入response handler config.MessageHandlers.Add(new ResponseHandler()); app.UseWebApi(config); }
完成了以上客户端+服务器端的工作后,即可按照我们的实际需求对Request及Response消息进行自定义的处理。
总结:以上两种方法中,客户端及服务器端的方案都是解耦的,也就是是,你可以采用方法一的客户端方案+方法二的服务器端方案,等等以此类推。
WebApi服务Uri加密及验证的两种方式的更多相关文章
- 使用使用dockerfile构建webapi镜像然后使用link和bridge两种方式进行桥接
首先新增一个webapi的项目 项目核心代码 UserContext using Microsoft.EntityFrameworkCore; using System; using System.C ...
- SQL Server验证的两种方式
1.Windows身份验证:本机连接或者受信的局域网连接(一般在忘记管理员密码或者做系统配置的情况下使用). 2.SQLServer验证:使用用户名.密码验证(推荐使用). 启用方法:以Windows ...
- WCF服务使用(IIS+Http)和(Winform宿主+Tcp)两种方式进行发布
1.写在前面 刚接触WCF不久,有很多地方知其然不知其所以然.当我在[创建服务->发布服务->使用服务]这一过程出现过许多问题.如客户端找不到服务引用:客户端只在本机环境中才能访问服务,移 ...
- 服务容错保护断路器Hystrix之一:入门示例介绍(springcloud引入Hystrix的两种方式)
限流知识<高可用服务设计之二:Rate limiting 限流与降级> 在微服务架构中,我们将系统拆分成了一个个的服务单元,各单元间通过服务注册与订阅的方式互相依赖.由于每个单元都在不同的 ...
- 不停止MySQL服务增加从库的两种方式
不停止MySQL服务增加从库的两种方式 转载自:http://lizhenliang.blog.51cto.com/7876557/1669829 现在生产环境MySQL数据库是一主一从,由于业务量访 ...
- XFire构建服务端Service的两种方式(转)
XFire构建服务端service的两种方式,一是用xfire构建,二是和spring集成构建. 一,xifre构建,确保把xfire的jar包导入到工程中或classpath. 1,service的 ...
- XFire构建服务端Service的两种方式
1.原声构建: 2.集成spring构建 http://blog.csdn.net/carefree31441/article/details/4000436XFire构建服务端Service的两种方 ...
- 不停止MySQL服务增加从库的两种方式【转载】
现在生产环境MySQL数据库是一主一从,由于业务量访问不断增大,故再增加一台从库.前提是不能影响线上业务使用,也就是说不能重启MySQL服务,为了避免出现其他情况,选择在网站访问量低峰期时间段操作. ...
- springboot 注册服务注册中心(zk)的两种方式
在使用springboot进行开发的过程中,我们经常需要处理这样的场景:在服务启动的时候,需要向服务注册中心(例如zk)注册服务状态,以便当服务状态改变的时候,可以故障摘除和负载均衡. 我遇到过两种注 ...
随机推荐
- nmap脚本扫描使用总结
nmap的脚本默认目录为:/usr/share/nmap/scripts/ Nmap提供的命令行参数如下 -sC: 等价于--script=default,使用默认类别的脚本进行扫描 可更换其他类别 ...
- kali开启ssh
Kali 2.0安装之后需要做的事--使用SSH进行远程登录 2015年8月11日,Kali官方推出了新的kali系统2.0版本,此次升级最大的特点就是系统界面的设计理念更加先进,以及系统的升级方 ...
- Oracle日期格式转换
本文主要介绍Oracle中的日期转换. 1. 日期转化为字符串 (以2016年10月20日为例) select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') st ...
- zkw费用流+当前弧优化
zkw费用流+当前弧优化 var o,v:..] of boolean; f,s,d,dis:..] of longint; next,p,c,w:..] of longint; i,j,k,l,y, ...
- GridView EmptyDataTemplate 动态显示
以下语句加在GridView.DataBind()之后: Table GridViewTable = ((Table)gvGridView.Controls[]); if (!isSearch) (( ...
- Java设计模式之-----策略模式
首先,我们来看下策略模式的概念.一般的解释如下: 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化.(原文:The St ...
- 4809 江哥的dp题c
4809 江哥的dp题c 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 有两个数x,y,一开始x=1,y= ...
- CSS3 Media Queries 片段
CSS3 Media Queries片段 在这里主要分成三类:移动端.PC端以及一些常见的响应式框架的Media Queries片段. 移动端Media Queries片段 iPhone5 @medi ...
- IE6/IE7/IE8/Firefox/Chrome/Safari的CSS hack兼容一览表
浏览器兼容问题一直是前段开发工程师比较头痛的问题,熟悉了里面的规则也就变得简单了,这里有一份资料可以分享给大家,大家平时开发过程中遵循这个规律的话,会变得轻松多了: 各浏览器CSS hack兼容表: ...
- Nodejs生态圈的TypeScript+React
基于Nodejs生态圈的TypeScript+React开发入门教程 基于Nodejs生态圈的TypeScript+React开发入门教程 概述 本教程旨在为基于Nodejs npm生态圈的前端程 ...