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

Posted

技术标签:

【中文标题】ASP.NET Core Windows 身份验证和应用程序角色【英文标题】:ASP.NET Core Windows Authentication and Application Roles 【发布时间】:2017-08-31 05:10:42 【问题描述】:

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

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

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

这是我要使用的示例控制器:

[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();
            
        
    

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

【问题讨论】:

【参考方案1】:

首先,我最终创建了一个自定义 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 对象。

【讨论】:

【参考方案2】:

您需要添加 DefaultChallengeScheme 才能使用 Windows 身份验证。我就是这样做的,但是如果有人有更好的解决方案,我会全力以赴:)

我在当前应用程序中使用以下设置。

services.AddIdentity<ApplicationUser, ApplicationRole>()
            .AddEntityFrameworkStores<SecurityDbContext>()
            .AddDefaultTokenProviders();

services.AddAuthentication(options =>

            options.DefaultChallengeScheme = IISDefaults.AuthenticationScheme;
);

然后我将我的应用程序声明放入变压器中。

services.AddTransient<IClaimsTransformation, ClaimsTransformer>();

我希望这会让你朝着正确的方向前进。

【讨论】:

哎呀。没有看到你的问题的日期。我希望你早就解决了。 我还没有彻底测试我所有的控制器,但看起来这确实完成了我正在寻找的东西。我将发布我想出的替代方案,它也使用更新的基于策略的授权。 也使用'options.DefaultScheme = IISDefaults.AuthenticationScheme;'

以上是关于ASP.NET Core Windows 身份验证和应用程序角色的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 中的 Windows 身份验证模拟是不是已死?

带有 Windows 身份验证的 ASP.Net Core 3.1 中的空闲超时

ASP.NET Core JWT/Windows 身份验证 HTTP 400 错误

带有 Windows 身份验证的 ASP.NET Core 2.1 自定义 RoleProvider

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

无法使用 Windows 身份验证发布到 asp.net core web api