您好,登錄后才能下訂單哦!
小編給大家分享一下WebApi如何實現客戶端與服務器之間的數據交換加密,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
一. 場景介紹:
如題如何有效的,最少量的現有代碼侵入從而實現客戶端與服務器之間的數據交換加密呢?
二. 探究:
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加密
/// <summary> /// 加密解密接口 /// </summary> public interface IMessageEnCryption { /// <summary> /// 加密 /// </summary> /// <param name="content"></param> /// <returns></returns> string Encode(string content); /// <summary> /// 解密 /// </summary> /// <param name="content"></param> /// <returns></returns> string Decode(string content); } IMessageEnCryption
編寫版本1.0 base64加密解密
/// <summary> /// 加解密 只做 base64 /// </summary> public class MessageEncryptionVersion1_0 : IMessageEnCryption { public string Decode(string content) { return content?.DecryptBase64(); } public string Encode(string content) { return content.EncryptBase64(); } } MessageEncryptionVersion1_0
編寫版本1.1 des加密解密
/// <summary> /// 數據加解密 des /// </summary> public class MessageEncryptionVersion1_1 : IMessageEnCryption { public static readonly string KEY = "fHil/4]0"; public string Decode(string content) { return content.DecryptDES(KEY); } public string Encode(string content) { return content.EncryptDES(KEY); } } MessageEncryptionVersion1_1
附上加密解密的基本的一個封裝類
public static class EncrypExtends { //默認密鑰向量 private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; internal static string Key = "*@&$(@#H"; //// <summary> /// DES加密字符串 /// </summary> /// <param name="encryptString">待加密的字符串</param> /// <param name="encryptKey">加密密鑰,要求為8位</param> /// <returns>加密成功返回加密后的字符串,失敗返回源串</returns> public static string EncryptDES(this string encryptString, string encryptKey) { try { byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8)); byte[] rgbIV = Keys; byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString); DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider(); MemoryStream mStream = new MemoryStream(); CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write); cStream.Write(inputByteArray, 0, inputByteArray.Length); cStream.FlushFinalBlock(); return Convert.ToBase64String(mStream.ToArray()); } catch { return encryptString; } } //// <summary> /// DES解密字符串 /// </summary> /// <param name="decryptString">待解密的字符串</param> /// <param name="decryptKey">解密密鑰,要求為8位,和加密密鑰相同</param> /// <returns>解密成功返回解密后的字符串,失敗返源串</returns> public static string DecryptDES(this string decryptString, string key) { try { byte[] rgbKey = Encoding.UTF8.GetBytes(key.Substring(0, 8)); byte[] rgbIV = Keys; byte[] inputByteArray = Convert.FromBase64String(decryptString); DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider(); MemoryStream mStream = new MemoryStream(); CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write); cStream.Write(inputByteArray, 0, inputByteArray.Length); cStream.FlushFinalBlock(); return Encoding.UTF8.GetString(mStream.ToArray()); } catch { return decryptString; } } public static string EncryptBase64(this string encryptString) { return Convert.ToBase64String(Encoding.UTF8.GetBytes(encryptString)); } public static string DecryptBase64(this string encryptString) { return Encoding.UTF8.GetString(Convert.FromBase64String(encryptString)); } public static string DecodeUrl(this string cryptString) { return System.Web.HttpUtility.UrlDecode(cryptString); } public static string EncodeUrl(this string cryptString) { return System.Web.HttpUtility.UrlEncode(cryptString); } } EncrypExtends
OK! 到此我們前題工作已經完成了80%,開始進行HTTP請求的 消息進和出的加密解密功能的實現.
我們暫時將加密的版本信息定義為 HTTP header頭中 以 api_version 的value 來判別分別是用何種方式加密解密
header例:
api_version: 1.0
api_version: 1.1
/// <summary> /// API消息請求處理 /// </summary> public class JoyMessageHandler : MessageProcessingHandler { /// <summary> /// 接收到request時 處理 /// </summary> /// <param name="request"></param> /// <param name="cancellationToken"></param> /// <returns></returns> protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.Content.IsMimeMultipartContent()) return request; // 獲取請求頭中 api_version版本號 var ver = System.Web.HttpContext.Current.Request.Headers.GetValues("api_version")?.FirstOrDefault(); // 根據api_version版本號獲取加密對象, 如果為null 則不需要加密 var encrypt = MessageEncryptionCreator.GetInstance(ver); if (encrypt != null) { // 讀取請求body中的數據 string baseContent = request.Content.ReadAsStringAsync().Result; // 獲取加密的信息 // 兼容 body: 加密數據 和 body: code=加密數據 baseContent = baseContent.Match("(code=)*(?<code>[\\S]+)", 2); // URL解碼數據 baseContent = baseContent.DecodeUrl(); // 用加密對象解密數據 baseContent = encrypt.Decode(baseContent); string baseQuery = string.Empty; if (!request.RequestUri.Query.IsNullOrEmpty()) { // 同 body // 讀取請求 url query數據 baseQuery = request.RequestUri.Query.Substring(1); baseQuery = baseQuery.Match("(code=)*(?<code>[\\S]+)", 2); baseQuery = baseQuery.DecodeUrl(); baseQuery = encrypt.Decode(baseQuery); } // 將解密后的 URL 重置URL請求 request.RequestUri = new Uri($"{request.RequestUri.AbsoluteUri.Split('?')[0]}?{baseQuery}"); // 將解密后的BODY數據 重置 request.Content = new StringContent(baseContent); } return request; } /// <summary> /// 處理將要向客戶端response時 /// </summary> /// <param name="response"></param> /// <param name="cancellationToken"></param> /// <returns></returns> protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken) { //var isMediaType = response.Content.Headers.ContentType.MediaType.Equals(mediaTypeName, StringComparison.OrdinalIgnoreCase); var ver = System.Web.HttpContext.Current.Request.Headers.GetValues("api_version")?.FirstOrDefault(); var encrypt = MessageEncryptionCreator.GetInstance(ver); if (encrypt != null) { if (response.StatusCode == HttpStatusCode.OK) { var result = response.Content.ReadAsStringAsync().Result; // 返回消息 進行加密 var encodeResult = encrypt.Encode(result); response.Content = new StringContent(encodeResult); } } return response; } } JoyMessageHandler
最后在 webapiconfig 中將我們的消息處理添加到容器中
public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服務 // 將 Web API 配置為僅使用不記名令牌身份驗證。 config.SuppressDefaultHostAuthentication(); config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); // 添加自定義消息處理 config.MessageHandlers.Add(new JoyMessageHandler()); } } WebApiConfig
編寫單元測試:
[TestMethod()] public void GetTest() { var id = 10; var resultSuccess = $"\"value{id}\""; //不加密 Trace.WriteLine($"without encryption."); var url = $"api/ApiTest?id={id}"; Trace.WriteLine($"get url : {url}"); var response = http.GetAsync(url).Result; var result = response.Content.ReadAsStringAsync().Result; Assert.AreEqual(result, resultSuccess); Trace.WriteLine($"result : {result}"); //使用 方案1加密 Trace.WriteLine($"encryption case one."); url = $"api/ApiTest?code=" + $"id={id}".EncryptBase64().EncodeUrl(); Trace.WriteLine($"get url : {url}"); http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Add("api_version", "1.0"); response = http.GetAsync(url).Result; result = response.Content.ReadAsStringAsync().Result; Trace.WriteLine($"result : {result}"); result = result.DecryptBase64(); Trace.WriteLine($"DecryptBase64 : {result}"); Assert.AreEqual(result, resultSuccess); //使用 方案2 加密通訊 Trace.WriteLine($"encryption case one."); url = $"api/ApiTest?code=" + $"id={id}".EncryptDES(MessageEncryptionVersion1_1.KEY).EncodeUrl(); Trace.WriteLine($"get url : {url}"); http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Add("api_version", "1.1"); response = http.GetAsync(url).Result; result = response.Content.ReadAsStringAsync().Result; Trace.WriteLine($"result : {result}"); result = result.DecryptDES(MessageEncryptionVersion1_1.KEY); Trace.WriteLine($"DecryptBase64 : {result}"); Assert.AreEqual(result, resultSuccess); } ApiTestControllerTests
以上是WebApi如何實現客戶端與服務器之間的數據交換加密的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。