【问题标题】:How to use Windows Active Directory Authentication and Identity Based Claims?如何使用 Windows Active Directory 身份验证和基于身份的声明?
【发布时间】:2018-01-03 04:30:57
【问题描述】:

问题

我们希望使用 Windows Active Directory 来验证用户进入应用程序。但是,我们不想使用 Active Directory 组来管理控制器/视图的授权。

据我所知,将广告和基于身份的声明结合起来并不容易。

目标

  • 使用本地 Active Directory 对用户进行身份验证
  • 使用身份框架管理声明

尝试(失败)

  • Windows.Owin.Security.ActiveDirectory-Doh。这适用于 Azure AD。不支持 LDAP。他们可以改称为 AzureActiveDirectory 吗?
  • Windows 身份验证 - 这适用于 NTLM 或 Keberos 身份验证。问题开始于:i) 令牌和声明都由 AD 管理,我不知道如何使用它来使用身份声明。
  • LDAP - 但这些似乎迫使我手动进行表单身份验证以使用身份声明?当然必须有更简单的方法吗?

任何帮助将不胜感激。我已经在这个问题上停留了很长时间,并希望得到外界对此事的意见。

【问题讨论】:

  • 您可以使用 Active Directory 联合服务 (ADFS) 吗?如果是这样,它可以公开 Windows 安全模型本机理解的声明感知身份验证点。身份验证后,您可以实现自定义 ClaimsAuthenticationManager 来填写您的应用程序需要的其他自定义声明。如果您不能使用 ADFS,ThinkTecture 有一个开源的身份服务器。

标签: authentication asp.net-identity claims-based-identity asp.net-core visual-studio-2015


【解决方案1】:

只需使用用户名和密码点击 AD,而不是针对您的数据库进行身份验证

// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindByNameAsync(model.UserName);
        if (user != null && AuthenticateAD(model.UserName, model.Password))
        {
            await SignInAsync(user, model.RememberMe);
            return RedirectToLocal(returnUrl);
        }
        else
        {
            ModelState.AddModelError("", "Invalid username or password.");
        }
    }
    return View(model);
}

public bool AuthenticateAD(string username, string password)
{
    using(var context = new PrincipalContext(ContextType.Domain, "MYDOMAIN"))
    {
        return context.ValidateCredentials(username, password);
    }
}

【讨论】:

  • 是的,如果您从模板生成项目,您需要做的就是添加 AuthenticateAD 函数并修改登录操作。
  • 取出authenticationMethod和cancellationToken。您所需要的就是我的回答中的上述内容。
  • SignInAsync 在 SignInManager 存在之前曾经是一个辅助方法。不过,这个概念是一样的。 PrincipalContext 来自命名空间 System.DirectoryServices.AccountManagement。在您的项目中添加对它的引用以使用它。
  • 不,不是,因为它是一个专用于使用 Active Directory 的命名空间,因此与 ASP.NET 无关
  • 用什么方法解决?默认情况下应该只是在 .NET 框架程序集中。右击“引用”->添加引用->搜索“目录”
【解决方案2】:

在 ASPNET5 (beta6) 上,想法是使用 CookieAuthentication 和 Identity :您需要在 Startup 类中添加:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddAuthorization();
    services.AddIdentity<MyUser, MyRole>()
        .AddUserStore<MyUserStore<MyUser>>()
        .AddRoleStore<MyRoleStore<MyRole>>()
        .AddUserManager<MyUserManager>()
        .AddDefaultTokenProviders();
}

在配置部分,添加:

private void ConfigureAuth(IApplicationBuilder app)
{
    // Use Microsoft.AspNet.Identity & Cookie authentication
    app.UseIdentity();
    app.UseCookieAuthentication(options =>
    {
        options.AutomaticAuthentication = true;
        options.LoginPath = new PathString("/App/Login");
    });
}

然后,你需要实现:

Microsoft.AspNet.Identity.IUserStore
Microsoft.AspNet.Identity.IRoleStore
Microsoft.AspNet.Identity.IUserClaimsPrincipalFactory

和扩展/覆盖:

Microsoft.AspNet.Identity.UserManager
Microsoft.AspNet.Identity.SignInManager

我实际上已经设置了一个示例项目来展示如何做到这一点。 GitHub Link.

我在 beta8 上进行了测试,并使用了一些小的适配(如 Context => HttpContext),它也能正常工作。

【讨论】:

  • 嗨,你好,这篇文章回答了你的问题吗?
  • 这种方法是否仍然适用于 RC1?我给了你一票。
  • 感谢您的认可!是的,这也适用于 RC1,但是需要应用一些命名更改(例如 Microsoft.Framework.OptionsModel 变为 Microsoft.Extensions.OptionsModel)。
  • ASP.NET Core 1.0 RC2 似乎即将发生重大变化?
  • 是的!它现在变化太频繁了,而且 RC 应该是稳定的,但他们仍然会做出核心变化……我会等到 RC2 完成后,才会在我这边做出改变。
【解决方案3】:

您上面的解决方案将我推向了一个对我有用的方向,即 MVC6-Beta3 Identityframework7-Beta3 EntityFramework7-Beta3:

// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    //
    // Check for user existance in Identity Framework
    //
    ApplicationUser applicationUser = await _userManager.FindByNameAsync(model.eID);
    if (applicationUser == null)
    {
        ModelState.AddModelError("", "Invalid username");
        return View(model);
    }

    //
    // Authenticate user credentials against Active Directory
    //
    bool isAuthenticated = await Authentication.ValidateCredentialsAsync(
        _applicationSettings.Options.DomainController, 
        _applicationSettings.Options.DomainControllerSslPort, 
        model.eID, model.Password);
    if (isAuthenticated == false)
    {
        ModelState.AddModelError("", "Invalid username or password.");
        return View(model);
    }

    //
    // Signing the user step 1.
    //
    IdentityResult identityResult 
        = await _userManager.CreateAsync(
            applicationUser, 
            cancellationToken: Context.RequestAborted);

    if(identityResult != IdentityResult.Success)
    {
        foreach (IdentityError error in identityResult.Errors)
        {
            ModelState.AddModelError("", error.Description);
        }
        return View(model);
    }

    //
    // Signing the user step 2.
    //
    await _signInManager.SignInAsync(applicationUser,
        isPersistent: false,
        authenticationMethod:null,
        cancellationToken: Context.RequestAborted);

    return RedirectToLocal(returnUrl);
}

【讨论】:

  • Authentication 是 MVC6 独有的功能吗?目前尚不清楚它应该来自哪里,这是一个关键部分。
  • 我相信“身份验证”应该代表用于针对域控制器验证用户名和密码的任何类或域上下文。比如在 System.DirectoryServices.AccountManagement -> PrincpalContext().ValidateCredentials(username, password)
【解决方案4】:

您可以使用 ClaimTransformation,我今天下午使用下面的文章和代码让它工作了。我正在使用窗口身份验证访问应用程序,然后根据存储在 SQL 数据库中的权限添加声明。这是一篇好文章,应该对你有所帮助。

https://github.com/aspnet/Security/issues/863

总结...

services.AddScoped<IClaimsTransformer, ClaimsTransformer>();

app.UseClaimsTransformation(async (context) =>
{
IClaimsTransformer transformer = context.Context.RequestServices.GetRequiredService<IClaimsTransformer>();
return await transformer.TransformAsync(context);
});

public class ClaimsTransformer : IClaimsTransformer
    {
        private readonly DbContext _context;

        public ClaimsTransformer(DbContext dbContext)
        {
            _context = dbContext;
        }
        public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
        {

            System.Security.Principal.WindowsIdentity windowsIdentity = null;

            foreach (var i in context.Principal.Identities)
            {
                //windows token
                if (i.GetType() == typeof(System.Security.Principal.WindowsIdentity))
                {
                    windowsIdentity = (System.Security.Principal.WindowsIdentity)i;
                }
            }

            if (windowsIdentity != null)
            {
                //find user in database by username
                var username = windowsIdentity.Name.Remove(0, 6);
                var appUser = _context.User.FirstOrDefault(m => m.Username == username);

                if (appUser != null)
                {

                    ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Id", Convert.ToString(appUser.Id)));

                    /*//add all claims from security profile
                    foreach (var p in appUser.Id)
                    {
                        ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim(p.Permission, "true"));
                    }*/

                }

            }
            return await System.Threading.Tasks.Task.FromResult(context.Principal);
        }
    }

【讨论】:

    【解决方案5】:

    你知道如何实现自定义System.Web.Security.MembershipProvider吗?您应该能够将它与 System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials() 结合使用(覆盖 ValidateUser)来针对 Active Directory 进行身份验证。

    尝试: var pc = new PrincipalContext(ContextType.Domain, "example.com", "DC=example,DC=com"); pc.ValidateCredentials(username, password);

    【讨论】:

    • 感谢您的回复。如果可能,您能否确认此建议适用于 ASP.NET5?
    • 不,我没有意识到你是专门问这个的。如果目录服务位于 .NET 核心中,我从未在 ASP.NET 5 和 IDK 上尝试过,但如果需要,您可以使用完整的 .NET。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-06
    • 1970-01-01
    相关资源
    最近更新 更多