91

我正在创建一个承载客户端页面的多租户网站。 URL的第一部分将是一个字符串,识别客户端,使用下面的URL路由方案在Global.asax中定义:如何重定向到ASP.NET MVC中的动态登录URL

"{client}/{controller}/{action}/{id}" 

这工作得很好,用的URL如/富/首页/索引。

但是,当使用[Authorize]属性时,我想重定向到也使用相同映射方案的登录页面。因此,如果客户端是foo,登录页面将是/ foo/Account/Login,而不是web.config中定义的固定/帐户/登录重定向。

MVC使用HttpUnauthorizedResult返回401未授权状态,我认为这会导致ASP.NET重定向到web.config中定义的页面。

那么有谁知道如何重写ASP.NET登录重定向行为?或者,通过创建一个自定义授权属性在MVC中重定向会更好吗?

编辑 - 答:经过一番挖掘到.NET源,我决定自定义验证属性是最好的解决办法:

public class ClientAuthorizeAttribute: AuthorizeAttribute 
{ 
    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     base.OnAuthorization(filterContext); 

     if (filterContext.Cancel && filterContext.Result is HttpUnauthorizedResult) 
     { 
      filterContext.Result = new RedirectToRouteResult(
       new RouteValueDictionary 
       { 
        { "client", filterContext.RouteData.Values[ "client" ] }, 
        { "controller", "Account" }, 
        { "action", "Login" }, 
        { "ReturnUrl", filterContext.HttpContext.Request.RawUrl } 
       }); 
     } 
    } 
} 
+2

具有路由做几乎同样的事情,所以我需要这个!谢谢! – 2008-12-27 21:20:41

+0

谢谢,我试图弄清楚如何做类似的事情。 – Chance 2010-02-03 02:34:06

+0

它给了我自己实现的想法,非常感谢! – 2010-10-31 16:47:08

回答

30

我认为主要的问题是,如果你打算以搭载内置的ASP.NET FormsAuthentication类(并且没有理由不应该这样做),那么在一天结束时将调用FormsAuthentication.RedirectToLoginPage()这将查看一个配置的URL。以前只有一个登录网址,这就是他们设计的方式。

我对这个问题(可能是Rube Goldberg实现)的攻击是让它重定向到所有客户端共享的根目录中的单个登录页面,比如/ account/login。此登录页面实际上不会显示任何内容;它会检查ReturnUrl参数或我在会话中获得的某个值,或者是一个标识客户端的cookie,并使用它来立即发送302到指定/客户端/帐户/登录页面的重定向。这是一个额外的重定向,但可能不明显,它允许您使用内置的重定向机制。

另一种选择是按照您的描述创建自己的自定义属性,并避免在FormsAuthentication类中调用RedirectToLoginPage()方法的任何内容,因为您将用自己的重定向逻辑替换它。 (你可以创建自己的类。)由于它是一个静态类,我没有意识到任何机制,你可以注入自己的替代接口,并让它神奇地与现有的[Authorize]属性一起工作,吹,但people have done similar things before

希望有帮助!

40

在RTM版本的ASP.NET MVC中,Cancel属性丢失。该代码可以使用ASP.NET MVC RTM:

using System; 
using System.Web; 
using System.Web.Mvc; 
using System.Web.Mvc.Resources; 

namespace ePegasus.Web.ActionFilters 
{ 
    public class CustomAuthorize : AuthorizeAttribute 
    { 
     public override void OnAuthorization(AuthorizationContext filterContext) 
     { 
      base.OnAuthorization(filterContext); 
      if (filterContext.Result is HttpUnauthorizedResult) 
      { 
       filterContext.Result = new RedirectToRouteResult(
        new System.Web.Routing.RouteValueDictionary 
         { 
           { "langCode", filterContext.RouteData.Values[ "langCode" ] }, 
           { "controller", "Account" }, 
           { "action", "Login" }, 
           { "ReturnUrl", filterContext.HttpContext.Request.RawUrl } 
         }); 
      } 
     } 
    } 
} 

编辑:您可能希望禁用web.config中的默认窗体身份验证loginUrl - 万一有人忘记你有一个自定义属性和用途错误地内置了[Authorize]属性。

修改网页中的值。配置:

<forms loginUrl="~/Account/ERROR" timeout="2880" /> 

然后制定行动方法“错误”是记录错误,并将用户重定向到您拥有最通用的登录页面。

2

我对这个问题的解决是一个自定义ActionResult类:

sealed public class RequiresLoginResult : ActionResult 
    { 
     override public void ExecuteResult (ControllerContext context) 
     { 
      var response = context.HttpContext.Response; 

      var url = FormsAuthentication.LoginUrl; 
      if (!string.IsNullOrWhiteSpace (url)) 
       url += "?returnUrl=" + HttpUtility.UrlEncode (ReturnUrl); 

      response.Clear(); 
      response.StatusCode = 302; 
      response.RedirectLocation = url; 
     } 

     public RequiresLoginResult (string returnUrl = null) 
     { 
      ReturnUrl = returnUrl; 
     } 

     string ReturnUrl { get; set; } 
    }