最近在与一个IOS应用作接口对接,以前一直都没有遇到什么很大的问题,可是有一天发现能够经过软件解析app的url,而后直接经过url的拼接修改接口数据,这一下使得数据的安全性和准确性都下降了,因而就想到了url加密。web
而后在网上查了一下url的加密算法,使用比较广泛的仍是Base64的加密,可是对于如何实现加密,网上的资料确很少,多是我搜索的关键词不对。既然没有现成的参考文件,那么就只能靠本身了。由于全部的Controller都继承一个基Controller,因此比较天然的想到在基Controller中作一些操做,因为须要在执行具体的Action以前对url中的参数进行解密处理,因此联想到了作Asp.net项目时使用的IHttpModule接口,不过MVC有个更好的功能,那就是过滤器Filter,mvc总共提供了四种默认的Filter接口,IAuthorizationFilter、IActionFilter、IResultFilter和IExceptionFilter,关于这四种Filter的执行时间和使用方法网络上有不少,这里就不赘述了。下面就个人摸索过程作一个说明,也供你们参考,若是你们有更好的方法,还望不吝告知。
算法
要想可以解密Url的参数,首先须要获取的HttpRequest传递过来的参数。首先建立一个Filter,我暂且命名为DecodeUrlFitler,继承至ActionFilterAttribute,这个类已经继承了IActionFilter接口,它有四个抽象方法,分别是OnActionExecuted(在action执行完后执行)、OnActionExecuting(在action执行前执行)、OnResultExecuted(在view视图渲染以后执行)、OnResultExecuting(在view视图渲染以前执行)。很明显,咱们须要重写OnActionExecuting方法,在action执行以前,将url中的参数进行解密。浏览器
第一步:获取Url中的查询参数安全
获取查询参数后,若是你仔细观察,会发现Base64格式的参数有时是通过UrEncode的,因此为了以后可以准确的进行Base64的解码,咱们须要将参数进行UrlDecode处理。网络
public class AppActionFilter : ActionFilterAttribute { public override void OnActionExecuting( ActionExecutingContext filterContext ) { HttpRequestBase bases = (HttpRequestBase) filterContext.HttpContext.Request; string url = bases.RawUrl.ToString().ToLower();
//获取url中的参数 string queryString = bases.QueryString.ToString();
//对获取到的参数进行UrlDecode处理
queryString = HttpUtility.UrlDecode(queryString);
} }
获取参数和处理在博客园如今有不少文章都介绍了,在msdn中查看一下类型的方法,上面的代码就能够很容易写出来,比较困难的是如何将解析后的url参数替换以前的参数,而后跳转到相应的action中,而后将执行的结果返回到客户端。我在这个问题上摸索了很久,最终找到了比较好的一个方法,下面就来讲说我摸到的几块石头。mvc
第二步:url的跳转app
第一块石头:使用RedirectResultdom
一开始想到的是从新拼url,将解析出来的参数拼接成一个完整的url,获取url的路径可使用HttpRequestBase的FilePath属性获取到路径,而后获取到domain,在加上解密的queryString就能够拼接成一条完整的url了。可是若是你查看浏览器的报文,会发现这实际上是进行了一个url的重定向,若是这样的话,咱们url加密的目的就没有实现了,url重定向,会将解密的url传递到客户端,这就让咱们的url暴露了,这彻底和咱们的设想相反,果断放弃。ide
第二块石头:使用IHttpHandler加密
以后查资料时,有提到使用IhttpHandler的ProcessRequest处理web请求的。
filterContext.RequestContext.HttpContext.RewritePath(url);//url为虚拟路径 IHttpHandler httpHandler = new MvcHttpHandler(); httpHandler.ProcessRequest(System.Web.HttpContext.Current);
这样也能够达到目的,可是若是你的action参数有非string类型的,那么在执行这个方法时会报错,虽然你看不到,可是你在Global.asax.cs的Application_Error方法中使用Server.GetLastError().GetBaseException();捕获异常,你会发现相似xxxxxx方法须要的int类型的参数,可是传递过来的是string类型等等。功能虽然实现了,可是看着就是不爽,因此继续摸索下一个方案。
第三块石头:使用ActionParameters修改context的参数
写代码就是要要耐心,通过我漫长的摸索,我发现没有加密的url时,ActionExecutingContext的ActionParameters属性就是url的查询参数集合,是一个Dictionary<string,object>的类型;可是若是对url进行加密,ActionParameters的参数集合里面只有key,没有value,因此我就想,能不能经过修改ActionParameters里面的值,而后在带调用其父类ActionFilterAttribute的OnActionExecuting方法,话很少说,贴出实现的代码。
//获取访问Action参数的描述,主要是参数的类型和参数名称 ParameterDescriptor[] pds = filterContext.ActionDescriptor.GetParameters(); 2 3 //从新填充参数 4 string paramName = ""; 5 string paramValue = ""; 6 foreach (string param in parameters) 7 { 8 paramName = param.Split('=')[0]; 9 paramValue = HttpUtility.UrlDecode(param.Split('=')[1]); 10 foreach (ParameterDescriptor pd in pds) 11 { 12 if (paramName == pd.ParameterName) 13 { 14 //判断参数的类型,若是是整形的数据,那么将参数转换成整形数据 15 if (pd.ParameterType.Name.ToLower() == "int32" || pd.ParameterType.Name.ToLower() == "nullable`1") 16 { 17 filterContext.ActionParameters.Add(paramName, Convert.ToInt32(paramValue)); 18 } 19 else 20 { 21 filterContext.ActionParameters.Add(paramName, paramValue); 22 } 23 break; 24 } 25 } 26 27 } 28 } 29 base.OnActionExecuting(filterContext);
不过下面和你们分享一下,使用参数替换过程当中遇到的问题和值得注意的几点。
一、在添加参数以前,必定要先使用Clear()方法清楚默认生成的参数,否则从新添加参数时,会出现“字典中已经存在此key的值”;还有一种的方法就是遍历传递过来的参数和ActionParameters的中的参数,替换参数的值。
二、第二点要注意的是参数的类型,参数的类型和名称能够经过ActionDestriptor方法获取,若是传递的参数类型与Action定义的参数类型不一致,会引起参数类型不一致的异常。
三、最后要注意的可空类型的参数,若是action的参数饱含可空类型的非空类型的参数,当可空参数有值时,那么其他的全部参数都要传递,而且赋值。最简单的办法就是遍历ActionDestriptor的参数,将全部的参数都加到ActionParameters中并附上值。
//若是饱含可空参数,那么须要不可空的而且不在请求参数列表中的参数添加到参数列表,不然会报错 30 foreach (ParameterDescriptor pd in pds) 31 { 32 if (!filterContext.ActionParameters.Keys.Contains(pd.ParameterName)) 33 { 34 if (pd.ParameterType.Name.ToLower() == "nullable`1") 35 { 36 filterContext.ActionParameters.Add(pd.ParameterName, null); 37 } 38 else if (pd.DefaultValue == null) 39 { 40 filterContext.ActionParameters.Add(pd.ParameterName, ""); 41 } 42 else 43 { 44 filterContext.ActionParameters.Add(pd.ParameterName, pd.DefaultValue); 45 } 46 } 47 }
还有一种方法是构建路由表,不过我没有尝试过,有兴趣的能够试下。
若是你们有更好的方法和建议,欢迎更贴拍砖。