最近使用WebApi开发一套对外接口,主要是数据的外送以及结果回传,接口没什么难度,采用WebApi+EF的架构简单创建一个模板工程,使用template生成一套WebApi接口,去掉put、delete等操作,修改一下就可以上线。这些都不在话下,反正网上一大堆教程,随便找那个step by step做下来就可以了。
然后发布上线后,接口是放在外网,面临两个问题:
其实这两个问题是相互结合的,先保证合法,然后在合法基础上保证请求的唯一性,避免参数被篡改。
鉴于接口上线期限紧迫,结合众多案例,先解决掉接口调用数据的安全性问题,这里采用了RSA报文加解密的方案,保证数据安全和防止接口被恶意调用以及参数篡改的问题。
本文参考博客园多篇博文,内容多有引用,文末附有参照博文的地址。
以下为正文!
首先,接口面临的问题:
解决方案:
先说服务端:
先看一下MessageProcessingHandler的介绍:
扩展这个类的目的是解密参数,其实也可以推迟到Action过滤器中做,但是还是觉得时机上在这里处理比较合适。具体的建议了解一下WebApi消息管道以及扩展过滤器的相关文章,本文不再延伸。
下面是扩展的实现代码:
/// <summary> /// 请求预处理,报文解密 /// </summary> /// <seealso cref="System.Net.Http.MessageProcessingHandler"/> public class ArgDecryptMessageProcesssingHandler : MessageProcessingHandler { /// <summary> /// 处理每个发送到服务器的请求。 /// </summary> /// <param name="request"> 要处理的 HTTP 请求消息。</param> /// <param name="cancellationToken">可由其他对象或线程用以接收取消通知的取消标记。</param> /// <returns>已处理的 HTTP 请求消息。</returns> protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken) { var contentType = request.Content.Headers.ContentType; //swagger请求直接跳过不予处理 if (request.RequestUri.AbsolutePath.Contains("/swagger")) { return request; } //获得平台私钥 string privateKey = Common.GetRsaPrivateKey(); //获取Get中的Query信息,解密后重置请求上下文 if (request.Method == HttpMethod.Get) { string baseQuery = request.RequestUri.Query; if (!string.IsNullOrEmpty(baseQuery)) { baseQuery = baseQuery.Substring(1); baseQuery = Regex.Match(baseQuery, "(sign=)*(?<sign>[\\S]+)").Groups[2].Value; baseQuery = RsaHelper.RSADecrypt(privateKey, baseQuery); var requestUrl = $"{request.RequestUri.AbsoluteUri.Split('?')[0]}?{baseQuery}"; request.RequestUri = new Uri(requestUrl); } } //获取Post请求中body中的报文信息,解密后重置请求上下文 if (request.Method == HttpMethod.Post) { string baseContent = request.Content.ReadAsStringAsync().Result; baseContent = Regex.Match(baseContent, "(sign=)*(?<sign>[\\S]+)").Groups[2].Value; baseContent = RsaHelper.RSADecrypt(privateKey, baseContent); request.Content = new StringContent(baseContent); //此contentType必须最后设置 否则会变成默认值 request.Content.Headers.ContentType = contentType; } return request; } /// <summary> /// 处理来自服务器的每个响应。 /// </summary> /// <param name="response"> 要处理的 HTTP 响应消息。</param> /// <param name="cancellationToken">可由其他对象或线程用以接收取消通知的取消标记。</param> /// <returns>已处理的 HTTP 响应消息。</returns> protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken) { return response; } }
获取平台私钥那里,实际上可以针对不同的接口调用方单独一个,另起一篇在介绍。
然后找到解决方案【App_Start】目录下的WebApiConfig类,在里面添加如下代码,启用消息处理扩展类:
注意!注意!注意!
原博文中是扩展的 AuthorizeAttribute,即认证和授权过滤器,代码实现上是没有多大差别的;在时机上认证和授权过滤器要比方法过滤器执行的要早,更适合做认证和授权的操作。而我们扩展这个过滤器的目的是对报文进行签名验证以及超时验证,所以使用方法过滤器更恰当些。
下面是扩展过滤器的代码:
这里为了方便写了个ResponseModel,代码如下:
然后下面是用的公共方法:
签名验证的过程如下:
然后,现在需要启用刚添加的方法过滤器,因为是继承与属性,可以全局启用,或者单个Controller中启用、或者为某个Action启用。全局启用代码如下:
下的WebApiConfig类添加如下代码:
OK,全部完成,最后附上两个前后的效果对比!
参考博文:
写博文太累了,回家吃螃蟹补补~
原文链接:https://www.cnblogs.com/buyixiaohan/p/11773088.html
原创文章,作者:优速盾-小U,如若转载,请注明出处:https://www.cdnb.net/bbs/archives/18196