authentication - ASP.NET Core Windows 身份验证和应用程序角色

标签 authentication asp.net-core asp.net-core-mvc asp.net-core-webapi asp.net-core-identity

我正在尝试创建一个相当简单的 Intranet 应用程序,它将使用 Active Directory 进行身份验证,并将使用 AspNetRoles 表来检查用户是否处于某个应用程序角色。此应用程序只是一个内部彩票,其中一些用户可以创建事件/竞赛,然后其他用户可以提交参赛作品。我正在考虑从 2 个基本角色开始:

  • 管理员 - 可以对“事件”或
    “竞赛”实体
  • 参赛者 - 可以执行 GET 操作
    “竞赛”实体,并且可以创建新的“参赛”实体。

  • 这就是我卡住的地方:我让 Windows 身份验证在某种意义上工作,从 Controller ,我可以做 User.Identity.Name并查看我的域登录名。此外,我可以通过执行 User.IsInRole("Domain Users") 来验证帐户是否属于域组。 .如果我想避免为我的应用程序中的每个角色创建新的 AD 组(假设设计更改需要额外的角色),我如何在 Controller 上使用授权来检查应用程序角色?

    这是我想使用的示例 Controller :

    [Route("api/[controller]")]
    [Authorize(Roles = "Contestant")]
    public class EventTypesController : Controller
    {
        private IRaffleRepository _repository;
        private ILogger<EventTypesController> _logger;
    
        public EventTypesController(IRaffleRepository repository, ILogger<EventTypesController> logger)
        {
            _repository = repository;
            _logger = logger;
        }
    
        [HttpGet("")]
        public IActionResult Get()
        {
            try
            {
                var results = _repository.GetAllEventTypes();
                return Ok(Mapper.Map<IEnumerable<EventTypeViewModel>>(results));
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to get all event types: {ex}");
                return BadRequest("Error occurred");
            }
        }
    }
    

    在我的 Startup.cs 中,在 ConfigureServices 中,我按如下方式连接 Identity:

    services.AddIdentity<RaffleUser, ApplicationRole>()
                .AddEntityFrameworkStores<RaffleContext>();
    

    我的 RaffleUser 类实际上只是 IdentityUser 的默认实现:

    public class RaffleUser : IdentityUser
    {
    
    }
    

    我的 ApplicationRole 类也只是 IdentityRole 的默认实现。我还尝试在种子类中播种一些数据:

    if (!await _roleManager.RoleExistsAsync("Administrator"))
    {
        var adminRole = new ApplicationRole()
        {
            Name = "Administrator"
        };
        await _roleManager.CreateAsync(adminRole);
        await _context.SaveChangesAsync();
    }
    
    if (await _userManager.FindByNameAsync("jmoor") == null)
    {
        using (var context = new PrincipalContext(ContextType.Domain))
        {
            var principal = UserPrincipal.FindByIdentity(context, "DOMAIN\\jmoor");
            if (principal != null)
            {
                var user = new RaffleUser()
                {
                    Email = principal.EmailAddress,
                    UserName = principal.SamAccountName
                };
    
                await _userManager.CreateAsync(user);
                await _context.SaveChangesAsync();
    
                var adminRole = await _roleManager.FindByNameAsync("Administrator");
                if (adminRole != null)
                {
                    await _userManager.AddToRoleAsync(user, adminRole.Name);
                    await _context.SaveChangesAsync();
                }
            }
        }
    }
    

    数据进入表,但似乎在 Controller 级别,我需要将经过身份验证的用户转换为 IdentityUser。我需要一些中间件类来为我做这件事吗?这是使授权可在所有 Controller 上重用的最佳方式吗?

    最佳答案

    首先,我最终创建了一个自定义 ClaimsTransformer,它返回一个填充了 UserClaims 和 RoleClaims 的 ClaimsPrincipal(在重构我的应用程序之后,我决定使用基于策略的授权,并且可以在角色或用户级别添加访问声明):

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
    {
        var identity = (ClaimsIdentity)context.Principal.Identity;
        var userName = identity.Name;
        if (userName != null)
        {
            var user = await _userManager.FindByLoginAsync("ActiveDirectory", userName);
            if (user != null)
            {
                identity.AddClaims(await _userManager.GetClaimsAsync(user));
                var roles = await _userManager.GetRolesAsync(user);
                identity.AddClaims(await GetRoleClaims(roles));
            }
        }
        return context.Principal;
    }
    
    private async Task<List<Claim>> GetRoleClaims(IList<string> roles)
    {
        List<Claim> allRoleClaims = new List<Claim>();
        foreach (var role in roles)
        {
            var rmRole = await _roleManager.FindByNameAsync(role);
            var claimsToAdd = await _roleManager.GetClaimsAsync(rmRole);
            allRoleClaims.AddRange(claimsToAdd);
        }
        return allRoleClaims;
    }
    

    我在 Startup.cs 中将其连接起来:

    services.AddScoped<IClaimsTransformer, Services.ClaimsTransformer>();
    

    我还使用了基于策略的授权:

    services.AddAuthorization(options =>
    {
        options.AddPolicy("Administrator", policy => policy.RequireClaim("AccessLevel", "Administrator"));
        options.AddPolicy("Project Manager", policy => policy.RequireClaim("AccessLevel", "Project Manager"));
    });
    

    因此,用户或角色可以具有名称为“AccessLevel”并指定值的声明集。为了完成所有工作,我还创建了一个自定义 UserManager,它仅在 CreateAsync 期间使用 ActiveDirectory 中的其他详细信息填充 User 对象。

    关于authentication - ASP.NET Core Windows 身份验证和应用程序角色,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43233693/

    相关文章:

    android - Android 登录验证失败

    c# - Asp.net Core MVC - 在应用程序关闭期间获取依赖项

    asp.net-core - 身份服务器 - 使用刷新 token 获取更新的声明

    entity-framework - 如何在 ASP.NET 5 模板中添加实体数据模型

    c# - Entity Framework 7 自引用表返回 null

    c# - LDAP 路径问题

    PHP session 变量在重定向后不保存?

    c# - 从 2.1 迁移到 3.1 .Net Core,Microsoft.AspNetCore.App 的替代品是什么?

    c# - 允许 ASP.Net Core WEB API 中的参数名称为 "["

    flutter - 使用手机进行身份验证时如何修复 'user unauthenticated' firebase 存储