How to use Windows Active Directory Authentication and Identity Based Claims?

前端 未结 5 1960
一向
一向 2020-12-07 12:21

Problem

We want to use Windows Active Directory to authenticate a user into the application. However, we do not want to use Active Directory groups to manage autho

相关标签:
5条回答
  • 2020-12-07 12:50

    You could use ClaimTransformation, I just got it working this afternoon using the article and code below. I am accessing an application with Window Authentication and then adding claims based on permissions stored in a SQL Database. This is a good article that should help you.

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

    In summary ...

    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);
            }
        }
    
    0 讨论(0)
  • 2020-12-07 12:54

    Just hit AD with the username and password instead of authenticating against your DB

    // 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);
        }
    }
    
    0 讨论(0)
  • 2020-12-07 12:56

    Do you know how to implement a custom System.Web.Security.MembershipProvider? You should be able to use this (override ValidateUser) in conjunction with System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials() to authenticate against active directory.

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

    0 讨论(0)
  • 2020-12-07 12:59

    Shoe your solution above pushed me toward a direction that worked for me on 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);
    }
    
    0 讨论(0)
  • 2020-12-07 13:07

    On ASPNET5 (beta6), the idea is to use CookieAuthentication and Identity : you'll need to add in your Startup class :

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

    In the configure section, add:

    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");
        });
    }
    

    Then, you will need to implement:

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

    and extend/override:

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

    I actually have setup a sample project to show how this can be done. GitHub Link.

    I tested on the beta8 and and with some small adaptatons (like Context => HttpContext) it worked too.

    0 讨论(0)
提交回复
热议问题