Home > Article > Backend Development > WebApi implements communication encryption
How to effectively achieve data exchange between the client and the service with the minimum amount of existing code intrusion What about encryption?
webapiThe server has the following Interface:
##
public class ApiTestController : ApiController { // GET api/<controller>/5 public object Get(int id) { return "value" + id; } }
ApiTestController
Unencrypted request GET /api/apitest?id=10Return result response "value10"We want The effect achieved is:
or MoreOthermethods of encryption
2. Functional analysis
If you want to make no changes to the existing code, we all know All api controllers are initialized after the router is determined, so we should encrypt the GET parameters and POST parameters before the router.Look at the picture below webapi Life cycle:
We see that there is a DelegationgHander layer for message processing before routingrouting.
Because we need to decrypt the parameters of each request and encrypt the return message, we aim at 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
3. Practice:Now we will implement it in the future Two versions of communication encryption and decryption functions, designated as version 1.0 base64 encryption and version 1.1 Des encryption1 /// <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 /// <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
Writing version 1.1 des encryption and decryption##
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 }
Attach encryption and decryption A basic encapsulation class
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加密解密
The above is the detailed content of WebApi implements communication encryption. For more information, please follow other related articles on the PHP Chinese website!