Customizing authentication in MVC

Introduction

If you want to develop a web application which requires authentication or security features not included in the regular ASP.NET membership feature, you might decide to implement these features yourself. But it seems as if the first instinct of many ASP.NET MVC developers is to do this by customizing their Controllers, but I will share you a better way that uses an attribute for that. We can inherit a System.Web.Mvc.AuthorizeAttribute for do that. You can specifies that access to a controller or action method is restricted to users who meet the authorization requirement.

Background

For my case, I want to authorize the user by role with attribute and can be apply to a controller and action, in the normal case, there are many actions in the controller, I want to restrict the role for most of actions of this controller but only allow a few actions for access, so the ACL attribute need to support two parameters, one for the disallow role and the others for allow actions, we can use comma for split each allow actions and disallow roles as below:
index, edit, add...

Using the code

1. Create the ACL attribute.  There are two parameters for set the allow or disallow user roles, so we need to define two variables in constructor:

public class AclAttribute : AuthorizeAttribute
{
    string _notAllowRoles;
    string _allowActions;

    /// <summary>
    /// Set the ACL attribute for access right
    /// </summary>
    /// <param name="NotAllowRoles">Not allow roles, split with comma</param>
    /// <param name="AllowActions">Set the actions allow for the restrict roles</param>
    public AclAttribute(string NotAllowRoles, string AllowActions = null)
    {
        _notAllowRoles = NotAllowRoles;
        _allowActions = AllowActions;
    }

    public AclAttribute() : base()
    {

    }
}

2. We need to implement the OnAuthorization for do your customizing authentication logic

public override void OnAuthorization(AuthorizationContext filterContext)
{
    if (filterContext == null)
    {
        throw new ArgumentNullException("filterContext");
    }

    //get the current action for check the accessright
    var currentAction = filterContext.RouteData.Values["Action"];
    bool ignore = false;

    //check login status
    if (filterContext.HttpContext.Session["IsLogin"] == null && currentAction != null)
    {
        //get the current url for redirect after login
        filterContext.HttpContext.Session["ReturnAction"] = HttpContext.Current.Request.Url.AbsoluteUri;

        UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);

        //if session timeout or lost, then clear all sessions
        filterContext.HttpContext.Session.Clear();

        //redirect to the login page if session is timeout
        var redirectUrl = url.RouteUrl(new { Controller = "Home", Action = "Login" });
        HttpContext.Current.Response.Redirect(redirectUrl);
    }

    //check the allow actions, if this is allow actions, then will ignore the access right checking
    if (!string.IsNullOrEmpty(_allowActions) && _allowActions.Contains(currentAction.ToString()))
    {
        ignore = true;
    }

    //if set not allow roles
    if (!string.IsNullOrEmpty(_notAllowRoles) && !ignore)
    {
        //if user not allow to access then will redirect to the denied page
        if (_notAllowRoles.Contains(filterContext.HttpContext.Session["UserRole"]))
        {
            filterContext.Result = new RedirectToRouteResult(new
                RouteValueDictionary(new { controller = "Error", action = "AccessDenied" }));
            return;
        }
    }

    //this Roles is inerit AuthorizeAttribute, if set it then will be allow to access
    if (!string.IsNullOrEmpty(this.Roles))
    {
        //check accessright by user role
        if ((!HasAccessRights(this.Roles, filterContext.HttpContext.Session["UserRole"])) && !ignore)
        {
            filterContext.Result = new RedirectToRouteResult(new
                RouteValueDictionary(new { controller = "Error", action = "AccessDenied" }));
        }
    }

    /// <summary>
    /// Check the access rights whether is allow
    /// </summary>
    /// <param name="roles"></param>
    /// <param name="currUserRoles"></param>
    /// <returns></returns>
    private bool HasAccessRights(string roles, string currUserRoles)
    {
        if (string.IsNullOrEmpty(currUserRoles))
        {
            return false;
        }

        var uRoles = roles.Split(',');
        foreach (var role in uRoles)
        {
            if (currUserRoles.Contains(role))
            {
                return true;
            }
        }

        return false;
    }

}

3. Use the ACL attribute in controller. As below,  the Account role user will only can access the Index and View actions:

[Acl("Account", AllowActions: "Index,View")]
public class CirculationController : BaseController
{
    public CirculationController()
    {
    }

    public ActionResult Index()
    {
        //do something...
    }

    public ActionResult AjaxHandler()
    {
        //do something...
    }

    public ActionResult View()
    {
        //do something...
    }

    public ActionResult Others()
    {
        //do something...
    }
}

 

 

2 Response(s)

  1. Wilma says :

    January 21, 2017

    TYVM you’ve solved all my premolbs

     
    Reply
    1. winson says :

      January 21, 2017

      you are welcome 🙂

       
      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *