【发布时间】:2020-05-28 14:49:45
【问题描述】:
Web 应用程序应允许拥有 AD 帐户的内部员工使用 Azure AD 身份验证在应用程序中进行身份验证。外部用户应该能够使用 ASP.NET Core Identity 进行注册和登录。我可以单独实现每一个,但不能在同一个应用程序中一起实现。当我将两种身份验证添加到同一个应用程序时,ASP.NET Core Identity 可以完美运行。我可以毫无问题地使用身份注册和登录。但是,当我尝试使用 Azure AD 登录时,应用程序将我重定向到租户的登录页面,我提交了用户名和密码,它将我重定向回应用程序,但没有用户通过身份验证。我再次点击登录按钮,同样的事情发生了。似乎网络应用程序或浏览器没有保存访问令牌或类似的东西。
我做错了什么?甚至可以在同一个应用程序上进行两组身份验证吗?
谢谢。代码如下:
包
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureAD.UI" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.1">
启动类
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration) => Configuration = configuration;
public void ConfigureServices(IServiceCollection services)
{
// Add Azure AD authentication
services.AddAuthentication(defaultScheme: AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
// Add the application db context
services.AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// Add Identity using Entity Framework Core
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
// Configure Identity
services.Configure<IdentityOptions>(options =>
{
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
options.User.RequireUniqueEmail = true;
});
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => endpoints.MapControllers());
}
用户控制器
这是一个自定义控制器,我们在其中处理与身份验证相关的 HTTP 请求。
private readonly UserManager<ApplicationUser> userManager;
private readonly SignInManager<ApplicationUser> signInManager;
public UserController(UserManager<ApplicationUser> um, SignInManager<ApplicationUser> sm) =>
(userManager, signInManager) = (um, sm);
// Internal employee users will authenticate using Azure AD
[HttpGet("internal-signin")]
public ChallengeResult InternalSignIn(string returnUrl = "/") =>
Challenge(new AuthenticationProperties { RedirectUri = returnUrl }, AzureADDefaults.AuthenticationScheme);
// Display view with a form to create a new external user account
[HttpGet("register")]
public ViewResult Register() => View();
// Create a new account for an external user
[HttpPost("register")]
public async Task<IActionResult> Register(RegistrationInputModel inputModel)
{
// Check if the model state is valid
if (!ModelState.IsValid)
{
// Redirect to the Register view
return View(viewName: nameof(Register), model: inputModel);
}
// Create an application user object
ApplicationUser user = new ApplicationUser
{
// Map the fields of the input model with the user
UserName = inputModel.Email,
Email = inputModel.Email,
FirstName = inputModel.FirstName,
LastName = inputModel.LastName,
Company = inputModel.CompanyName,
};
// Try to register the user on the database
IdentityResult result = await userManager.CreateAsync(user, inputModel.Password);
// If failed, then set the error messages into the model state
if (!result.Succeeded)
{
foreach (IdentityError error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
// Return the user to the registration view
return View(viewName: nameof(Register), model: inputModel);
}
// Sign In the user
await signInManager.SignInAsync(user, isPersistent: false);
// Otherwise, redirect the user to the index page
return RedirectToAction(nameof(HomeController.Index), controllerName: "Home");
}
// External users sign out action
[HttpGet("signout")]
[Authorize]
public async Task<IActionResult> SignOut()
{
await signInManager.SignOutAsync();
return RedirectToAction(nameof(HomeController.Index), "Home");
}
// Display form to login for external users
[HttpGet("signin")]
public ViewResult SignIn() => View();
// Login an external user
[HttpPost("signin")]
public async Task<IActionResult> SingIn(SingInInputModel inputModel)
{
// Check if the model state is valid
if (!ModelState.IsValid)
{
// Send the user back to the sign in view
return View(viewName: nameof(SignIn), model: inputModel);
}
// Try to sign in the user
SignInResult result = await signInManager
.PasswordSignInAsync(inputModel.Email, inputModel.Password, inputModel.RememberMe, lockoutOnFailure: false);
// Check if the login was unsuccessful
if (!result.Succeeded)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(viewName: nameof(SignIn), model: inputModel);
}
// Send the user back to the index page
return RedirectToAction(nameof(HomeController.Index), "Home");
}
应用用户
public class ApplicationUser : Microsoft.AspNetCore.Identity.IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
}
【问题讨论】:
-
使用 Azure AD B2C 不是更好的解决方案吗?它具有多个 OAuth 选项,以及与来自不同组织的不同 AD 组协作的选项。所有这些都是在 Azure 云上完成的,因此可以节省大量时间。
标签: c# azure asp.net-core asp.net-core-identity