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

How do I access role objects from HttpContext, or more specifically from a custom authorize attribute?

背景

我想根据他们的 ApplicationRoleRoleClaim 授权一个 ApplicationUser。我想这样实现:

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);
    }

问题

我遇到的问题是我可以找到从我的自定义授权属性 returns 到达 ApplicationUserApplicationRole 的任何方法 [=21] 的字符串数组=] 不是 ApplicationRole 的数组,所以我无法到达 ApplicationRole.Claims。我还使用 Unity 而不是 Owin 来处理 ApplicationRoleManager,所以我无法通过 HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>().

请求 ApplicationRoleManager

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

或者,如果这是一个更合适的解决方案,我如何将当前 ApplicationUser's ApplicationRoles' RoleClaim 的字符串数组存储在 HttpContext 中,就像角色如何存储一样存储?我知道在这种情况下我的授权属性无法正常工作,但我仍然可以处理这种情况。


相关类

应用程序用户

// 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;
    }
}

应用角色

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; }
}

角色声明

[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
}

ApplicationRoleManager

public class ApplicationRoleManager : RoleManager<ApplicationRole, Guid>
{
    public ApplicationRoleManager(IRoleStore<ApplicationRole, Guid> store) : base(store)
    {
    }
}

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

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

或者,如果您不想直接使用解析器,您可以使用 Unity 的 属性 注入功能,以便 Unity 自动将角色管理器注入自定义属性中,这在 中进行了解释。然后调用 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)); 
    }
}