C# ASP.NET MVC - 如何在登录页面上显示未经授权的错误?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1498727/
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-08-06 18:10:14  来源:igfitidea点击:

ASP.NET MVC - How to show unauthorized error on login page?

c#.netasp.netasp.net-mvcauthorization

提问by Ronnie Overby

In my ASP.NET MVC app, I have most controllers decorated with

在我的 ASP.NET MVC 应用程序中,我的大多数控制器都装饰有

[Authorize(Roles="SomeGroup")]

When a user is not authorized to access something, they are sent to "~/Login" which is the Login action on my Account controller.

当用户无权访问某些内容时,他们将被发送到“~/Login”,这是我帐户控制器上的登录操作。

How can I determine that a user has reached the login page because of not being authorized so that I can show an appropriate error?

如何确定用户由于未获得授权而已到达登录页面,以便显示适当的错误?

采纳答案by Ben Scheirman

You can look for the ?ReturnUrl=querystring value, or you can create your own authorization filter & set a field in TempDataindicating the reason.

您可以查找?ReturnUrl=查询字符串值,或者您可以创建自己的授权过滤器并设置一个字段来TempData指示原因。

Here is a simple custom filter that will do the trick:

这是一个简单的自定义过滤器,可以解决问题:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{

    // NOTE: This is not thread safe, it is much better to store this
    // value in HttpContext.Items.  See Ben Cull's answer below for an example.
    private bool _isAuthorized;

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
    {
        _isAuthorized = base.AuthorizeCore(httpContext);
        return _isAuthorized;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if(!_isAuthorized)
        {
            filterContext.Controller.TempData.Add("RedirectReason", "Unauthorized");
        }
    }
}

Then in your view, you can do something like this:

然后在您看来,您可以执行以下操作:

@if(TempData["RedirectReason"] == "Unauthorized")
{
    <b>You don't have permission to access that area</b>
}

(Though I'd recommend a better approach than these magic strings, but you get the point)

(虽然我推荐一种比这些魔法字符串更好的方法,但你明白了)

回答by Ben Cull

UPDATE (Jun 2015):@daniel-lidstr?m has correctly pointed out that you should not use Response.Redirect in an ASP.NET MVC application. For more information about why, please see this link: Response.Redirect and ASP.NET MVC – Do Not Mix.

更新(2015 年 6 月):@daniel-lidstr?m 已正确指出您不应在 ASP.NET MVC 应用程序中使用 Response.Redirect。有关原因的更多信息,请参阅此链接:Response.Redirect 和 ASP.NET MVC – 不要混合

UPDATE (Sep 2014):I'm not sure when HandleUnauthorizedRequest was added to the AuthorizeAttribute, but either way I've been able to refine the AuthorizeRedirect code into something smaller and simpler.

更新(2014 年 9 月):我不确定何时将 HandleUnauthorizedRequest 添加到 AuthorizeAttribute,但无论哪种方式,我都能够将 AuthorizeRedirect 代码细化为更小更简单的代码。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeRedirect : AuthorizeAttribute
{
    public string RedirectUrl = "~/Error/Unauthorized";

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        base.HandleUnauthorizedRequest(filterContext);

        if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.Result = new RedirectResult(RedirectUrl);
        }
    }
}


Original Answer Below (still fully functional)

下面的原始答案(仍然功能齐全)

I've left this answer here as it still gives you an insight as to how the Authorization pipeline works.

我把这个答案留在这里,因为它仍然让您深入了解授权管道的工作原理。

For anyone still landing here, I've edited Ben Scheirman's answer to automatically redirect to an unauthorized page when the user is logged in but not authorized. You can change the redirect path using the name parameter RedirectUrl.

对于仍然在这里登陆的任何人,我已经编辑了 Ben Scheirman 的答案,以便在用户登录但未授权时自动重定向到未经授权的页面。您可以使用名称参数 RedirectUrl 更改重定向路径。

EDIT:I've made the solution thread-safe thanks to the advice of Tarynnand MSDN

编辑:由于TarynnMSDN的建议,我已经使解决方案线程安全

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeRedirect : AuthorizeAttribute
{
    private const string IS_AUTHORIZED = "isAuthorized";

    public string RedirectUrl = "~/error/unauthorized";

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
    {
        bool isAuthorized = base.AuthorizeCore(httpContext);

        httpContext.Items.Add(IS_AUTHORIZED, isAuthorized);

        return isAuthorized;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        var isAuthorized = filterContext.HttpContext.Items[IS_AUTHORIZED] != null 
            ? Convert.ToBoolean(filterContext.HttpContext.Items[IS_AUTHORIZED]) 
            : false;

        if (!isAuthorized && filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.RequestContext.HttpContext.Response.Redirect(RedirectUrl);
        }
    }
}

回答by divide_byzero

If you have a controller and don't want to have a url in you code you can redirect this way as well. It will not change the url in the address bar of the browser so the user will never see the url for the unauthorized page. This was written in MVC 3. This method will also work if you want to redirect them to a login page or if you want to redirect them to a page to just tell them they aren't authorized. I had section in the program that some user didn't have rights to but they were logged in so this is what I used.

如果您有一个控制器并且不想在您的代码中包含 url,您也可以通过这种方式重定向。它不会更改浏览器地址栏中的 url,因此用户永远不会看到未经授权页面的 url。这是在 MVC 3 中编写的。如果您想将它们重定向到登录页面,或者如果您想将它们重定向到一个页面以告诉他们他们未经授权,此方法也将起作用。我在程序中有一些用户没有权限但他们已登录的部分,所以这就是我使用的。

public class AuthorizedRedirect : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        bool isAuthorized = base.AuthorizeCore(httpContext);
        return isAuthorized;
    }
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    filterContext.RequestContext.RouteData.Values["controller"] = "error";
    filterContext.Result = new ViewResult { ViewName = "unauthorized" };
}

回答by mousedoc

And an even simpler version that utilizes FormsAuthentication settings. For those not familiar with Contract, Contract.Requires is a .NET 4 addition. Pros and cons to using Code Contracts.

还有一个使用 FormsAuthentication 设置的更简单的版本。对于那些不熟悉 Contract 的人,Contract.Requires 是 .NET 4 的补充。使用代码契约的利弊。

public class RequiresAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        Contract.Requires(filterContext != null);

        HttpContextBase context = filterContext.RequestContext.HttpContext;

        if (context.User.Identity.IsAuthenticated)
        {
            // user does not possess the required role permission
            string url = context.GetCustomErrorUrl(401);
            context.Response.Redirect(url);
        }
        else
        {

            // redirect the user to the login page
            string extraQueryString  = context.Request.RawUrl;
            FormsAuthentication.RedirectToLoginPage(extraQueryString);
        }
    }
}

回答by Bindi

Going further from divide_byzero's answer even if you don't have a controller you can still use the HandleUnauthorizedRequest to change the redirect.

从divide_byzero的答案走得更远,即使您没有控制器,您仍然可以使用 HandleUnauthorizedRequest 来更改重定向。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AuthoriseRedirect : AuthorizeAttribute
    {
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            filterContext.RequestContext.HttpContext.Response.Redirect("UrlToRedirectTo");
        }
    }

Comes in handy if you have a legacy webforms site that you will be converting to MVC over a longer period of time.....!

如果您有一个旧的 webforms 站点,您将在更长的时间内转换为 MVC,那么它会派上用场......!

回答by Brian Vander Plaats

Ben Cull's method works well, but remember there are two AuthorizeAttribute classes - one in System.Web.HTTP (used by Web API), and the other in System.Web.Mvc. Ben's method uses the System.Web.Mvc class. For clarity, I suggest using the fully qualified path.

Ben Cull 的方法运行良好,但请记住有两个 AuthorizeAttribute 类——一个在 System.Web.HTTP(由 Web API 使用)中,另一个在 System.Web.Mvc 中。Ben 的方法使用 System.Web.Mvc 类。为清楚起见,我建议使用完全限定的路径。

If you're using Web API alongside MVC, you will need to implement two filters:

如果您将 Web API 与 MVC 一起使用,则需要实现两个过滤器:

public class AuthorizeRedirectMVCAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        base.HandleUnauthorizedRequest(filterContext);

        if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.Result = new RedirectResult("~/Account/AccessDenied");
        }
    }
}

public class AuthorizeRedirectAPIAttribute : System.Web.Http.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
    {
        base.HandleUnauthorizedRequest(actionContext);

        if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
        }
    }
}

Note that asp.net will let you decorate your MVC controller with an API filter - it just won't work the way you expect, so keep your attribute names explicit.

请注意,asp.net 将允许您使用 API 过滤器装饰您的 MVC 控制器 - 它不会以您期望的方式工作,因此请保持您的属性名称明确。

回答by Yovav

I like what Brian Vander Plaats posted, just added few improvements:

我喜欢 Brian Vander Plaats 发布的内容,只是添加了一些改进:

/// <summary>
/// Authorize or redirect to an unauthorized MVC action if the user does not have the required roles
/// (an unauthenticated user will be redirected to the defualt sign in action)
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AuthorizeOrRedirectAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        base.HandleUnauthorizedRequest(filterContext);

        if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
        {
            var routeData = new RouteData();
            routeData.Values.Add("controller", "Error");
            routeData.Values.Add("action", "Unauthorized");
            filterContext.Result = new RedirectToRouteResult(routeData.Values);
        }
    }
}

/// <summary>
/// Authorize or redirect to an unauthorized API action if the user does not have the required roles
/// (an unauthenticated user will be redirected to the defualt sign in action)
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AuthorizeOrRedirectApiFilterAttribute : System.Web.Http.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
    {
        base.HandleUnauthorizedRequest(actionContext);

        if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
        }
    }
}