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

Ron*_*rby 45 .net c# asp.net asp.net-mvc authorization

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

[Authorize(Roles="SomeGroup")]
Run Code Online (Sandbox Code Playgroud)

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

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

Ben*_*ull 76

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

更新(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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

对于仍然登陆这里的人,我编辑了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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 从MSDN:从AuthorizeAttribute派生如果从AuthorizeAttribute类派生,派生类型必须是线程安全的.因此,不要将状态存储在类型本身的实例中(例如,在实例字段中),除非该状态适用于所有请求.而是在Items属性中存储每个请求的状态,该属性可以通过传递给AuthorizeAttribute的上下文对象访问.是不是_isAuthorized一个实例字段? (5认同)
  • 我没有在类中使用重定向依赖项,而是将其替换为 `filterContext.RequestContext.HttpContext.AddError(new HttpException(403, "You have no power here..."));` 这让我可以使用 `customError` web.config。我仍然希望有一种方法可以实际返回一个 `403` 而不是 `302`。 (2认同)
  • filterContext.RequestContext.HttpContext.Response.Redirect(RedirectUrl) - 对我来说,这实际上确实执行了重定向,但也会抛出“System.Web.HttpException (0x80004005): 发送 HTTP 标头后无法重定向”。在引擎盖下。这可以避免吗?如果可以,如何避免? (2认同)

Ben*_*man 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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在您的视图中,您可以执行以下操作:

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

(虽然我推荐一种比这些神奇琴弦更好的方法,但你明白了这一点)


Bri*_*ats 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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