【问题标题】:Using Claims Transformation with Windows Authentication将声明转换与 Windows 身份验证一起使用
【发布时间】:2018-06-19 21:12:46
【问题描述】:

在 ASP.NET Core 2.1 应用程序中使用 Windows 身份验证。在数据库中,我们有一个User 表,用于存储用户及其 Sid。它与 UserProfile 具有 1-1 关系,其中包含我想用于声明的信息。

我为索赔转换添加了此服务:

public class UserStatusClaimsTransformation : IClaimsTransformation
{
    private readonly MyDbContext _context;

    public UserStatusClaimsTransformation(MyDbContext context)
    {
        _context = context;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        if (principal.Identity is WindowsIdentity identity)
        {
            User user = await _context.User
                .Include(u => u.UserProfile)
                .Where(u => new SecurityIdentifier(u.WindowsSid, 0) == identity.User)
                .SingleOrDefaultAsync();

            if (user != null)
            {
                identity.AddClaim(new Claim("Status", user.UserProfile));
            }
        }

        return principal;
    }
}

我的问题是,一旦注册了此服务,在管道中其他地方访问的 IPrincipal 现在是 ClaimsPrincipal 而不是 WindowsPrincipal。例如,在 MyDbContext 中,我通过 DI 注入 IPrincipal:

public MyDbContext(DbContextOptions<MyDbContext> options, IPrincipal principal) : base(options)
{
    _principal = principal;
}

以前,这是 WindowsPrincipal,我可以从 _principal.Identity.Name 获取用户名,但在注册我的 Claims Transformer 后,它是 ClaimsPrincipal,而 _principal.Identity.Name 为空。在使用声明转换后,有没有办法将通过 DI 提供的 IPrincipal 保留为 WindowsPrincipal?

【问题讨论】:

    标签: c# asp.net-core windows-authentication claims-based-identity


    【解决方案1】:

    我使用 ASP.NET MVC Core 2.2,对您来说可能为时已晚,但也许对其他人有帮助。然后我启动了我的应用程序,但没有找到有用的信息,这是我的自定义建议。它有效,您可以使用它:

     public class ClaimsLoader : IClaimsTransformation
    {        
        private IUserrolesRepository repository;
        public ClaimsLoader(IUserrolesRepository repo)
        {
            repository = repo;
        }
        public bool ifRoleExist(ClaimsPrincipal principal, string value)
        {
            var ci = (ClaimsIdentity)principal.Identity;
            var claim = principal.FindAll(ci.RoleClaimType);
            foreach (var c in claim)
            {
                if (c.Value == value)
                    return true;
            }
            return false;
        }
    
        public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
       {            
            var ci = (ClaimsIdentity)principal.Identity;
            List<Userrole> userroles = await repository.Userroles.Include(u => u.R).Include(u => u.U).Where(u => u.U.Uid==principal.Identity.Name.Substring(10)).ToListAsync();
    
            if (userroles!=null)                {
    
                foreach (Userrole ur in userroles)
                {
                    Claim claim = new Claim(ci.RoleClaimType, ur.Rid); 
                    if (!ifRoleExist(principal, ur.Rid))                  
                    ci.AddClaim(claim);
                }
            }                     
            return await Task.FromResult(principal);   
        }
    }
    

    我这里没有使用ApplicationDbContext,而是使用repository,这是repository接口:

        public interface IUserrolesRepository
    {
        IQueryable<Userrole> Userroles { get; }
    
    }
    

    和 EFUserRepository 类:

    public class EFUserrolesRepository : IUserrolesRepository
    {
        private ApplicationDbContext context;
    
        public EFUserrolesRepository(ApplicationDbContext ctx)
        {
            context = ctx;
        }
    
        public IQueryable<Userrole> Userroles => context.Userrole;
    
    }
    

    这是我的 Userrole 类(以防万一有人需要):

    public partial class Userrole
    {
        public string Rid { get; set; }//pk
        public string Uid { get; set; }//pk
    
        public virtual Role R { get; set; }//fk
        public virtual User U { get; set; }//fk
    }
    

    在 ApplicationDbContext 中我也做了一些更改:

    public class ApplicationDbContextFactory
           : IDesignTimeDbContextFactory<ApplicationDbContext>
    {
    
        public ApplicationDbContext CreateDbContext(string[] args) =>
            Program.BuildWebHost(args).Services
                .GetRequiredService<ApplicationDbContext>();
    }
    

    最后在启动中:

            services.AddSingleton<IClaimsTransformation, ClaimsLoader>();
            services.AddTransient<IUserrolesRepository, EFUserrolesRepository>();
            services.AddMvc();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    
    
    
    services.AddAuthentication(Microsoft.AspNetCore.Server.IISIntegration.IISDefaults.AuthenticationScheme);
    

    Program.cs 文件也发生了变化:

    public class Program
    {
        public static void Main(string[] args)
        {
            //CreateWebHostBuilder(args).Build().Run();
            BuildWebHost(args).Run();
        }
    
        /*public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();*/
    
        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .UseDefaultServiceProvider(options =>
                    options.ValidateScopes = false)
                .Build();
    }
    

    就是这样。它应该工作。

    【讨论】:

    • 赞成。非常好的和详细的答案!嘿@valuator 这值得设置这应该是答案:)。
    猜你喜欢
    • 2014-05-20
    • 2020-03-12
    • 1970-01-01
    • 2020-03-23
    • 1970-01-01
    • 2011-04-09
    • 2018-09-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多