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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/356982/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 10:47:06  来源:igfitidea点击:

How to redirect to a dynamic login URL in ASP.NET MVC

.netasp.net-mvcauthenticationforms-authenticationasp.net-routing

提问by Mike Scott

I'm creating a multi-tenancy web site which hosts pages for clients. The first segment of the URL will be a string which identifies the client, defined in Global.asax using the following URL routing scheme:

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

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

This works fine, with URLs such as /foo/Home/Index.

这工作正常,使用诸如 /foo/Home/Index 之类的 URL。

However, when using the [Authorize] attribute, I want to redirect to a login page which also uses the same mapping scheme. So if the client is foo, the login page would be /foo/Account/Login instead of the fixed /Account/Login redirect defined in web.config.

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

MVC uses an HttpUnauthorizedResult to return a 401 unauthorised status, which I presume causes ASP.NET to redirect to the page defined in web.config.

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

So does anyone know either how to override the ASP.NET login redirect behaviour? Or would it be better to redirect in MVC by creating a custom authorization attribute?

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

EDIT - Answer:after some digging into the .Net source, I decided that a custom authentication attribute is the best solution:

编辑 - 答案:在深入研究 .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 }
                });
        }
    }
}

采纳答案by Nicholas Piasecki

I think the main issue is that if you're going to piggyback on the built-in ASP.NET FormsAuthentication class (and there's no good reason you shouldn't), something at the end of the day is going to call FormsAuthentication.RedirectToLoginPage()which is going to look at the one configured URL. There's only one login URL, ever, and that's just how they designed it.

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

My stab at the problem (possibly a Rube Goldberg implementation) would be to let it redirect to a single login page at the root shared by all clients, say /account/login. This login page wouldn't actually display anything; it inspects either the ReturnUrl parameter or some value I've got in the session or a cookie that identifies the client and uses that to issue an immediate 302 redirect to the specific /client/account/login page. It's an extra redirect, but likely not noticeable and it lets you use the built in redirection mechanisms.

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

The other option is to create your own custom attribute as you describe and avoid anything that calls the RedirectToLoginPage()method on the FormsAuthenticationclass, since you'll be replacing it with your own redirection logic. (You might create your own class that is similar.) Since it's a static class, I'm not aware of any mechanism by which you could just inject your own alternative interface and have it magically work with the existing [Authorize] attribute, which blows, but people have done similar things before.

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

Hope that helps!

希望有帮助!

回答by user134936

In the RTM version of ASP.NET MVC, the Cancel property is missing. This code works with ASP.NET MVC RTM:

在 ASP.NET MVC 的 RTM 版本中,缺少 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 }
                        });
            }
        }
    }
}


Edit:You may want to disable the default forms authentication loginUrl in web.config - in case somebody forgets you have a custom attribute and uses the built in [Authorize] attribute by mistake.

编辑:您可能希望在 web.config 中禁用默认表单身份验证 loginUrl - 以防有人忘记您拥有自定义属性并错误地使用内置 [Authorize] 属性。

Modify the value in web.config:

修改 web.config 中的值:

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

Then make an action method 'ERROR' that logs an error and redirects the user to the most generic login page you have.

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

回答by Kieron

My solution to this problem was a custom ActionResultclass:

我对这个问题的解决方案是一个自定义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; }
    }

回答by turdus-merula

Still, if one decides to use the built-in ASP.NET FormsAuthentication, one can overide Application_AuthenticateRequestin Global.asax.csas follows:

不过,如果你已经决定使用内置的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;
        }
    }
}