在我的ASP.NET MVC应用程序中,我有大多数控制器用
装饰[Authorize(Roles="SomeGroup")]
当用户无权访问某些内容时,会将其发送到“〜/ Login”,这是我的帐户控制器上的“登录”操作。
如何确定用户因未获得授权而已到达登录页面,以便我能够显示相应的错误?
答案 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更改重定向路径。
的建议,我已使解决方案成为线程安全的[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);
}
}
}