Custom Authorization (Permissions) ASP.NET MVC

旧街凉风 提交于 2019-12-04 13:06:13
public class PermissionAttribute : AuthorizeAttribute
    {
        private readonly IAccountService _accountService;
        private readonly IEnumerable<PermissionEnum> _permissions;

        public PermissionAttribute(params PermissionEnum[] permissions):
            this(DependencyResolver.Current.GetService<IAccountService>())
        {
            _permissions = permissions;
        }

        protected PermissionAttribute(IAccountService accountService)
        {
            _accountService = accountService;
        }

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if(!_permissions.Any(x=>_accountService.HasPermission(filterContext.HttpContext.User.Identity.Name,(int)x)))
                filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden);
            base.OnAuthorization(filterContext);
        }

    }

Basically you have to create your own AuthorizeAttribute, but use IIdentity from .NET. What you have described here is a Claim based system for authentication and authorization.

Most likely you will have to throw away the Membership from ASP.NET or use just part of it. As far as I know, it is not built with claims in mind.

In .NET 4.5 the guys added the class: ClaimsPrincipal, which implements the interface IPrincipal. This class can be used to implement custom authentication and authorization based on claims.

So, when the user is authenticated, you can add the claims on the thread:

var id = new ClaimsIdentity(claims, "Dummy");
var principal = new ClaimsPrincipal(new[] { id });
Thread.CurrentPrincipal = principal;

and then later on just use the claims you find on the Thread.CurrentPrincipal.

In ASP.NET MVC, you can do the following steps:

  1. create a Delegating Handler which authenticates the user. If the user is authenticated, then claims are added to the thread principal. Ideally, this delegating handler should be as high up the chain as possible so that you have the claims available everywhere in the execution chain. Also remember to set the HttpContext.Current.User with the same principal

    public class AuthHandler : DelegatingHandler{

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
    //authenticate user
    //get claims
    //create principal
    var newPrincipal = CreatePrincipal(claims);
    Thread.CurrentPrincipal = newPrincipal;
    if (HttpContext.Current != null)
         HttpContext.Current.User = newPrincipal;
    return await base.SendAsync(request, cancellationToken);
    }
    

    }

  2. Create a filter which authorizes based on the claims added to the Thread Principal. Here you can do something like compare the current route with the information found in the claims.

So i think what you says is: ActionA is accessible only if user has perm1,perm2 and similarly ActionB is accessible when user has perm1 and perm3

The code i gave is for illustration, i did not compile it. But will give you the picture of the approach i am stating

STEP 1: You can proceed with creating a permission enum attributed with Flags attribute

STEP 2: Add claims to current principal based on user permission stored in your data store.

STEP 3: When Action is invoked authorize access against claims

[Flags]
    enum PermType
    {
        None = 0x0,
        Perm1 = 0x1,
        perm2 = 0x2,
        perm3 = 0x4,
        perm4 = 0x8,
        perm5 = 0x10 
    }

Adding claims to the CurrentPrincipal

var currentPrincipal = ClaimsPrincipal.Current;
var cms = currentPrincipal.Claims;
var permissions = PermType.Perm1 | PermType.perm2;
var claims = cms.ToList();
claims.Add(new Claim("Action1", permissions.ToString()));
claims.Add(new Claim("Action2", permissions.ToString()));
claims.Add(new Claim("Action3", permissions.ToString()));
System.Threading.Thread.CurrentPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims));

check if user can access a particular action

public bool CanAccessThisAction(string acionName,PermType requiredPerms)
{
    var claim = principal.Claims.FirstOrDefault(c => c.Type == acionName);
    if (customPermissionClaim != null)
    {
        //check if required permission is present in claims for this user
        //return true/false
    }
    return false;
}

on Action

public ActionResult TestAction(string id)
{
    if(CanAccessThisAction("TestAction",PermType.Perm1|PermType.perm3|PermType.perm5))
    {
        //do your work here
    }
    else
    {
        //redirect user to some other page which says user is not authorized
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!