Introduction
In MVC the default method to perform authorization is hard coding the "Authorize" attribute in the controllers, for each action, in this article I will explain a simple way
to implement "Dynamic Authorization" with the ability to assign permissions for actions to roles or users.
Using the code
First I will explain my user authentication and role assigning model, I have used Forms Authentication this scenario, here is my sample login action:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
Dictionary<string, string> users = new Dictionary<string, string>();
users.Add("admin", "admin-pass");
string roles;
if (users[model.UserName] == model.Password)
{
Session["User"] = model.UserName;
roles = "admin;customer";
Session["Roles"] = roles;
HttpContext.Items.Add("roles", roles);
FormsAuthentication.SetAuthCookie(model.UserName, false);
string returnUrl = Request.QueryString["ReturnUrl"] as string;
return RedirectToLocal(returnUrl);
if (returnUrl != null)
{
Response.Redirect(returnUrl);
}
else
{
Response.Redirect("Default.aspx");
}
}
else
{
ModelState.AddModelError("",
"The user name or password provided is incorrect");
return View(model);
}
}
All the actions that need authentication have to be loaded in a list, and also all of the roles and actions that each role has access to, I have put some sample code
to simulate them "AllRoles" and "NeedAuthenticationActions". Then we need to create a base class for controllers in which I have overridden the OnActionExecuting
method, in which the user will be authorized based on its current role and whether he/she has logged in or not, the action may also has no need to be authorized.
public class ControllerBase : Controller
{
private string ActionKey;
Dictionary<string, List<string>> AllRoles =
new Dictionary<string, List<string>>();
protected void initRoles()
{
AllRoles.Add("role1", new List<string>() { "Controller1-View",
"Controller1-Create", "Controller1-Edit", "Controller1-Delete" });
AllRoles.Add("role2", new List<string>() { "Controller1-View", "Controller1-Create" });
AllRoles.Add("role3", new List<string>() { "Controller1-View" });
}
List<string> NeedAuthenticationActions =
new List<string>() { "Controller1-Edit", "Controller1-Delete"};
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
ActionKey = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName +
"-" + filterContext.ActionDescriptor.ActionName;
string role = Session["Roles"].ToString(); if (NeedAuthenticationActions.Any(s => s.Equals(ActionKey, StringComparison.OrdinalIgnoreCase)))
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
string redirectUrl = string.Format("?returnUrl={0}",
filterContext.HttpContext.Request.Url.PathAndQuery);
filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + redirectUrl, true);
}
else {
if (!AllRoles[role].Contains(ActionKey))
{
filterContext.HttpContext.Response.Redirect("~/NoAccess", true);
}
}
}
}
Points of Interest
Using this scenario there is no need to hard code the Authorize attribute and role or user names in the controller class, and all of them may be loaded from any source
and be used dynamically.