回到目录html
web api愈来愈火,由于它的跨平台,由于它的简单,由于它支持xml,json等流行的数据协议,咱们在开发基于面向服务的API时,有个问题一直在困扰着咱们,那就是数据的安全,请求的安全,通常所说的安全也无非就是请求的防篡改和请求的防复用,例如,你向API发一个查询用户帐户的请求,在这个过程当中,你可能要传递用户ID,用户所在项目ID等,而如今拦截工具如此盛行,很容易就能够把它的请求拦截,而后篡改,再转发,这样你的API就是不安全的,而对于订单,帐户模块这种糟糕的API设计更是致命的,可能引发的损失是不可预计的,是灾难性的,拍拍脑子想一想就知道,你向项目提现10元,我把请求拦截把10改为100000,那么这个将是一个灾难!web
通常使用的方式就是把参数拼接,加上双方约定的“密钥”,加上你的当前项目AppKey,作一次MD5加密,这个过程生成的字符串咱们通常称为密文,而对应的可见参数咱们叫明文,其中明文和密文用于网络传输,而密钥存储在本地和服务器,不能进行传输!算法
通常请求,被重复的使用,也是正常的,就上面的方式进行加密,就没法解决防复用的问题,这时咱们须要在客户端和服务端分别生成UTC的时间戳,这个UTC是防止你的客户端与服务端不在同一个时区,呵呵,而后把时间戳timestamp拼在密文里就能够了,至于防复用的有效性,咱们能够自定义,固然大叔定义的是秒,即同一秒内,请求能够重复发送。数据库
web api核心安全校验代码片段json
代码供你们参考和学习,正式的项目能够根据本身公司的须要去设计api
/// <summary> /// 功能:api数据安全性验证 /// 校验方式:ciphertext=md5(form键的值拼接+timestamp+passkey),服务端用接收到的表单数据与时间戳和本身的passkey进行md5生成,最后比较值是否一致 /// passkey为私钥,不用于网络传递,你能够将它与appKey进行关联,appKey用来传递,服务器根据appKey去数据库里取对应的passkey而后进行比较 /// 功能:请求惟一性,防伪造性 /// timestamp:UTC时间戳,不用于网络传递,在客户端调用服务器时,服务器也生成yyyyMMddhhmmss的时间戳,而后进行计算,看是否过时 /// </summary> [AttributeUsage(AttributeTargets.Method)] public class ApiValidateFilter : ActionFilterAttribute { public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { #region 初始化 var context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];//获取传统context var request = context.Request;//定义传统request对象 var paramStr = new StringBuilder(); var coll = new NameValueCollection(); if (request.HttpMethod.ToLower() == "get") coll = request.QueryString; else coll = request.Form; #endregion #region 解析XML配置文件 var config = CacheConfigFile.ConfigFactory.Instance.GetConfig<ApiValidateModelConfig>().ApiValidateModelList.FirstOrDefault(i => i.AppKey == coll["AppKey"]); if (config == null) { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = new StringContent("AppKey不是合并的,请先去组织生成有效的Key", Encoding.GetEncoding("UTF-8")) }; base.OnActionExecuting(actionContext); } if (config.ExpireDate < DateTime.Now) { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = new StringContent("AppKey不是合并的,密钥已过时", Encoding.GetEncoding("UTF-8")) }; base.OnActionExecuting(actionContext); } #endregion #region 验证算法 var keys = new List<string>(); foreach (string param in coll.Keys) { if (!string.IsNullOrEmpty(param)) { keys.Add(param.ToLower()); } } keys.Sort(); foreach (string p in keys) { if (p != "ciphertext") { if (!string.IsNullOrEmpty(coll[p])) { paramStr.Append(coll[p]); } } } paramStr.Append(DateTime.Now.ToUniversalTime().ToString("yyyyMMddHHmmss")); paramStr.Append(config.PassKey); #endregion if (Lind.DDD.Utils.Encryptor.Utility.EncryptString(paramStr.ToString(), Lind.DDD.Utils.Encryptor.Utility.EncryptorType.MD5) != request["cipherText"]) { actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden) { Content = new StringContent("验证失败,请求非法", Encoding.GetEncoding("UTF-8")) }; } base.OnActionExecuting(actionContext); } }
在上的配置项大叔把它存储到的XML里,使用的是大叔本身封装的XML缓存组件CacheConfigFile,文件第一次访问会加载到内存,下次使用直接从内存返回,而当文件修改后,文件的最后更新时间发生变化,这时缓存过时,在生产缓存时,仍是采用了单例模式,缓存
这个在大叔框架里常常被看到,呵呵。安全
回到目录服务器