原文地址:ASP.NET MVC: Securing Your Controller Actions
原文作者:Rob Conery
本文地址:http://www.cnblogs.com/QLeelulu/archive/2008/04/04/1137580.html
譯者:QLeelulu
首先,希望大家以後關於ASP.NET MVC的文章的Tag統一使用:aspnetmvc (之前我也一直是使用"ASP.NET MVC"的,呵)
The ActionFilterAttribute
ASP.NET MVC Preview 2 引入了新的ActionFilterAttribute,這使你可以在控制器中為你的Action聲明"Filter Attributes"。想一下這個,實質上,就好像"OnLoad"事件—— 一小段代碼在你的Action之前或者之後執行。
使用ActionFilterAttribute 是非常簡單的。你聲明一個類然後重寫基類的一個或者兩個方法:
在這裡你可以看到我們有兩個方法可以重寫:
- OnActionExecuting: 在調用Action方法前執行(例如可以做為身分識別驗證)
- OnActionExecuted: 在調用Action方法後執行(例如日誌記錄)
當你建立你的Filter,你此外也可能得訪問FilterExecutingContext -一個非常漂亮的對象,它會告訴你當前的app會發生些什麼:
就算你不是很熟悉Attributes,你也可以聲明屬性,當你使用Attribute你可以為改屬性設值:
你可以好像這樣設定屬性的值:
這將"MyFilterProperty"設定為"SomeSetting",你可以在你的OnActionExcuting方法中使用這個屬性。
Putting It Together
我們想要做的是在調用Action之前先檢查當前的使用者是否已經登入,如果沒有登入我們就將他重新導向到登入頁,並追加一個讓他可以返回的當前頁的URL。
為什麼使用一個Filter而不是好像Webforms一樣使用Web.config ?原因是你不可以準確的鎖定一個Action來使用Forms認證,在之前,Phil 討論了這個並做了很好的解釋:
====>>>>>>
假如你有一個website,而你想阻止未經驗證的使用者訪問Admin檔案夾。做為一個標準的網站,一個途徑是可以將下面的Web.config檔案放到Admin檔案夾...中:
<?xml version="1.0"?><configuration> <system.web> <authorization> <deny users="*" /> </authorization> </system.web></configuration>
嘗試導航到Admin目錄下你會得到一個拒絕訪問的錯誤。然而,你可能會好像下面一樣使用一個實現了WebFormRouteHandler 的連結將URL fizzbucket映射到Admin目錄下:
RouteTable.Routes.Add(new Route("fizzbucket", new WebFormRouteHandler("~/admin/secretpage.aspx"));
現在,一個請求 /fizzbucket 的URL將會顯示Admin目錄下的 secretpage.aspx。這可能是你一直想要的。不過,這可能並不能達到你想要的效果。(原文:Now, a request for the URL /fizzbucket will display secretpage.aspx in the admin directory. This might be what you want all along. Then again, it might not be.)
<<<<<<====
這裡的要點是Urls不是映射到Pages和目錄 而且 有不止一種途徑來Skin An Action(不知道怎麼翻譯,skim?) ——你可以容易地在底層用你的routes來映射,而且會不經意的開啟一個連結到受安全保護的頁面的URL(原文:you can easily shoot yourself in the foot with your routes, and inadvertantly open up a Url to a secured page)。構建安全的Action始終是更好的選擇。
指出所有的這些,我們現在可以開始構建我們新的Filter來檢查身分識別驗證了-the RequiresAuthentication Filter:
/// <summary> /// 使用FormsAuthentication來檢查使用者的身分識別驗證 /// 並在驗證失敗時重新導向到登入頁 /// </summary> public class RequiresAuthenticationAttribute : ActionFilterAttribute { public override void OnActionExecuting(FilterExecutingContext filterContext) { //redirect if not authenticated if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { //use the current url for the redirect string redirectOnSuccess = filterContext.HttpContext.Request.Url.AbsolutePath; //send them off to the login page string redirectUrl = string.Format("?ReturnUrl={0}", redirectOnSuccess); string loginUrl = FormsAuthentication.LoginUrl + redirectUrl; filterContext.HttpContext.Response.Redirect(loginUrl, true); } } }
使用這個是非常的簡單的——只需將它附加到你想要受安全保護的Action方法中就可以了:
另外一個很有用的Filter是用來檢查特定的角色:
/// <summary>/// 使用FormsAuthentication來檢查使用者的角色/// 如果未授權的時候會拋出一個 UnauthorizedAccessException 異常/// </summary>public class RequiresRoleAttribute : ActionFilterAttribute { public string RoleToCheckFor { get; set; } public override void OnActionExecuting(FilterExecutingContext filterContext) { //redirect if the user is not authenticated if (!String.IsNullOrEmpty(RoleToCheckFor)) { if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { //use the current url for the redirect string redirectOnSuccess = filterContext.HttpContext.Request.Url.AbsolutePath; //send them off to the login page string redirectUrl = string.Format("?ReturnUrl={0}", redirectOnSuccess); string loginUrl = FormsAuthentication.LoginUrl + redirectUrl; filterContext.HttpContext.Response.Redirect(loginUrl, true); } else { bool isAuthorized = filterContext.HttpContext.User.IsInRole(this.RoleToCheckFor); if (!isAuthorized) throw new UnauthorizedAccessException("You are not authorized to view this page"); } } else { throw new InvalidOperationException("No Role Specified"); } } }
注意我在這裡傳了一個字串用來檢查角色:
你可以修改這個為一個枚舉、常量、或者其他的。你也可以將他修改為具有更多的行為——例如 "RequiresUserCanEditPage" 並在這個方法中確保使用者為 "Administrators"或者 "Content Editors".這裡的point是由你決定的。
PS:汗一個,英文好菜,好幾個地方不知道怎麼翻譯。Orz....