【问题标题】:How do I access role objects from HttpContext, or more specifically from a custom authorize attribute?如何从 HttpContext,或者更具体地说,从自定义授权属性访问角色对象?
【发布时间】:2017-01-10 06:15:36
【问题描述】:

背景

我想根据他们的ApplicationRoles'RoleClaims 授权一个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);
    }

问题

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

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

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


相关类

应用程序用户

// 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)
    {
    }
}

【问题讨论】:

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


    【解决方案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)); 
        }
    }
    

    【讨论】:

    • 感谢您提供的替代解决方案,它比我想象的要好得多。在ApplicationSignInManagerCreateUserIdentityAsync 下的ident.AddClaims 行出现问题。异常如下:无法创建类型为“ApplicationUserRole”的常量值。此上下文仅支持原始类型或枚举类型。稍微挖掘了一下,看起来这是来自 Linq 查询的一半,正在研究如何自己修复它,但认为我应该发布。
    • 不幸的是,这并没有解决问题。以下代码是导致异常的原因,只是它仅在枚举枚举时抛出:_roleManager.Roles.Where(r =&gt; user.Roles.Any(ur =&gt; ur.RoleId == r.Id))
    猜你喜欢
    • 2012-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-23
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    • 2020-04-09
    相关资源
    最近更新 更多