如何从HttpContext访问角色对象,或者更具体地从自定义授权属性访问角色对象?

时间:2017-01-07 13:12:02

标签: c# asp.net-identity-2 asp.net-mvc-5.2

背景

我想根据ApplicationUser s ApplicationRole来授权RoleClaim。我想这样实现:

public class RoleClaimAuthorizeAttribute : AuthorizeAttribute
{
    public RoleClaim RoleClaim { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        foreach (ApplicationRole role in Roles)
        {
            if ((RoleClaim & role.Claims) > 0)
            {
                return true;
            }
        }
        return false;
    }
}

然后我可以像这样装饰控制器动作:

    [RoleClaimAuthorize(RoleClaim = 
        RoleClaim.CanCreateRoles | 
        RoleClaim.CanReadRoles | 
        RoleClaim.CanDeleteRoles | 
        RoleClaim.CanUpdateRoles
    )]
    //
    // GET: /Roles/
    public ActionResult Index()
    {
        return View(_roleManager.Roles);
    }

问题

我遇到的问题是我可以找到从我的自定义authorize属性到达ApplicationUser ApplicationRole的任何方法返回ApplicationRole.Name的字符串数组而不是数组ApplicationRole所以我无法前往ApplicationRole.Claims。我也使用Unity代替Owin来处理ApplicationRoleManager,因此我无法通过ApplicationRoleManager请求HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>()

那么如何才能为当前用户获取ApplicationRole个对象的集合,从而ApplicationRole.Claims

或者,如果它是一个更合适的解决方案,我如何在ApplicationUser中存储当前ApplicationRole的{​​{1}} s'RoleClaim的字符串数组,就像如何角色存储?我知道我的授权属性不能像这种情况所描述的那样工作,但这是我可以使用的情况。

相关课程

ApplicationUser

HttpContext

ApplicationRole

// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser<Guid, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public override Guid Id { get; set; }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, Guid> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, Guid> manager, string authenticationType)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
        // Add custom user claims here
        return userIdentity;
    }
}

RoleClaim

public class ApplicationRole : IdentityRole<Guid, ApplicationUserRole>
{
    public ApplicationRole() : base()
    {
        this.Id = Guid.NewGuid();
    }

    public ApplicationRole(string name)
        : this()
    {
        this.Name = name;
    }

    public ApplicationRole(string name, params string[] claims)
        : this(name)
    {
        Claims = (RoleClaim)Enum.Parse(typeof(RoleClaim), String.Join(",", claims));
    }

    public RoleClaim Claims { get; set; }
}

ApplicationRoleManager

[Flags]
public enum RoleClaim : int
{
    CanCreateUsers = 1,
    CanReadUsers = 2,
    CanUpdateUsers = 4,
    CanDeleteUsers = 8,
    CanCreateRoles = 16,
    CanReadRoles = 32,
    CanUpdateRoles = 64,
    CanDeleteRoles = 128,
    CanCreateTests = 256,
    CanReadTests = 512,
    CanUpdateTests = 1024,
    CanDeleteTests = 2048
}

1 个答案:

答案 0 :(得分:1)

如果您在Unity中注册了角色管理器,只需调用以下方法即可在任何地方检索,包括您的自定义属性:

var roleManager = DependencyResolver.Current.GetService<ApplicationRoleManager>();

或者,如果您不想直接使用解析器,则可以使用Unity的属性注入功能,以便Unity自动在自定义属性中注入角色管理器,在解释here时。然后调用roleManager.FindByNameAsync()方法来检索角色对象。

但不推荐这种方法,因为在每次调用中,您的代码都会访问数据库以检索声明。最好在用户登录时在ClaimsIdentity中存储用户的声明,然后在属性中检索它们,如下所示:

public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
    private readonly ApplicationRoleManager _roleManager;

    public ApplicationSignInManager(ApplicationUserManager userManager, 
        IAuthenticationManager authenticationManager,
        ApplicationRoleManager rolemanager)
            : base(userManager, authenticationManager)
    {
         //inject the role manager to the sign in manager
        _roleManager=rolemanager;
    }

    public override async Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
    {
        var ident= await user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
        // add your custom claims here
        var userRoles=user.Roles.Select(r=>r.RoleId);
        ident.AddClaims(_roleManager.Roles.Where(r => userRoles.Any(ur => ur == r.Id))
            .Select(r=>r.Claims).ToList()
            .Select(c => new Claim("RoleClaims", c.ToString())));
        return ident;
    }
}

现在RoleClaims在用户登录时添加为声明。您可以在属性中检索它们,如下所示:

public class RoleClaimAuthorizeAttribute : AuthorizeAttribute
{
    public RoleClaim RoleClaim { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        foreach (var claims in GetClaims(httpContext.User.Identity as ClaimsIdentity))
        {
            if ((RoleClaim & claims) > 0)
            {
                return true;
            } 
        }
        return false;
    }
    private IEnumerable<RoleClaim> GetClaims(ClaimsIdentity ident)
    {
        return ident==null
            ? Enumerable.Empty<RoleClaim>()
            : ident.Claims.Where(c=>c.Type=="RoleClaims")
                .Select(c=>(RoleClaim)Enum.Parse(typeof(RoleClaim), c.Value)); 
    }
}