最近在與一個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 }
還有一種方法是構建路由表,不過我沒有嘗試過,有興趣的能夠試下。
若是你們有更好的方法和建議,歡迎更貼拍磚。