如何在ASP.NET MVC中重定向到动态登录URL


96

我正在创建一个多租户网站,为客户托管页面。URL的第一段将是标识客户端的字符串,该字符串使用以下URL路由方案在Global.asax中定义:

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

使用/ foo / Home / Index之类的URL可以正常工作。

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

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
用路由做几乎完全相同的事情,所以我需要这个!谢谢!
特雷弗·德Koekkoek

谢谢,我试图弄清楚如何做类似的事情。
机遇

它给了我实现自己的想法,非常感谢!
Alexander Beletsky

3
一定要设置面积= NULL(或正确的区域),如果使用MVC 2及以上的-否则会得到从页面继承您试图访问
Simon_Weaver

没有MVC可以做到这一点吗?
DARKGuy 2014年

Answers:


30

我认为主要的问题是,如果你要捎带上内置的ASP.NET FormsAuthentication类(有没有很好的理由,你不应该),东西在一天结束时会调用FormsAuthentication.RedirectToLoginPage()这是会查看一个已配置的URL。永远只有一个登录URL,这就是他们设计的方式。

我对这个问题(可能是Rube Goldberg实现)的追求是让它重定向到所有客户端共享的根目录下的单个登录页面,例如/ account / login。该登录页面实际上不显示任何内容。它检查ReturnUrl参数或我在会话中获得的某个值或标识客户端的cookie,并使用该cookie发出立即302重定向到特定的/ client / account / login页面。这是一个额外的重定向,但可能不会引起注意,它使您可以使用内置的重定向机制。

另一种选择是在描述时创建自己的自定义属性,并避免RedirectToLoginPage()FormsAuthentication类上调用任何方法,因为您将用自己的重定向逻辑替换它。(您可能会创建类似的自己的类。)由于它是静态类,所以我不知道有什么机制可以注入您自己的备用接口并使之神奇地与现有的[Authorize]属性一起使用,打击,但人们以前做过类似的事情

希望有帮助!


这可能是最安全的方法。创建自己的[MyAuthorize]属性很危险。除非您的构建正在验证人们没有使用内置的[Authorize]属性,否则您冒着人们(或您自己)忘记并使用错误的属性的风险
Simon_Weaver

在某些情况下,覆盖可能会有所帮助Application_AuthenticateRequest(请参阅下面的答案)。
turdus-merula

41

在ASP.NET MVC的RTM版本中,缺少“取消”属性。此代码可与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]属性。

修改web.config中的值:

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

然后,创建一个记录错误的操作方法“ ERROR”,并将用户重定向到您拥有的最通用的登录页面。


2
如果使用MVC 2及更高版本,请确保将{area,null}添加到字典(或您所说的任何区域)中-否则它将从您尝试访问的页面继承而来
Simon_Weaver

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; }
    }

0

不过,如果你已经决定使用内置的ASP.NET FormsAuthentication,一个可以在此改变Application_AuthenticateRequestGlobal.asax.cs,如下所示:

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    string url = Request.RawUrl;

    if (url.Contains(("Account/Login"))
    {
        return;
    }

    if (Context.User == null)
    {
        // Your custom tenant-aware logic
        if (url.StartsWith("/foo"))
        {
            // Your custom login page.
            Response.Redirect("/foo/Account/Login");
            Response.End();
            return;
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.