У меня есть .NET Core 2.2 MVC веб-приложение. И я добавил туда два типа аутентификации / провайдеров:
- Логин / пароль с базой данных локальных пользователей (пользовательская вещь, без .NET Core Identity )
- Azure AD
Моя цель - создать страницу входа в систему /account/login
, где пользователи могут выбирать между этими двумя аутентификациями и входить в систему с помощью любой из них. Таким образом, каждый раз, когда неаутентифицированный пользователь открывает любую страницу (с контроллера с [Authorize]
attrubite), он перенаправляется на страницу /account/login
, которая имеет веб-форму логина / пароля с собственной кнопкой отправки и, кроме того, Office 365 login
ссылка / кнопка.
Просто чтобы прояснить ситуацию - мне не нужна пользовательская страница входа Microsoft / Azure AD. Я только хочу, чтобы неаутентифицированные пользователи сначала получали мою страницу входа, откуда они могут либо войти в систему, используя мою веб-форму, либо щелкнуть Office 365 login
и перейти на страницу входа Microsoft.
Теперь проверка подлинностичасть выполнена и, кажется, работает нормально, я могу войти с любой из аутентификаций, но мой план с перенаправлением неаутентифицированного пользователя на /account/login
не удался. Вместо этого пользователь сразу же перенаправляется на страницу входа Microsoft. Таким образом, похоже, что аутентификация Azure AD имеет более высокий приоритет.
Вот моя реализация.
Startup.cs
:
// ...
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.None;
});
// the presence of CookieAuthenticationDefaults.AuthenticationScheme doesn't seem to influence anything
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
// makes no difference either
//services.AddAuthentication(
// options =>
// {
// options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// }
//)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, // and it also can be omitted here
options =>
{
options.LoginPath = "/Account/Login";
options.ExpireTimeSpan = TimeSpan.FromDays(45);
})
.AddAzureAD(options => _configuration.Bind("AzureAD", options));
services.AddAuthorization(options =>
{
// as the default policy, it applies to all [Authorize] controllers
options.DefaultPolicy = new AuthorizationPolicyBuilder(
CookieAuthenticationDefaults.AuthenticationScheme,
AzureADDefaults.AuthenticationScheme
)
.RequireAuthenticatedUser() // a simple policy that only requires a user to be authenticated
.Build();
});
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
services.AddMvc(options =>
{
// it is my understanding that there is no need create a policy here
// and perform "options.Filters.Add(new AuthorizeFilter(policy))",
// because the default policy is already added and controllers have explicit [Authorize] attribute
// [...] well, actually I tried that too, but it didn't change anything
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory
)
{
// ...
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}"
);
});
}
AccountController.cs
:
[Authorize]
[Route("account")]
public class AccountController : Controller
{
// ...
// that is where "Office 365 login" link leads
[HttpGet("login-ad")]
[AllowAnonymous]
public IActionResult LoginAD(string returnUrl = null)
{
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Account");
}
else
{
if (string.IsNullOrEmpty(returnUrl)) { returnUrl = "/"; }
return Challenge(
new AuthenticationProperties { RedirectUri = returnUrl },
AzureADDefaults.AuthenticationScheme
);
}
}
[HttpGet("login")]
[AllowAnonymous]
public IActionResult Login(string returnUrl = null)
{
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Account");
}
ViewData["ReturnUrl"] = returnUrl;
return View();
}
// that is where login/password web-form submits to
[HttpPost("login")]
[AllowAnonymous]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
if (ModelState.IsValid)
{
await _usersManager.SignIn(
model.Login,
model.Password
);
// ...
return LocalRedirect(returnUrl);
}
ViewData["ReturnUrl"] = returnUrl;
return View(model);
}
// ...
}
HomeController.cs
:
[Authorize]
public class HomeController : Controller
{
// ...
public IActionResult Index()
{
return View();
}
// ...
}
Итак, открытие любой страницы неаутентифицированным пользователем приводит к немедленному перенаправлению на страницу входа Microsoft. А чтобы получить /account/login
(чтобы иметь возможность войти в систему с использованием другой аутентификации), пользователям необходимо явно открыть этот URL-адрес.
Если я удаляю AzureADDefaults.AuthenticationScheme
из политики по умолчанию, то все неаутентифицированные запросытеперь будет перенаправлено на /account/login
- именно то, что я хочу - но, естественно, аутентификация Azure AD больше не работает:
Эти перенаправления говорят мне об этом после успешной аутентификации в MicrosoftСтраница входа возвращает пользователя на /account/login
, но пользователь все еще не аутентифицирован на моем веб-сайте.
Я, конечно, могу добавить [AllowAnonymous]
к Index
действию HomeController
и вернуть перенаправление на/account/login
для неаутентифицированных пользователей, но это, очевидно, будет работать только для /
маршрута.
У меня такое чувство, что я не понимаю некоторые вещи о AddAuthentication()
, схемах и политиках, таким образом, очевидно, я сделалчто-то не так в Startup.cs
. Можете ли вы помочь мне понять, что там не так? Или, может быть, есть какой-то другой способ добиться того, чего я хочу?