阿不

不抛弃,不放弃

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  158 随笔 :: 0 文章 :: 2109 评论 :: 67 引用

在ASP.NET MVC中,如何来实现表单的验证与授权访问呢?至少在CTP3中,还没有一个官方的解决方案。ASP.NET WebForm的表单验证和授权机制是否适合在ASP.NET MVC中使用呢?带着这些问题来进入我们今天的主题。

在ASP.NET WebForm的架构下,我们可以通过一定的配置即可实现用户身份验证和授权。特别是在ASP.NET 2.0的Membership功能的支撑下,可以做到更加简洁可复用的用户验证系统。通过web.config可以做到对页面或目录对不同用户身份可见性的定制,但是它是基于物理文件和目录。而在ASP.NET MVC架构下,用户访问的每一个页面在磁盘中并没有一个固定的物理文件,它是通过Controller控制数据与视图的组合来生成HTML代码,进而向客户端输出。那么我们该如何来复用已有的表单验证授权机制呢?

在MVC中,请求的功能入口是Controller相应的Action函数,我们可以在函数执行前去控制请求权限。在ASP.NET MVC Preview 2后,提供了一个机制让我们可以对Action的AOP拦截,这个接口定义如下:

   1: public interface IActionFilter
   2: {
   3:     void OnActionExecuted(ActionExecutedContext filterContext);
   4:     void OnActionExecuting(ActionExecutingContext filterContext);
   5:     void OnResultExecuted(ResultExecutedContext filterContext);
   6:     void OnResultExecuting(ResultExecutingContext filterContext);
   7: }

我们有两种方式来实现拦截,一种我们可以通过定义Attribute来实现拦截的功能,在System.Web.Mvc程序集中有一个ActionFilterAttribute抽象类,通过重写这个抽象类的这些虚方法,我们就可以实现对特定的执行过程进行拦截。

另一种方法,我们注意到Controller这个类也实现了IActionFilter这个接口,并且也提供了这四个函数的虚拟方法定义。框架内部,在调用Action方法的时候同时来调用这些拦截方法。具体的可以参考:ControllerActionInvoker 这个类的实现,所有的Action的调用都在这个类当中被实现。所以我们只要重写Controller里这四个虚方法,也可完成本Controller面的所有Action的拦截。

在这里,我也找到了国外友人已经实现好的基于角色的MVC权限控制的方案。自定义了两个自定义Attribute,分别为:RequiresAuthenticationAttribute和RequiresRoleAttribute。通过这两个Attribute来可以作用于Class和Method,用标记哪些Controller或Action需要登录后,或者需要拥有哪些角色才能执行。如果用户没有拥有访问当然Controller或Action权限的时候,就会自动被重定向到登录页面去。下面是两个类的定义:

/// <summary>
/// Checks the User's authentication using FormsAuthentication
/// and redirects to the Login Url for the application on fail
/// </summary>
[RequiresAuthentication]
public class RequiresAuthenticationAttribute : ActionFilterAttribute
{
 
    public override void OnActionExecuting(ActionExecutingContext 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);
 
        }
    }
}
 
/// <summary>
/// Checks the User's role using FormsAuthentication
/// and throws and UnauthorizedAccessException if not authorized
/// </summary>
public class RequiresRoleAttribute : ActionFilterAttribute
{
 
    public string RoleToCheckFor { get; set; }
 
    public override void OnActionExecuting(ActionExecutingContext 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");
        }
    }
}

如上所介绍的两种方法,我们一样可以定义一个Controller基类,通过拦截来进行权限的控制。但是与定义Attribute相比,手法并不是很好,也不利于通用化。但是就理论上的性能来说,会比Attribute更好。

到目前为止,ASP.NET MVC还没有更新的消息,我想在正式版本的ASP.NET MVC框架,权限控制问题会有一个官方说法。希望到时候会有一种更为灵活和可配置的方案。也许通过控制Url来控制访问权限也是一种可行的方案,会不会集成到RouteTable里面呢?让我们试目以待吧。

上一篇:ASP.NET MVC :从ASP.NET WebForm到ASP.NET MVC技术上的共用和差异

广告:我的基于MVC的网站

阿不 http://hjf1223.cnblogs.com
Tag标签: mvc
posted on 2008-07-09 22:22 阿不 阅读(2151) 评论(15)  编辑 收藏 所属分类: .Net相关技术

评论

#1楼 [楼主] 2008-07-09 22:22 阿不      
国外友人的实现:
http://blog.wekeroad.com/blog/aspnet-mvc-securing-your-controller-actions/
  回复  引用  查看    

兄台居然“自沙”,实在过分啊。
  回复  引用  查看    

#3楼  2008-07-10 08:12 雅阁布      
up!!!!
  回复  引用  查看    

#4楼  2008-07-10 08:54 henry      
实在不喜欢这两种方式,在用MonoRail就感觉不爽.为什么要在controller或action中加上filterattribute,为什么不在filter里描述这个filter的应用范围对象,非要在原来的地方搞上一腿.

  回复  引用  查看    

#5楼 [楼主] 2008-07-10 09:05 阿不      
@henry
如果能在Url级别上来进行权限控制,肯定会更灵活。
  回复  引用  查看    

#6楼  2008-07-10 11:10 SZW      
@阿不
我之前也试过在Controller上面直接加一个OnActionExecuting,使下面的Action不用每个都标记属性,但是发现有一个问题,程序在进入Controller和Action的时候,会对这个验证分别运行一次(虽然从程序运行逻辑上来说合情合理),也就说在使用了这个验证(比如RequiresRoleAttribute)的时候,一个请求要读两次数据库(暂不考虑缓存),这个问题有办法解决吗?
  回复  引用  查看    

#7楼 [楼主] 2008-07-10 11:31 阿不      
@SZW
你确定吗?我在我这边试了下。只在Controller中标记,Attribute并不会被执行两次啊。

这是第2000篇评论,好好利用。

我再仔细看了下代码,发现确实是不会被执行两次,除非你在Controller和Action都标记了Attribute。你可以去看: GetAllActionFilters 函数,这里会去找出Action的所有Atrribute,包括它的DeclaringType,也就是Contoller类型。还要注意:Type是从MemberInfo继承下来的。这也就是 GetActionFiltersForMember的参数用MemberInfo的原因。
  回复  引用  查看    

#8楼  2008-07-10 16:22 Q.Lee.lulu      
--引用--------------------------------------------------
阿不: 如果能在Url级别上来进行权限控制,肯定会更灵活。
--------------------------------------------------------
不懂.在MVC中(这里指纯粹的MVC),URL都是被映射到控制器的Action中来的,所以我认为在控制器中进行权限控制是很适合的,无论你什么URL,只要你访问的是这个控制器,就要进行权限判断.这不是更灵活吗 ?
  回复  引用  查看    

#9楼 [楼主] 2008-07-10 16:42 阿不      
@ Q.Lee.lulu
我是这样想的,我们可以通过Url来控制访问权限,并且是在Contoller被实例之前就被验证。而且,希望是可配置的,在web.config中可配置。
  回复  引用  查看    

#10楼  2008-07-14 14:49 SZW      
@阿不
新打开一个页面不会,你保持Attribute中的断点,刷新一下看看。
我也是偶然的一次调试中发现这个问题的,今天发现新打开的不会这样,也算松了半口气,哈哈。
  回复  引用  查看    

#11楼  2008-07-14 15:06 SZW      
我大概想的和Q.Lee.lulu有点接近,哪怕web.config中可以设置,在一些时候也很笨拙。其中一个原因是MVC的Controller即使路径上面“外貌相仿”,但是实际出现的个数可能要远远大于WebForm中的文件夹个数,而且表面上看来是比较零乱的,这里面维护起来压力很大(当然我们也可以按照Home,User,Admin这样的分类,不过很多时候我们还是会按照Products,Customers——貌似也是DynamicData应用的趋势,这里面的Action权限可能是细分的,在Web.config里面也能做到,但做起来就比较累人了),除此之外,这样基本上限定了Controller必须在Url第一节的用法,似乎有点太“过分”了。当然这种情况是可以人为控制的。全部在Controller里面控制我也不觉得是最好的选择,倘若能够在Web.config里面针对Controller和Action的一些规律进行判断的话就更好了。
  回复  引用  查看    

#12楼 [楼主] 2008-07-15 17:26 阿不      
@ SZW
你可能是刷新了两次了,自然就执行两次了
  回复  引用  查看    

#13楼 [楼主] 2008-07-15 17:27 阿不      
@SZW
我觉得可以这样:
把RouteTable做到可配置的,不要在程序中写死,做成一个可配置文件。然后在配置文件中,配置所有的Controller,Action和参数,还有访问权限等。
  回复  引用  查看    

#14楼  2008-07-19 14:28 SZW      
@阿不
--引用--------------------------------------------------
阿不: @SZW<br>我觉得可以这样:<br> 把RouteTable做到可配置的,不要在程序中写死,做成一个可配置文件。然后在配置文件中,配置所有的Controller,Action和参数,还有访问权限等。
--------------------------------------------------------
是个不错的方法!

我只是按了F5,没有刷新两次阿,而且是在设断点的情况下,Attribute中的代码连续运行了两次
  回复  引用  查看    

#15楼  2008-07-29 15:12 汤丽珠 [未注册用户]
好多人看啊
  回复  引用    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-07-09 22:30 编辑过


相关链接: