webApi签名验证
还是一如既往先上结构图如下:
上一讲说明了redis,也谢谢心态的建议。这里经过改进后的redis的地址
当然这里是加密了的,具体实现如下图:
这里提供的解密。
先把加密解密的帮助类放上来。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace KuRuMi.Mio.DoMain.Infrastructure.Common { /// <summary> /// 对称加密帮助类 /// </summary> public class CryptographyExtension { // 对称加密算法提供器 private ICryptoTransform encryptor; // 加密器对象 private ICryptoTransform decryptor; // 解密器对象 private const int BufferSize = 1024; private static string dataKey = "XXXXXXXX"; public CryptographyExtension(string algorithmName, string key) { SymmetricAlgorithm provider = SymmetricAlgorithm.Create(algorithmName); provider.Key = Encoding.UTF8.GetBytes(key); provider.IV = new byte[] { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; encryptor = provider.CreateEncryptor(); decryptor = provider.CreateDecryptor(); } public CryptographyExtension(string key) : this("TripleDES", key) { } /// <summary> /// 加密算法 /// </summary> /// <param name="clearText"></param> /// <returns></returns> public string Encrypt(string clearText) { // 创建明文流 byte[] clearBuffer = Encoding.UTF8.GetBytes(clearText); MemoryStream clearStream = new MemoryStream(clearBuffer); // 创建空的密文流 MemoryStream encryptedStream = new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write); // 将明文流写入到buffer中 // 将buffer中的数据写入到cryptoStream中 int bytesRead = 0; byte[] buffer = new byte[BufferSize]; do { bytesRead = clearStream.Read(buffer, 0, BufferSize); cryptoStream.Write(buffer, 0, bytesRead); } while (bytesRead > 0); cryptoStream.FlushFinalBlock(); // 获取加密后的文本 buffer = encryptedStream.ToArray(); string encryptedText = Convert.ToBase64String(buffer); return encryptedText; } /// <summary> /// 解密算法 /// </summary> /// <param name="encryptedText"></param> /// <returns></returns> public string Decrypt(string encryptedText) { byte[] encryptedBuffer = Convert.FromBase64String(encryptedText); Stream encryptedStream = new MemoryStream(encryptedBuffer); MemoryStream clearStream = new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read); int bytesRead = 0; byte[] buffer = new byte[BufferSize]; do { bytesRead = cryptoStream.Read(buffer, 0, BufferSize); clearStream.Write(buffer, 0, bytesRead); } while (bytesRead > 0); buffer = clearStream.GetBuffer(); string clearText = Encoding.UTF8.GetString(buffer, 0, (int)clearStream.Length); return clearText; } /// <summary> /// 加密 /// </summary> /// <param name="clearText"></param> /// <param name="key"></param> /// <returns></returns> public static string Encrypts(string clearText) { CryptographyExtension helper = new CryptographyExtension(dataKey); return helper.Encrypt(clearText); } /// <summary> /// 解密 /// </summary> /// <param name="encryptedText"></param> /// <param name="key"></param> /// <returns></returns> public static string Decrypts(string encryptedText) { CryptographyExtension helper = new CryptographyExtension(dataKey); return helper.Decrypt(encryptedText); } } }
完成这一些后,下面来看看签名验证。本来是写好了token+签名的验证,因为token目前还有些问题后来删掉了,改用签名验证。
这个方法是放在client端返回加密后的key。因为博主的项目全是post请求,这里就没有补充get请求。
public static T Post<T>(string url, string data, string appid) { byte[] bytes = Encoding.UTF8.GetBytes(data); HttpWebRequest request = WebRequest.CreateHttp(url) as HttpWebRequest; //加入到头信息中 request.Headers.Add("appid", appid);//当前的用户的请求id request.Headers.Add("sign", GetSignature());//签名验证 //写数据 request.Method = "POST"; request.ContentLength = bytes.Length; request.ContentType = "application/json"; Stream reqstream = request.GetRequestStream(); reqstream.Write(bytes, 0, bytes.Length); //读数据 request.Timeout = 30000; request.Headers.Set("Pragma", "no-cache"); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream streamReceive = response.GetResponseStream(); StreamReader streamReader = new StreamReader(streamReceive, Encoding.UTF8); string strResult = streamReader.ReadToEnd(); //关闭流 reqstream.Close(); streamReader.Close(); streamReceive.Close(); request.Abort(); response.Close(); return JsonConvert.DeserializeObject<T>(strResult); }
然后是调用:
页面上的写法就是在ajax里面加入请求头
$(function () { $("#regist").click(function () { var data = { userName: $("#text_r").val(), email: $("#email_r").val(), passWord: $("#password_r").val() }; debugger; $.ajax({ //"Content-Type":"application/ json" //这里如果将此放入到headers里面会出现传入DTO参数的时候为空 headers: { appid: "2xl7w0Doqog=", sign: "MwOMp5bATVf1N2qNmAxW1GL1mduieOjsLHe45frBuISIpRE9OWncZ569sZraRQnwmWuQHNmJZJjCT/FaWsSiZw=="}, type: 'post', datatype: 'json', data: data, url: 'http://localhost:13292/Api/App/DataForAjax', success: function (data) { $('small').html(data); $('small').delay(3000).hide(0); } }); });
//"Content-Type":"application/ json" //这里如果将此放入到headers里面会出现传入DTO参数的时候为空
这里就是加上了
Content-Type":"application/ json造成的
去掉了之后
下面是filter代码
using KuRuMi.Mio.AppService.Common; using KuRuMi.Mio.AppService.Models; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Web; using System.Web.Http.Controllers; using System.Web.Http.Filters; namespace KuRuMi.Mio.AppService.Filter { public class SignSecretFilter : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { ResultMsg resultMsg = null; string appId = string.Empty; string sign = string.Empty; if (actionContext.Request.Headers.Contains("appid")) { appId = HttpUtility.UrlDecode(actionContext.Request.Headers.GetValues("appid").FirstOrDefault()); } if (actionContext.Request.Headers.Contains("sign")) { sign = HttpUtility.UrlDecode(actionContext.Request.Headers.GetValues("sign").FirstOrDefault()); } //判断请求头是否包含以下参数 if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(sign)) { resultMsg = new ResultMsg(); resultMsg.StatusCode = (int)StatusCodeEnum.ParameterError; resultMsg.Info = StatusCodeEnum.ParameterError.GetEnumText(); resultMsg.Data = ""; actionContext.Response = HttpResponseExtension.toJson(JsonConvert.SerializeObject(resultMsg)); base.OnActionExecuting(actionContext); return; } //验证签名算法 bool result = SignExtension.Validate(appId, sign); if (!result) { resultMsg = new ResultMsg(); resultMsg.StatusCode = (int)StatusCodeEnum.HttpRequestError; resultMsg.Info = StatusCodeEnum.HttpRequestError.GetEnumText(); resultMsg.Data = ""; actionContext.Response = HttpResponseExtension.toJson(JsonConvert.SerializeObject(resultMsg)); base.OnActionExecuting(actionContext); return; } } } }
服务器端验证签名的方法:
using KuRuMi.Mio.DoMain.Infrastructure.Common; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Web; namespace KuRuMi.Mio.AppService.Common { /// <summary> /// 判断签名算法 /// </summary> public class SignExtension { private static readonly string keys = "Kurumi"; /// <summary> /// 判断签名算法 /// </summary> /// <param name="appId"></param> /// <param name="url"></param> /// <param name="sign">签名</param> /// <returns></returns> public static bool Validate(string appId, string sign) { byte[] bytes = Encoding.UTF8.GetBytes(ConfigManagerExtension.SecretKey); string signs = string.Empty; if (appId != "1000") signs = CryptographyExtension.Decrypts(appId) + keys; else signs = appId + keys; byte[] val = Encoding.UTF8.GetBytes(string.Concat(signs.OrderBy(c => c)));//排序 string key = null; using (HMACSHA512 SecretKey = new HMACSHA512(bytes)) { var SecretKeyBytes = SecretKey.ComputeHash(val); key = Convert.ToBase64String(SecretKeyBytes); } return (sign.Equals(key, StringComparison.Ordinal)); } } }