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


  最近在與一個IOS應用做接口對接,之前一直都沒有遇到什么很大的問題,但是有一天發現可以通過軟件解析app的url,然后直接通過url的拼接修改接口數據,這一下使得數據的安全性和准確性都降低了,於是就想到了url加密。

  然后在網上查了一下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中,然后將執行的結果返回到客戶端。我在這個問題上摸索了好久,最終找到了比較好的一個方法,下面就來說說我摸到的幾塊石頭。

第二步:url的跳轉

第一塊石頭:使用RedirectResult

  一開始想到的是重新拼url,將解析出來的參數拼接成一個完整的url,獲取url的路徑可以使用HttpRequestBase的FilePath屬性獲取到路徑,然后獲取到domain,在加上解密的queryString就可以拼接成一條完整的url了。但是如果你查看瀏覽器的報文,會發現這其實是進行了一個url的重定向,如果這樣的話,我們url加密的目的就沒有實現了,url重定向,會將解密的url傳遞到客戶端,這就讓我們的url暴露了,這完全和我們的設想相反,果斷放棄。

第二塊石頭:使用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);

 

  不過下面和大家分享一下,使用參數替換過程中遇到的問題和值得注意的幾點。

  1、在添加參數之前,一定要先使用Clear()方法清楚默認生成的參數,不然重新添加參數時,會出現“字典中已經存在此key的值”;還有一種的方法就是遍歷傳遞過來的參數和ActionParameters的中的參數,替換參數的值。

  2、第二點要注意的是參數的類型,參數的類型和名稱可以通過ActionDestriptor方法獲取,如果傳遞的參數類型與Action定義的參數類型不一致,會引發參數類型不一致的異常。

  3、最后要注意的可空類型的參數,如果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                     }

 

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

  如果大家有更好的方法和建議,歡迎更貼拍磚。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM