96

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

"{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 }
                });
        }
    }
}
4

4 回答 4

41

在 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] 属性。

修改 web.config 中的值:

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

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

于 2009-07-08T13:38:18.740 回答
30

我认为主要问题是,如果您要搭载内置的 ASP.NET FormsAuthentication 类(并且没有充分的理由不应该这样做),那么一天结束时会调用FormsAuthentication.RedirectToLoginPage()正在发生的事情查看一个配置的 URL。永远只有一个登录 URL,这就是他们设计的方式。

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

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

希望有帮助!

于 2008-12-11T06:16:22.987 回答
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; }
    }
于 2010-07-16T15:35:26.610 回答
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;
        }
    }
}
于 2019-02-08T07:56:17.260 回答