【发布时间】:2018-12-16 00:36:54
【问题描述】:
我在使用 ASP.NET Core 2.1 网站时遇到了一个奇怪的问题。当我登录它并在 30 分钟后刷新它时,我总是会抛出这个异常:
InvalidOperationException:没有为方案“Identity.External”注册注销身份验证处理程序。注册的注销方案是:Identity.Application。您是否忘记调用 AddAuthentication().AddCookies("Identity.External",...)?
我没有注册Identity.External 是正确的,但我也不希望它注册。为什么它一直试图退出?以下是我注册 cookie 的方式:
services.AddAuthentication(
o => {
o.DefaultScheme = IdentityConstants.ApplicationScheme;
}).AddCookie(IdentityConstants.ApplicationScheme,
o => {
o.Events = new CookieAuthenticationEvents {
OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
};
});
services.ConfigureApplicationCookie(
o => {
o.Cookie.Expiration = TimeSpan.FromHours(2);
o.Cookie.HttpOnly = true;
o.Cookie.SameSite = SameSiteMode.Strict;
o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
o.AccessDeniedPath = "/admin";
o.LoginPath = "/admin";
o.LogoutPath = "/admin/sign-out";
o.SlidingExpiration = true;
});
有人可以为我指出如何解决这个问题的正确方向吗?
更新
这里是 @Edward 在 cmets 中要求的完整代码和使用过程。为简洁起见,我省略了一些部分。
Startup.cs
public sealed class Startup {
public void ConfigureServices(
IServiceCollection services) {
// ...
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddApplicationIdentity();
services.AddScoped<ApplicationSignInManager>();
services.Configure<IdentityOptions>(
o => {
o.Password.RequiredLength = 8;
o.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
o.Lockout.MaxFailedAccessAttempts = 5;
});
services.ConfigureApplicationCookie(
o => {
o.Cookie.Name = IdentityConstants.ApplicationScheme;
o.Cookie.Expiration = TimeSpan.FromHours(2);
o.Cookie.HttpOnly = true;
o.Cookie.SameSite = SameSiteMode.Strict;
o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
o.AccessDeniedPath = "/admin";
o.LoginPath = "/admin";
o.LogoutPath = "/admin/sign-out";
o.SlidingExpiration = true;
});
// ...
}
public void Configure(
IApplicationBuilder app) {
// ...
app.UseAuthentication();
// ...
}
}
ServiceCollectionExtensions.cs
public static class ServiceCollectionExtensions {
public static IdentityBuilder AddApplicationIdentity(
this IServiceCollection services) {
services.AddAuthentication(
o => {
o.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
o.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
o.DefaultForbidScheme = IdentityConstants.ApplicationScheme;
o.DefaultSignInScheme = IdentityConstants.ApplicationScheme;
o.DefaultSignOutScheme = IdentityConstants.ApplicationScheme;
}).AddCookie(IdentityConstants.ApplicationScheme,
o => {
o.Events = new CookieAuthenticationEvents {
OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
};
});
services.TryAddScoped<SignInManager<User>, ApplicationSignInManager>();
services.TryAddScoped<IPasswordHasher<User>, PasswordHasher<User>>();
services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
services.TryAddScoped<IdentityErrorDescriber>();
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<User>>();
services.TryAddScoped<IUserClaimsPrincipalFactory<User>, UserClaimsPrincipalFactory<User>>();
services.TryAddScoped<UserManager<User>>();
services.TryAddScoped<IUserStore<User>, ApplicationUserStore>();
return new IdentityBuilder(typeof(User), services);
}
}
DefaultController.cs
[Area("Admin")]
public sealed class DefaultController :
AdminControllerBase {
[HttpPost, AllowAnonymous]
public async Task<IActionResult> SignIn(
SignIn.Command command) {
var result = await Mediator.Send(command);
if (result.Succeeded) {
return RedirectToAction("Dashboard", new {
area = "Admin"
});
}
return RedirectToAction("SignIn", new {
area = "Admin"
});
}
[HttpGet, ActionName("sign-out")]
public async Task<IActionResult> SignOut() {
await Mediator.Send(new SignOut.Command());
return RedirectToAction("SignIn", new {
area = "Admin"
});
}
}
SignIn.cs
public sealed class SignIn {
public sealed class Command :
IRequest<SignInResult> {
public string Password { get; set; }
public string Username { get; set; }
}
public sealed class CommandHandler :
HandlerBase<Command, SignInResult> {
private ApplicationSignInManager SignInManager { get; }
public CommandHandler(
DbContext context,
ApplicationSignInManager signInManager)
: base(context) {
SignInManager = signInManager;
}
protected override SignInResult Handle(
Command command) {
var result = SignInManager.PasswordSignInAsync(command.Username, command.Password, true, false).Result;
return result;
}
}
}
SignOut.cs
public sealed class SignOut {
public sealed class Command :
IRequest {
}
public sealed class CommandHandler :
HandlerBase<Command> {
private ApplicationSignInManager SignInManager { get; }
public CommandHandler(
DbContext context,
ApplicationSignInManager signInManager)
: base(context) {
SignInManager = signInManager;
}
protected override async void Handle(
Command command) {
await SignInManager.SignOutAsync();
}
}
}
这里有所有相关的代码,从我如何配置身份到我如何登录和退出。我仍然不明白为什么 Identity.External 在我从未要求过的情况下出现在画面中。
从技术上讲,SignIn 和 SignOut 类可以删除,它们的功能合并到 DefaultController 中,但是我选择保留它们以保持应用程序结构一致。
【问题讨论】:
-
我遇到过那个帖子,但它对我没有帮助。我要注意的一件事是我使用
SignInManager.PasswordSignInAsync方法登录,因为我需要检查结果和SignInManager.SingOutAsync用于注销。 -
您能否与我们分享完整的代码和详细步骤来重现您的问题?
-
@Edward,我已根据您的要求使用相关代码更新了我的帖子。
标签: c# asp.net-core asp.net-identity