Это похоже на IdentityServer4 Заставить пользователя повторно ввести учетные данные, но в решении говорится использовать строку запроса prompt=login
в URL-адресе /authorize
, что работает, но также позволяет хитрым пользователям удалить ее. Кроме того, поскольку я не использую .AddOpenIdConnect()
, предложение использовать OnRedirectToIdentityProvider
ко мне не относится.
Итак, как мы можем заставить пользователя всегда вводить учетные данные, не полагаясь на prompt=login
в строке запроса?
Вот моя базовая настройка IdentityServer4:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var builder = services.AddIdentityServer()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApis())
.AddInMemoryClients(Config.GetClients());
builder.AddDeveloperSigningCredential();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHttpsRedirection();
app.UseFileServer();
app.UseIdentityServer();
app.UseMvc();
}
Где на моей странице входа есть только кнопки «ОК» и «Отмена» (мне все равно, какой пользователь входит в систему, только эта аутентификация была в порядке или нет), а контроллер делает следующее, когда он аутентифицирует пользователя:
public async Task<IActionResult> Post(AuthenticateViewModel model, string button)
{
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
if (button == "authCancel")
{
if (context != null)
{
await _interaction.GrantConsentAsync(context, ConsentResponse.Denied);
return Redirect(model.ReturnUrl);
}
return Redirect("~/");
}
if (button == "authOk" && ModelState.IsValid)
{
if (context != null)
{
await _events.RaiseAsync(new UserLoginSuccessEvent("provider", "userId", "subjectId", "name"));
await HttpContext.SignInAsync("subject", "name", new AuthenticationProperties());
return Redirect(model.ReturnUrl);
}
}
return Redirect("~/");
}
Звучит неплохо. Хотите привести пример?
Вы должны иметь возможность добиться желаемого поведения, переопределив схему файлов cookie по умолчанию, которая AddIdentityServer()
регистрируется внутри:
services.AddIdentityServer()...
services.AddAuthentication("CustomScheme")
.AddCookie("CustomScheme", options =>
{
options.ExpireTimeSpan = ...;
});
Убедитесь, что вы добавили схему переопределения после AddIdentityServer()
, последовательность здесь важна из-за того, как работает ASP.Net Core DI.
Спасибо. Похоже, это основано на истечении срока действия файлов cookie. Каких значений обычно достаточно? Я предполагаю, что 0 секунд слишком мало, и пользователь уже снова выйдет из системы на auhorize/callback
, а 10 секунд позволяют быстрым пользователям повторить попытку до истечения срока действия.
Просто выберите что-то разумное, подходящее для вашей бизнес-проблемы, чего я не могу посоветовать. Я использовал истечение нескольких минут для аналогичного требования, как у вас в прошлом.
Я попробовал это и вижу в журнале следующие сообщения: CustomScheme was not authenticated. Failure message: Ticket expired
, даже по истечении нескольких минут. Есть идеи, почему?
Кажется, что это появляется много раз, когда вызывается authorize
(вероятно, чтобы проверить, вошел ли пользователь в систему), но не после того, как пользователь прошел аутентификацию. Однако кажется немного ненужным заполнять журнал многими из этих сообщений журнала. Какие-либо предложения?
Попробуйте 10 минут (из-за перекоса часов) и дайте файлу cookie какое-то пользовательское имя и проверьте, сохраняется ли правильный файл cookie при входе пользователя в систему.
Вариант может заключаться в том, чтобы придерживаться prompt=login
для всех запросов или на основе некоторых настроек клиента или заголовка http.
Легко заглянуть в валидатор запросов по умолчанию и реализовать свою настройку следующим образом:
public class YourCustomAuthorizeRequestValidator:ICustomAuthorizeRequestValidator
{
public Task ValidateAsync(CustomAuthorizeRequestValidationContext context)
{
var request = context.Result.ValidatedRequest;
if (string.IsNullOrWhiteSpace(request.Raw["prompted"]))
{
request.Raw.Add("prompted", "true");
request.PromptMode = OidcConstants.PromptModes.Login;
}
else if (request.Subject.IsAuthenticated())
{
request.PromptMode = OidcConstants.PromptModes.None;
}
return Task.CompletedTask;
}
}
а затем при запуске Identityserver:
services.AddIdentityServer()
.AddCustomAuthorizeRequestValidator<YourCustomAuthorizeRequestValidator>();
Я попытался добавить пользовательский ICustomAuthorizeRequestValidator
из вашего примера, и если он должен принудительно входить в систему при каждом запросе на authorize
, это не совсем работает. Я звоню authorize
и он первый раз просит логин. Во второй раз он не запрашивает логин и переходит прямо к redirect_uri
.
Я пытался закомментировать условие, но оно никогда не продолжается после /authorize
. Я получаю экран входа в систему, нажимаю, чтобы войти, затем я снова получаю экран входа (повторяю до бесконечности)
Вероятно, вам следует обновить свой код, чтобы использовать string.IsNullOrEmpty()
вместо .IsMissing()
, но в остальном это, кажется, делает именно то, что я хочу. Спасибо :)
IsMissing()
- это просто удобная оболочка return string.IsNullOrWhiteSpace(value);
, но, возможно, я украл ее из внутренностей Identityserver... тогда изменится
Это довольно просто: просто переопределите схему файлов cookie по умолчанию, и пользователям всегда придется вводить учетные данные, несмотря на взлом URL-адресов.