【翻译】ASP.NET MVC : 构建安全的控制器Action

原文地址: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 是非常简单的。你声明一个类然后重写基类的一个或者两个方法:

Filter1

在这里你可以看到我们有两个方法可以重写:

  • OnActionExecuting: 在调用Action方法前执行(例如可以做为身份验证)
  • OnActionExecuted: 在调用Action方法后执行(例如日志记录)

当你创建你的Filter,你此外也可能得访问FilterExecutingContext -一个非常漂亮的对象,它会告诉你当前的app会发生些什么:

Filter2

就算你不是很熟悉Attributes,你也可以声明属性,当你使用Attribute你可以为改属性设值:

Filter3Prop

你可以好像这样设置属性的值:

SettingMyFilter

SettingMyFilter2

这将"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方法中就可以了:

finished

另外一个很有用的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");
            }
        }
    }

注意我在这里传了一个字符串用来检查角色:

AdminCheck

你可以修改这个为一个枚举、常量、或者其他的。你也可以将他修改为具有更多的行为——例如 "RequiresUserCanEditPage" 并在这个方法中确保用户为 "Administrators"或者 "Content Editors".这里的point是由你决定的。

 

PS:汗一个,英文好菜,好几个地方不知道怎么翻译。Orz....

posted on 2008-04-04 17:29  Q.Lee.lulu  阅读(4258)  评论(7编辑  收藏  举报