ASP.NET 5 Identity 3 用户在一段时间后注销
ASP.NET 5 Identity 3 users get signed out after some time
我正在使用 RC1 位和外部 (Google) 身份验证,没有 Identity.EntityFramework。
登录时,我设置了'Remember me'标志。
登录用户在浏览器重启(我看到 cookie 设置为 14 天后过期)和网站重启后仍然存在。
但是在闲置一段时间后(大约 15 分钟),无论 browser/site 是否重新启动,刷新页面都会导致注销,日志显示:
info: Microsoft.AspNet.Authentication.Cookies.CookieAuthenticationMiddleware:
AuthenticationScheme: Microsoft.AspNet.Identity.Application signed out.
AuthenticationScheme: Microsoft.AspNet.Identity.External signed out.
AuthenticationScheme: Microsoft.AspNet.Identity.TwoFactorUserId signed out.
这看起来像之前 ASP 中的 "sessions",但我在这里没有使用任何会话。
这是我本地的开发机器,没有IIS,直接Kestrel连接到5000端口,所以这不是
为什么用户被迫退出?
更新:我的Startup.cs
文件:
public void ConfigureServices(IServiceCollection services)
{
....
var identityBuilder = services
.AddIdentity<User, UserRole>(options =>
{
options.User.AllowedUserNameCharacters = null;
options.Cookies.ApplicationCookie.LoginPath = "/user/login";
options.Cookies.ApplicationCookie.LogoutPath = "/user/logout";
});
identityBuilder.Services
.AddScoped<IUserStore<User>, SportCmsDb>(serviceProvider => serviceProvider.GetService<SportCmsDb>())
.AddScoped<IRoleStore<UserRole>, SportCmsDb>(serviceProvider => serviceProvider.GetService<SportCmsDb>());
identityBuilder
.AddDefaultTokenProviders();
....
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
....
app.UseIdentity();
app.UseGoogleAuthentication(options =>
{
options.ClientId = Configuration["OAuth:Google:ClientId"];
options.ClientSecret = Configuration["OAuth:Google:Secret"];
});
....
SportCmsDb
是 DbContext
并且还实现了 IUserStore<User>
、IUserLoginStore<User>
、IUserEmailStore<User>
、IRoleStore<UserRole>
、IUserClaimStore<User>
更新 2
我启用了详细(调试)日志记录,发现当用户注销时 - 在此之前我的 IUserStore<User>.FindByIdAsync
被调用。使用 real/existing 用户 ID,函数返回正确的非空用户。一切似乎都很好。但是我的 load-from-db User
是 "rejected" 并被迫注销。没有可以显示 why/where.
的其他日志消息
哇,我解决了!
TL;DR
我需要在我的自定义 UserManager
(又名 SportCmsDb
)上实施 IUserSecurityStampStore<User>
。
详情
在 AddIdentity
调用期间(在 Startup.cs
ConfigureServices
方法中)IdentityOptions
are configured with default instance of IdentityCookieOptions
. In constructor of IdentityCookieOptions
instance of ApplicationCookie
(of type CookieAuthenticationOptions
) is created with handler CookieAuthenticationEvents.OnValidatePrincipal
set to SecurityStampValidator.ValidatePrincipalAsync
静态方法。
在 UseIdentity
调用期间(在 Startup.cs
Configure
方法中)CookieAuthenticationMiddleware
是 configured with IdentityOptions.Cookies.ApplicationCookie
options。
CookieAuthenticationHandler
(由 CookieAuthenticationMiddleware
创建)在其 HandleAuthenticateAsync
方法中从 cookie 读取票证并调用 Options.Events.ValidatePrincipal 处理程序进行验证。
实际上,调用了 SecurityStampValidator.ValidatePrincipalAsync
。此方法检查 enough time has elapsed since cookie was issued (30 min by default) 并调用 ISecurityStampValidator.validateAsync
(第 81-82 行)。
ISecurityStampValidator
的默认实现是 SecurityStampValidator<TUser>
。它调用 SignInManager<TUser>.ValidateSecurityStampAsync
并在返回 null 时 - 拒绝主体并强制用户注销(第 30-40 行)。
SignInManager<TUser>
在其 ValidateSecurityStampAsync method 中尝试从 User
和 returns 读取安全标记,如果不能(如果 UserManager<User>
不支持此界面)或标记与保存的(在 cookie 中)不匹配。
我的自定义 UserManager
没有实现 IUserSecurityStampStore<User>
。宾果。
非常感谢您之前的回答,我今天处理了这个问题并解决了这个问题:
1.- 自定义 UserStore.cs:
public class UserStore : IUserStore<User>,
IUserPasswordStore<User>,
IUserEmailStore<User>,
IUserRoleStore<User>,
IUserSecurityStampStore<User>
{
//omitted...
public Task SetSecurityStampAsync(User user, string stamp, CancellationToken cancellationToken = default(CancellationToken))
{
user.SecurityStamp = stamp;
return Task.FromResult(0);
}
public Task<string> GetSecurityStampAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
if (user.SecurityStamp == null) {
return Task.FromResult("AspNet.Identity.SecurityStamp");
}
return Task.FromResult(user.SecurityStamp);
}
}
2.- 在 User.cs 和 DB Table 用户中添加 SecurityStamp 作为字符串。
对于测试,将 Startup.cs 中的默认 30m 更改为 1m:
services.Configure<SecurityStampValidatorOptions>(options => {
options.ValidationInterval = TimeSpan.FromMinutes(1);
});
就我而言,我使用的是身份服务器,我遇到了同样的问题。问题是我一开始就没有将 SecurityStamp 声明添加到 cookie,所以当调用 SecurityStampValidator 时它总是返回 false。
之前的代码:
var user = await this.loginService.GetOrCreateUser(claimsPrincipal, provider);
var localSignInProps = this.GetAuthenticationProperties(result);
// issue authentication cookie for user
var identityServerUser = new IdentityServerUser(user.Id)
{
DisplayName = user.UserName,
IdentityProvider = provider
};
await this.HttpContext.SignInAsync(identityServerUser, localSignInProps);
之后的代码:
var user = await this.loginService.GetOrCreateUser(claimsPrincipal, provider);
var principal = await this.signInManager.CreateUserPrincipalAsync(user);
var localSignInProps = this.GetAuthenticationProperties(result);
// issue authentication cookie for user
var identityServerUser = new IdentityServerUser(user.Id)
{
DisplayName = user.UserName,
IdentityProvider = provider,
AdditionalClaims = principal.Claims.ToList(),
};
await this.HttpContext.SignInAsync(identityServerUser, localSignInProps);
主体包含如下所示的声明:
{AspNet.Identity.SecurityStamp: BBDVFD2WCAZTMA75O3IQKPUWNKKOZOL5}
我正在使用 RC1 位和外部 (Google) 身份验证,没有 Identity.EntityFramework。
登录时,我设置了'Remember me'标志。
登录用户在浏览器重启(我看到 cookie 设置为 14 天后过期)和网站重启后仍然存在。
但是在闲置一段时间后(大约 15 分钟),无论 browser/site 是否重新启动,刷新页面都会导致注销,日志显示:
info: Microsoft.AspNet.Authentication.Cookies.CookieAuthenticationMiddleware:
AuthenticationScheme: Microsoft.AspNet.Identity.Application signed out.
AuthenticationScheme: Microsoft.AspNet.Identity.External signed out.
AuthenticationScheme: Microsoft.AspNet.Identity.TwoFactorUserId signed out.
这看起来像之前 ASP 中的 "sessions",但我在这里没有使用任何会话。
这是我本地的开发机器,没有IIS,直接Kestrel连接到5000端口,所以这不是
为什么用户被迫退出?
更新:我的Startup.cs
文件:
public void ConfigureServices(IServiceCollection services)
{
....
var identityBuilder = services
.AddIdentity<User, UserRole>(options =>
{
options.User.AllowedUserNameCharacters = null;
options.Cookies.ApplicationCookie.LoginPath = "/user/login";
options.Cookies.ApplicationCookie.LogoutPath = "/user/logout";
});
identityBuilder.Services
.AddScoped<IUserStore<User>, SportCmsDb>(serviceProvider => serviceProvider.GetService<SportCmsDb>())
.AddScoped<IRoleStore<UserRole>, SportCmsDb>(serviceProvider => serviceProvider.GetService<SportCmsDb>());
identityBuilder
.AddDefaultTokenProviders();
....
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
....
app.UseIdentity();
app.UseGoogleAuthentication(options =>
{
options.ClientId = Configuration["OAuth:Google:ClientId"];
options.ClientSecret = Configuration["OAuth:Google:Secret"];
});
....
SportCmsDb
是 DbContext
并且还实现了 IUserStore<User>
、IUserLoginStore<User>
、IUserEmailStore<User>
、IRoleStore<UserRole>
、IUserClaimStore<User>
更新 2
我启用了详细(调试)日志记录,发现当用户注销时 - 在此之前我的 IUserStore<User>.FindByIdAsync
被调用。使用 real/existing 用户 ID,函数返回正确的非空用户。一切似乎都很好。但是我的 load-from-db User
是 "rejected" 并被迫注销。没有可以显示 why/where.
哇,我解决了!
TL;DR
我需要在我的自定义 UserManager
(又名 SportCmsDb
)上实施 IUserSecurityStampStore<User>
。
详情
在 AddIdentity
调用期间(在 Startup.cs
ConfigureServices
方法中)IdentityOptions
are configured with default instance of IdentityCookieOptions
. In constructor of IdentityCookieOptions
instance of ApplicationCookie
(of type CookieAuthenticationOptions
) is created with handler CookieAuthenticationEvents.OnValidatePrincipal
set to SecurityStampValidator.ValidatePrincipalAsync
静态方法。
在 UseIdentity
调用期间(在 Startup.cs
Configure
方法中)CookieAuthenticationMiddleware
是 configured with IdentityOptions.Cookies.ApplicationCookie
options。
CookieAuthenticationHandler
(由 CookieAuthenticationMiddleware
创建)在其 HandleAuthenticateAsync
方法中从 cookie 读取票证并调用 Options.Events.ValidatePrincipal 处理程序进行验证。
实际上,调用了 SecurityStampValidator.ValidatePrincipalAsync
。此方法检查 enough time has elapsed since cookie was issued (30 min by default) 并调用 ISecurityStampValidator.validateAsync
(第 81-82 行)。
ISecurityStampValidator
的默认实现是 SecurityStampValidator<TUser>
。它调用 SignInManager<TUser>.ValidateSecurityStampAsync
并在返回 null 时 - 拒绝主体并强制用户注销(第 30-40 行)。
SignInManager<TUser>
在其 ValidateSecurityStampAsync method 中尝试从 User
和 returns 读取安全标记,如果不能(如果 UserManager<User>
不支持此界面)或标记与保存的(在 cookie 中)不匹配。
我的自定义 UserManager
没有实现 IUserSecurityStampStore<User>
。宾果。
非常感谢您之前的回答,我今天处理了这个问题并解决了这个问题:
1.- 自定义 UserStore.cs:
public class UserStore : IUserStore<User>,
IUserPasswordStore<User>,
IUserEmailStore<User>,
IUserRoleStore<User>,
IUserSecurityStampStore<User>
{
//omitted...
public Task SetSecurityStampAsync(User user, string stamp, CancellationToken cancellationToken = default(CancellationToken))
{
user.SecurityStamp = stamp;
return Task.FromResult(0);
}
public Task<string> GetSecurityStampAsync(User user, CancellationToken cancellationToken = default(CancellationToken))
{
if (user.SecurityStamp == null) {
return Task.FromResult("AspNet.Identity.SecurityStamp");
}
return Task.FromResult(user.SecurityStamp);
}
}
2.- 在 User.cs 和 DB Table 用户中添加 SecurityStamp 作为字符串。
对于测试,将 Startup.cs 中的默认 30m 更改为 1m:
services.Configure<SecurityStampValidatorOptions>(options => {
options.ValidationInterval = TimeSpan.FromMinutes(1);
});
就我而言,我使用的是身份服务器,我遇到了同样的问题。问题是我一开始就没有将 SecurityStamp 声明添加到 cookie,所以当调用 SecurityStampValidator 时它总是返回 false。
之前的代码:
var user = await this.loginService.GetOrCreateUser(claimsPrincipal, provider);
var localSignInProps = this.GetAuthenticationProperties(result);
// issue authentication cookie for user
var identityServerUser = new IdentityServerUser(user.Id)
{
DisplayName = user.UserName,
IdentityProvider = provider
};
await this.HttpContext.SignInAsync(identityServerUser, localSignInProps);
之后的代码:
var user = await this.loginService.GetOrCreateUser(claimsPrincipal, provider);
var principal = await this.signInManager.CreateUserPrincipalAsync(user);
var localSignInProps = this.GetAuthenticationProperties(result);
// issue authentication cookie for user
var identityServerUser = new IdentityServerUser(user.Id)
{
DisplayName = user.UserName,
IdentityProvider = provider,
AdditionalClaims = principal.Claims.ToList(),
};
await this.HttpContext.SignInAsync(identityServerUser, localSignInProps);
主体包含如下所示的声明:
{AspNet.Identity.SecurityStamp: BBDVFD2WCAZTMA75O3IQKPUWNKKOZOL5}