Nous voulons utiliser Windows Active Directory pour authentifier un utilisateur dans l'application. Cependant, nous ne voulons pas utiliser de groupes Active Directory pour gérer l'autorisation des contrôleurs/vues.
Pour autant que je sache, il n'y a pas de moyen facile de marier AD et revendications basées sur l'identité.
Toute aide serait plus qu'appréciée. Je suis coincé sur ce problème depuis assez longtemps et j'apprécierais une contribution extérieure sur la question.
Shoe votre solution ci-dessus m'a poussé vers une direction qui a fonctionné pour moi sur 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);
}
Appuyez simplement sur AD avec le nom d'utilisateur et le mot de passe au lieu de vous authentifier sur votre base de données
// 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);
}
}
Sur ASPNET5 (beta6), l'idée est d'utiliser CookieAuthentication et Identity: vous devrez ajouter dans votre classe de démarrage:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization();
services.AddIdentity<MyUser, MyRole>()
.AddUserStore<MyUserStore<MyUser>>()
.AddRoleStore<MyRoleStore<MyRole>>()
.AddUserManager<MyUserManager>()
.AddDefaultTokenProviders();
}
Dans la section de configuration, ajoutez:
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");
});
}
Ensuite, vous devrez implémenter:
Microsoft.AspNet.Identity.IUserStore
Microsoft.AspNet.Identity.IRoleStore
Microsoft.AspNet.Identity.IUserClaimsPrincipalFactory
et étendre/remplacer:
Microsoft.AspNet.Identity.UserManager
Microsoft.AspNet.Identity.SignInManager
J'ai en fait configuré un exemple de projet pour montrer comment cela peut être fait. Lien GitHub .
J'ai testé sur la beta8 et avec quelques petites adaptations (comme Context => HttpContext) cela a fonctionné aussi.
Vous pouvez utiliser ClaimTransformation, je viens de le faire fonctionner cet après-midi en utilisant l'article et le code ci-dessous. J'accède à une application avec l'authentification Windows, puis j'ajoute des revendications basées sur des autorisations stockées dans une base de données SQL. Ceci est un bon article qui devrait vous aider.
https://github.com/aspnet/Security/issues/86
En résumé ...
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);
}
}
Savez-vous comment implémenter un System.Web.Security.MembershipProvider
Personnalisé? Vous devriez pouvoir utiliser ceci (remplacer ValidateUser
) conjointement avec System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials()
pour vous authentifier auprès d'Active Directory.
essayez: var pc = new PrincipalContext(ContextType.Domain, "example.com", "DC=example,DC=com"); pc.ValidateCredentials(username, password);