登录  /  注册
WebApi实现通讯加密
PHPz
发布: 2017-03-12 16:17:01
原创
1946人浏览过

一. 场景介绍:

如题如何有效的,最少量的现有代码侵入从而实现客户端与服务器之间的数据交换加密呢?

二. 探究:

1.需求分析

webapi服务端 有如下接口:


public class ApiTestController : ApiController
{    // GET api/<controller>/5
    public object Get(int id)
    {        return "value" + id;
    }
}
登录后复制

ApiTestController

无加密请求

GET /api/apitest?id=10

返回结果

response "value10"

我们想要达到的效果为:

Get /api/apitest?aWQ9MTA=

response InZhbHVlMTAi (解密所得 "value10")

或者更多其它方式加密

2.功能分析

要想对现有代码不做任何修改, 我们都知道所有api controller 初始化在router确定之后, 因此我们应在router之前将GET参数和POST的参数进行加密才行.

看下图 webapi 生命周期:

我们看到在 路由routing 之前 有DelegationgHander 层进行消息处理.

因为我们要对每个请求进行参数解密处理,并且又将返回消息进行加密处理, 因此我们 瞄准 MessageProcessingHandler


    //
    // 摘要:    //     A base type for handlers which only do some small processing of request and/or    //     response messages.
    public abstract class MessageProcessingHandler : DelegatingHandler
    {        //
        // 摘要:        //     Creates an instance of a System.Net.Http.MessageProcessingHandler class.
        protected MessageProcessingHandler();        //
        // 摘要:        //     Creates an instance of a System.Net.Http.MessageProcessingHandler class with        //     a specific inner handler.        //
        // 参数:        //   innerHandler:        //     The inner handler which is responsible for processing the HTTP response messages.
        protected MessageProcessingHandler(HttpMessageHandler innerHandler);        //
        // 摘要:        //     Performs processing on each request sent to the server.        //
        // 参数:        //   request:        //     The HTTP request message to process.        //
        //   cancellationToken:        //     A cancellation token that can be used by other objects or threads to receive        //     notice of cancellation.        //
        // 返回结果:        //     Returns System.Net.Http.HttpRequestMessage.The HTTP request message that was        //     processed.
        protected abstract HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken);        //
        // 摘要:        //     Perform processing on each response from the server.        //
        // 参数:        //   response:        //     The HTTP response message to process.        //
        //   cancellationToken:        //     A cancellation token that can be used by other objects or threads to receive        //     notice of cancellation.        //
        // 返回结果:        //     Returns System.Net.Http.HttpResponseMessage.The HTTP response message that was        //     processed.
        protected abstract HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken);        //
        // 摘要:        //     Sends an HTTP request to the inner handler to send to the server as an asynchronous        //     operation.        //
        // 参数:        //   request:        //     The HTTP request message to send to the server.        //
        //   cancellationToken:        //     A cancellation token that can be used by other objects or threads to receive        //     notice of cancellation.        //
        // 返回结果:        //     Returns System.Threading.Tasks.Task`1.The task object representing the asynchronous        //     operation.        //
        // 异常:        //   T:System.ArgumentNullException:        //     The request was null.
        protected internal sealed override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
    }
登录后复制

MessageProcessingHandler

三. 实践:

现在我们将来 先实现2个版本的通讯加密解密功能,定为 版本1.0 base64加密, 版本1.1 Des加密

 1     /// <summary> 2     /// 加密解密接口 3     /// </summary> 4     public interface IMessageEnCryption 5     { 6         /// <summary> 7         /// 加密 8         /// </summary> 9         /// <param name="content"></param>10         /// <returns></returns>11         string Encode(string content);12         /// <summary>13         /// 解密14         /// </summary>15         /// <param name="content"></param>16         /// <returns></returns>17         string Decode(string content);18     }
登录后复制

IMessageEnCryption

编写版本1.0 base64加密解密

 1     /// <summary> 2     /// 加解密 只做 base64 3     /// </summary> 4     public class MessageEncryptionVersion1_0 : IMessageEnCryption 5     { 6         public string Decode(string content) 7         { 8             return content?.DecryptBase64(); 9         }10 11         public string Encode(string content)12         {13             return content.EncryptBase64();14         }15     }
登录后复制

MessageEncryptionVersion1_0

编写版本1.1 des加密解密


 1     /// <summary> 2     /// 数据加解密 des 3     /// </summary> 4     public class MessageEncryptionVersion1_1 : IMessageEnCryption 5     { 6         public static readonly string KEY = "fHil/4]0"; 7         public string Decode(string content) 8         { 9             return content.DecryptDES(KEY);10         }11 12         public string Encode(string content)13         {14             return content.EncryptDES(KEY);15         }16     }
登录后复制

MessageEncryptionVersion1_1

附上加密解密的基本的一个封装类


 1     public static class EncrypExtends 2     { 3  4         //默认密钥向量 5         private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; 6         internal static string Key = "*@&$(@#H"; 7  8         //// <summary> 9         /// DES加密字符串10         /// </summary>11         /// <param name="encryptString">待加密的字符串</param>12         /// <param name="encryptKey">加密密钥,要求为8位</param>13         /// <returns>加密成功返回加密后的字符串,失败返回源串</returns>14         public static string EncryptDES(this string encryptString, string encryptKey)15         {16             try17             {18                 byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8));19                 byte[] rgbIV = Keys;20                 byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString);21                 DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider();22                 MemoryStream mStream = new MemoryStream();23                 CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);24                 cStream.Write(inputByteArray, 0, inputByteArray.Length);25                 cStream.FlushFinalBlock();26                 return Convert.ToBase64String(mStream.ToArray());27             }28             catch29             {30                 return encryptString;31             }32         }33         //// <summary>34         /// DES解密字符串35         /// </summary>36         /// <param name="decryptString">待解密的字符串</param>37         /// <param name="decryptKey">解密密钥,要求为8位,和加密密钥相同</param>38         /// <returns>解密成功返回解密后的字符串,失败返源串</returns>39         public static string DecryptDES(this string decryptString, string key)40         {41             try42             {43                 byte[] rgbKey = Encoding.UTF8.GetBytes(key.Substring(0, 8));44                 byte[] rgbIV = Keys;45                 byte[] inputByteArray = Convert.FromBase64String(decryptString);46                 DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider();47                 MemoryStream mStream = new MemoryStream();48                 CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);49                 cStream.Write(inputByteArray, 0, inputByteArray.Length);50                 cStream.FlushFinalBlock();51                 return Encoding.UTF8.GetString(mStream.ToArray());52             }53             catch54             {55                 return decryptString;56             }57         }58         public static string EncryptBase64(this string encryptString)59         {60             return Convert.ToBase64String(Encoding.UTF8.GetBytes(encryptString));61         }62         public static string DecryptBase64(this string encryptString)63         {64             return Encoding.UTF8.GetString(Convert.FromBase64String(encryptString));65         }66         public static string DecodeUrl(this string cryptString)67         {68             return System.Web.HttpUtility.UrlDecode(cryptString);69         }70         public static string EncodeUrl(this string cryptString)71         {72             return System.Web.HttpUtility.UrlEncode(cryptString);73         }74     }
登录后复制

EncrypExtends

OK! 到此我们前题工作已经完成了80%,开始进行HTTP请求的 消息进和出的加密解密功能的实现.

我们暂时将加密的版本信息定义为 HTTP header头中 以 api_version 的value 来判别分别是用何种方式加密解密

header例:

  api_version: 1.0

  api_version: 1.1


 1     /// <summary> 2     ///  API消息请求处理 3     /// </summary> 4     public class JoyMessageHandler : MessageProcessingHandler 5     { 6  7         /// <summary> 8         /// 接收到request时 处理 9         /// </summary>10         /// <param name="request"></param>11         /// <param name="cancellationToken"></param>12         /// <returns></returns>13         protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken)14         {15             if (request.Content.IsMimeMultipartContent())16                 return request;17             // 获取请求头中 api_version版本号18             var ver = System.Web.HttpContext.Current.Request.Headers.GetValues("api_version")?.FirstOrDefault();19             // 根据api_version版本号获取加密对象, 如果为null 则不需要加密20             var encrypt = MessageEncryptionCreator.GetInstance(ver);21 22             if (encrypt != null)23             {24                 // 读取请求body中的数据25                 string baseContent = request.Content.ReadAsStringAsync().Result;26                 // 获取加密的信息27                 // 兼容 body: 加密数据  和 body: code=加密数据28                 baseContent = baseContent.Match("(code=)*(?<code>[\\S]+)", 2);29                 // URL解码数据30                 baseContent = baseContent.DecodeUrl();31                 // 用加密对象解密数据32                 baseContent = encrypt.Decode(baseContent);33 34                 string baseQuery = string.Empty;35                 if (!request.RequestUri.Query.IsNullOrEmpty())36                 {37                     // 同 body38                     // 读取请求 url query数据39                     baseQuery = request.RequestUri.Query.Substring(1);40                     baseQuery = baseQuery.Match("(code=)*(?<code>[\\S]+)", 2);41                     baseQuery = baseQuery.DecodeUrl();42                     baseQuery = encrypt.Decode(baseQuery);43                 }44                 // 将解密后的 URL 重置URL请求45                 request.RequestUri = new Uri($"{request.RequestUri.AbsoluteUri.Split('?')[0]}?{baseQuery}");46                 // 将解密后的BODY数据 重置47                 request.Content = new StringContent(baseContent);48             }49 50             return request;51         }52 53         /// <summary>54         /// 处理将要向客户端response时55         /// </summary>56         /// <param name="response"></param>57         /// <param name="cancellationToken"></param>58         /// <returns></returns>59         protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken)60         {61             //var isMediaType = response.Content.Headers.ContentType.MediaType.Equals(mediaTypeName, StringComparison.OrdinalIgnoreCase);62             var ver = System.Web.HttpContext.Current.Request.Headers.GetValues("api_version")?.FirstOrDefault();63             var encrypt = MessageEncryptionCreator.GetInstance(ver);64             if (encrypt != null)65             {66                 if (response.StatusCode == HttpStatusCode.OK)67                 {68                     var result = response.Content.ReadAsStringAsync().Result;69                     // 返回消息 进行加密70                     var encodeResult = encrypt.Encode(result);71                     response.Content = new StringContent(encodeResult);72                 }73             }74 75             return response;76         }77 78     }
登录后复制

JoyMessageHandler

最后在 webapiconfig 中将我们的消息处理添加到容器中


 1     public static class WebApiConfig 2     { 3         public static void Register(HttpConfiguration config) 4         { 5             // Web API 配置和服务 6             // 将 Web API 配置为仅使用不记名令牌身份验证。 7             config.SuppressDefaultHostAuthentication(); 8             config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); 9 10             // Web API 路由11             config.MapHttpAttributeRoutes();12 13             config.Routes.MapHttpRoute(14                 name: "DefaultApi",15                 routeTemplate: "api/{controller}/{id}",16                 defaults: new { id = RouteParameter.Optional }17             );18 19             // 添加自定义消息处理20             config.MessageHandlers.Add(new JoyMessageHandler());21 22         }23     }
登录后复制

WebApiConfig

编写单元测试:


 1  [TestMethod()] 2         public void GetTest() 3         { 4             var id = 10; 5             var resultSuccess = $"\"value{id}\""; 6             //不加密 7             Trace.WriteLine($"without encryption."); 8             var url = $"api/ApiTest?id={id}"; 9             Trace.WriteLine($"get url : {url}");10             var response = http.GetAsync(url).Result;11             var result = response.Content.ReadAsStringAsync().Result;12             Assert.AreEqual(result, resultSuccess);13             Trace.WriteLine($"result : {result}");14 15             //使用 方案1加密16             Trace.WriteLine($"encryption case one.");17 18             url = $"api/ApiTest?code=" + $"id={id}".EncryptBase64().EncodeUrl();19 20             Trace.WriteLine($"get url : {url}");21 22             http.DefaultRequestHeaders.Clear();23             http.DefaultRequestHeaders.Add("api_version", "1.0");24             response = http.GetAsync(url).Result;25 26             result = response.Content.ReadAsStringAsync().Result;27 28             Trace.WriteLine($"result : {result}");29 30             result = result.DecryptBase64();31 32             Trace.WriteLine($"DecryptBase64 : {result}");33 34             Assert.AreEqual(result, resultSuccess);35 36             //使用 方案2 加密通讯37             Trace.WriteLine($"encryption case one.");38             39             url = $"api/ApiTest?code=" + $"id={id}".EncryptDES(MessageEncryptionVersion1_1.KEY).EncodeUrl();40 41             Trace.WriteLine($"get url : {url}");42 43             http.DefaultRequestHeaders.Clear();44             http.DefaultRequestHeaders.Add("api_version", "1.1");45             response = http.GetAsync(url).Result;46 47             result = response.Content.ReadAsStringAsync().Result;48 49             Trace.WriteLine($"result : {result}");50 51             result = result.DecryptDES(MessageEncryptionVersion1_1.KEY);52 53             Trace.WriteLine($"DecryptBase64 : {result}");54 55             Assert.AreEqual(result, resultSuccess);56         }
登录后复制

ApiTestControllerTests

至此为止功能实现完毕..

四.思想延伸

要想更加安全的方案,可以将给每位用户生成不同的 private key , 利用AES加密解密

以上就是WebApi实现通讯加密的详细内容,更多请关注php中文网其它相关文章!

相关标签:
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 技术文章
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2023 //m.sbmmt.com/ All Rights Reserved | 苏州跃动光标网络科技有限公司 | 苏ICP备2020058653号-1

 | 本站CDN由 数掘科技 提供

登录PHP中文网,和优秀的人一起学习!
全站2000+教程免费学