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

时间:2017-04-05 14:10:55

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

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

  • 管理员 - 可以对"事件"执行CRUD操作要么 "竞赛"实体
  • 参赛者 - 可以执行GET操作 "竞赛"实体,并可以创建新的"条目"实体。

我遇到了问题:我已经通过Windows身份验证工作,从控制器开始,我可以执行User.Identity.Name并查看我的域名登录名。此外,我可以通过执行User.IsInRole("Domain Users")来验证帐户是否属于域组。如果我想避免为我的应用程序中的每个角色创建新的AD组(让我们说设计变更需要额外的角色),我如何在控制器上使用Authorization来检查应用程序角色?

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

[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。我需要一些中间件类来为我做这个吗?这是在所有控制器上重用授权的最佳方式吗?

2 个答案:

答案 0 :(得分:2)

首先,我最终创建了一个自定义的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"));
});

因此,用户或角色可以拥有名称为&#34; AccessLevel&#34;和指定的值。为了完成所有工作,我还创建了一个自定义UserManager,它只是在CreateAsync期间使用ActiveDirectory中的其他详细信息填充User对象。

答案 1 :(得分:1)

您需要添加DefaultChallangeScheme才能使用Windows身份验证。这就是我的方式,但如果有人有更好的解决方案,我全都耳朵:)

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

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

services.AddAuthentication(options =>
{
            options.DefaultChallengeScheme = IISDefaults.AuthenticationScheme;
});

然后我在变压器中提出申请。

services.AddTransient<IClaimsTransformation, ClaimsTransformer>();

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