Override Authorize Attribute in ASP.NET MVC
I have an MVC controller base class on which I applied the Authorize attribute since I want almost all of the controllers (and their actions along) to be authorized.
However I need to have a controller and an action of another controller unauthorized. I wanted to be able to decorate them with the [Authorize(false)]
or something but this is not available.
Any ideas?
Solution 1:
Edit: Since ASP.NET MVC 4 the best approach is simply to use the built-in AllowAnonymous attribute.
The answer below refers to earlier versions of ASP.NET MVC
You could create a custom authorisation attribute inheriting from the standard AuthorizeAttribute with an optional bool parameter to specify whether authorisation is required or not.
public class OptionalAuthorizeAttribute : AuthorizeAttribute
{
private readonly bool _authorize;
public OptionalAuthorizeAttribute()
{
_authorize = true;
}
public OptionalAuthorizeAttribute(bool authorize)
{
_authorize = authorize;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if(!_authorize)
return true;
return base.AuthorizeCore(httpContext);
}
}
Then you can decorate your base controller with that attribute:
[OptionalAuthorize]
public class ControllerBase : Controller
{
}
and for any controllers you don't want authorisation simply use the override with a 'false' - e.g.
[OptionalAuthorize(false)]
public class TestController : ControllerBase
{
public ActionResult Index()
{
return View();
}
}
Solution 2:
It seems ASP.NET MVC 4 'fixed' this by adding an AllowAnonymous attribute.
David Hayden wrote about this :
[Authorize]
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Login()
{
// ...
}
// ...
}
Solution 3:
My personal take on this would be to split the controller. Just create another controller For the actions you don't need authentication.
Or you could have :
BaseController
doesn't require authentication - here you have all your "base stuff" :).BaseAuthController : BaseController
all actions here require authentication.
That way you can have authentication when you want , just by deriving from a specific class.
Solution 4:
If you just want one action to be unauthorized on an otherwise authorized controller you can do something like this:
public class RequiresAuthorizationAttribute : ActionFilterAttribute
{
private readonly bool _authorize;
public RequiresAuthorizationAttribute()
{
_authorize = true;
}
public RequiresAuthorizationAttribute(bool authorize)
{
_authorize = authorize;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var overridingAttributes = filterContext.ActionDescriptor.GetCustomAttributes(typeof (RequiresAuthorizationAttribute), false);
if (overridingAttributes.Length > 0 && overridingAttributes[0] as RequiresAuthorizationAttribute != null && !((RequiresAuthorizationAttribute)overridingAttributes[0])._authorize)
return;
if (_authorize)
{
//redirect if not authenticated
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
//use the current url for the redirect
var redirectOnSuccess = filterContext.HttpContext.Request.Url.AbsolutePath;
//send them off to the login page
//var redirectUrl = string.Format("?RedirectUrl={0}", redirectOnSuccess);
var loginUrl = LinkBuilder.BuildUrlFromExpression<HomeController>(filterContext.RequestContext, RouteTable.Routes,
x => x.Login(redirectOnSuccess));
filterContext.HttpContext.Response.Redirect(loginUrl, true);
}
}
}
}