利用MVC的過濾器實現url的參數加密和解密

  最近在與一個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                     }

 

  還有一種方法是構建路由表,不過我沒有嘗試過,有興趣的能夠試下。   

  若是你們有更好的方法和建議,歡迎更貼拍磚。

相關文章
相關標籤/搜索