【问题标题】:.NET Core 3.0 ClaimsTransformation.NET Core 3.0 声明转换
【发布时间】:2020-02-17 09:14:30
【问题描述】:

之前在 .NET Framework 中,我使用自定义 RoleProvider 和 Windows 身份验证来针对当前主体提供自定义角色,而不是使用 Active Directory 组。

因此,目标是能够使用装饰性[Authorize(Roles="")] 属性,其中角色来自数据库而不是活动目录(或者两者的组合都可以)。

为了在核心中实现这一点,我相信我需要使用IClaimsTransformation 来分配角色声明,正如here 所讨论的那样。

在这里,我只是想添加一个角色“管理员”,但是当我使用 [Authorize(Roles = "Admin")] 时,我得到了 403 Unauthorized 响应。

Startup.cs

services.AddRazorPages();
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddSingleton<IClaimsTransformation, ClaimsTransformer>();
-------
app.UseAuthorization();

ClaimsTransformer.cs

    public class ClaimsTransformer : IClaimsTransformation
    {
        public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
        {
            var identity = (ClaimsIdentity)principal.Identity;
            var c = new Claim(identity.RoleClaimType, "Admin");
            identity.AddClaim(c);
            return await Task.FromResult(principal);
        }
    }

令人讨厌的是,当我调用 User.IsInRole() 时,这有效,并且当我检查声明时可以看到该组,因此它正在被添加,但是它不适用于 Authorize 属性。任何建议将不胜感激。

【问题讨论】:

    标签: c# .net-core authorization windows-authentication


    【解决方案1】:

    设法使用ClaimsTransformer 和自定义TypeFilterAttribute 解决此问题

    ClaimsTransformer.cs

        public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
        {
            var identity = (WindowsIdentity)principal.Identity;
    
            Guid userGuid;
    
            SecurityIdentifier sid = identity.User;
    
            using (DirectoryEntry userDirectoryEntry = new DirectoryEntry("LDAP://<SID=" + sid.Value + ">"))
            {
                userGuid = userDirectoryEntry.Guid;
            }
    
            UserAccount user = null;
    
            if (userGuid != Guid.Empty)
                user = await db.UserAccounts.Where(x => x.GUID == userGuid).SingleOrDefaultAsync();
    
            if (user == null)
                return principal;
    
            if (user.Historic)
                return principal;
    
    
            var claims = new List<Claim>();
            foreach (var role in user?.UserAccountGroups)
            {
                claims.Add(new Claim(ClaimTypes.GroupSid, role.Group.Name));
            };
    
            identity.AddClaims(claims);
    
            return principal;
        }
    

    GroupsAttribute.cs

        [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
    public class GroupsAttribute : TypeFilterAttribute
    {
        public GroupsAttribute(string groups) : base(typeof(ClaimRequirementFilter))
        {
            Arguments = new object[] { groups };
        }
    }
    
    public class ClaimRequirementFilter : IAuthorizationFilter
    {
        readonly string _groups;
    
        public ClaimRequirementFilter(string groups)
        {
            _groups = groups;
        }
    
        public void OnAuthorization(AuthorizationFilterContext context)
        {
    
    
            var groups = _groups.Split(',');
            bool hasClaim = false;
            foreach (var group in groups)
            {
                if (context.HttpContext.User.Claims.Any(c => c.Type == ClaimTypes.GroupSid && c.Value.Equals(group.Trim(), StringComparison.OrdinalIgnoreCase)))
                    hasClaim = true;
            }
    
            if (!hasClaim)
            {
                context.Result = new ForbidResult();
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      我使用类似的方法根据用户的 Active Directory 组向用户提供自定义角色声明。

      public class ClaimsTransformer : IClaimsTransformation
      {
          private readonly IConfiguration _configuration;
      
          public ClaimsTransformer(IConfiguration configuration)
          {
              _configuration = configuration;
          }
          public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
          {
              var claimsIdentity = (ClaimsIdentity)principal.Identity;
             // AppRole has two string props, Displayname and AdGroup. Get Adgroup from appsettings.json.
              AppRole customRole = new AppRole() 
              {  
                  DisplayName =_configuration.GetSection("Roles")
                          .GetSection("CustomRole")
                          .GetSection("DisplayName").Value,
                  AdGroup = _configuration.GetSection("Roles")
                          .GetSection("CustomRole")
                          .GetSection("AdGroup").Value
              };
              if (principal.IsInRole(customRole.AdGroup))
              {
                  Claim customRoleClaim = new Claim(claimsIdentity.RoleClaimType, "CustomRole");
                  claimsIdentity.AddClaim(customRoleClaim);
              }
      
              return Task.FromResult(principal);
          }
      }
      

      要让 Claimstransformer 使用 Authorize 属性,请在 Startup.cs 中使用:

      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
         ...
      
         app.UseAuthorization();
         app.UseAuthentication();
      
         ...
      }
      

      我将它用于 Blazor,所以它可以在 Blazor 组件中像这样使用。

      授权整个组件:

          @attribute [Authorize(Roles = "CustomRole")]
      

      或授权组件的某些部分:

          <AuthorizeView Roles="CustomRole">
              <Authorized>You are authorized</Authorized>
          </AuthorizeView>
      

      【讨论】:

        【解决方案3】:

        我能够通过 Startup.cs 中的策略设置获得授权

                    services.AddAuthorization(options =>
                    {               
                        options.FallbackPolicy = options.DefaultPolicy;
                        options.AddPolicy("AdminOnly", policy => policy.RequireClaim(System.Security.Claims.ClaimTypes.Role, "Admin"));
                    });
        

        【讨论】:

          猜你喜欢
          • 2020-05-30
          • 1970-01-01
          • 2016-09-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-26
          • 2014-09-12
          • 1970-01-01
          相关资源
          最近更新 更多