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

时间:2009-09-30 15:07:09

标签: c# .net asp.net asp.net-mvc authorization

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

装饰
[Authorize(Roles="SomeGroup")]

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

如何确定用户因未获得授权而已到达登录页面,以便我能够显示相应的错误?

7 个答案:

答案 0 :(得分:76)

更新(2015年6月): @ daniel-lidström已正确指出您不应在ASP.NET MVC应用程序中使用Response.Redirect。有关原因的详细信息,请参阅此链接:Response.Redirect and ASP.NET MVC – Do Not Mix

UPDATE(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);
        }
    }
}

下面的原始答案(仍然完全正常)

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

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

编辑:由于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);
        }
    }
}

答案 1 :(得分:28)

您可以查找?ReturnUrl=查询字符串值,也可以创建自己的授权过滤器&在TempData中设置一个字段,指明原因。

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

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

然后在您看来,您可以这样做:

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

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

答案 2 :(得分:5)

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

如果您在MVC旁边使用Web API,则需要实现两个过滤器:

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

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

答案 3 :(得分:4)

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

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

答案 4 :(得分:2)

一个更简单的版本,它使用FormsAuthentication设置。对于那些不熟悉Contract的人,Contract.Requires是.NET 4的补充。使用Code Contracts的利弊。

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

答案 5 :(得分:1)

即使您没有控制器,也可以从divide_byzero的答案中继续前进,您仍然可以使用HandleUnauthorizedRequest来更改重定向。

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

如果您有一个遗留的webforms网站,您将在更长的时间内转换为MVC,这会派上用场.......

答案 6 :(得分:0)

我喜欢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);
        }
    }
}