基于Microsoft.Owin实现改密码后除当前设备外全设备登出
修改密码时强制其他设备登出的实现方案
要实现修改密码后踢掉当前设备外的所有登录会话,核心思路是追踪每个用户的登录会话状态,在密码变更时标记非当前会话为无效,并在后续请求中验证会话有效性。以下是具体实现步骤:
1. 给每个登录会话分配唯一标识
用户登录时,生成一个唯一的会话ID(如Guid),将其与用户ID关联存入数据库,同时把这个会话ID添加到用户的身份凭证(Claims)中,确保每个请求都能携带当前会话的标识。
示例代码(登录逻辑):
// 生成唯一会话ID var sessionId = Guid.NewGuid().ToString(); // 保存会话记录到数据库 _db.UserSessions.Add(new UserSession { UserId = currentUserId, SessionId = sessionId, IsValid = true, CreatedAt = DateTime.UtcNow }); _db.SaveChanges(); // 创建身份凭证并加入会话ID Claim var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie); identity.AddClaim(new Claim("SessionId", sessionId)); // 添加其他必要Claims(如用户名、用户ID等) // 完成登录 HttpContext.GetOwinContext().Authentication.SignIn( new AuthenticationProperties { IsPersistent = rememberMe }, identity );
2. 修改密码时标记非当前会话为无效
当用户密码修改成功后,从当前用户的Claims中获取当前会话ID,然后将数据库中该用户的其他所有会话标记为无效(或直接删除)。
示例代码(密码修改成功后的处理):
if (passwordChangeSuccess) { var currentSessionId = User.Claims.FirstOrDefault(c => c.Type == "SessionId")?.Value; var currentUserId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (!string.IsNullOrEmpty(currentSessionId) && !string.IsNullOrEmpty(currentUserId)) { // 筛选出当前用户的非当前会话,标记为无效 var invalidSessions = _db.UserSessions .Where(s => s.UserId == currentUserId && s.SessionId != currentSessionId); foreach (var session in invalidSessions) { session.IsValid = false; } _db.SaveChanges(); } }
3. 拦截请求验证会话有效性
通过自定义Action过滤器,在每个需要身份验证的请求中检查当前会话是否有效。如果会话已被标记为无效,立即强制用户登出并跳转至登录页。
示例代码(自定义Action过滤器):
public class ValidateSessionAttribute : ActionFilterAttribute { private readonly YourDbContext _db; public ValidateSessionAttribute(YourDbContext db) { _db = db; } public override void OnActionExecuting(ActionExecutingContext filterContext) { var httpContext = filterContext.HttpContext; var user = httpContext.User; if (user.Identity.IsAuthenticated) { var sessionId = user.Claims.FirstOrDefault(c => c.Type == "SessionId")?.Value; var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (!string.IsNullOrEmpty(sessionId) && !string.IsNullOrEmpty(userId)) { // 检查会话是否有效 var validSession = _db.UserSessions .Any(s => s.UserId == userId && s.SessionId == sessionId && s.IsValid); if (!validSession) { // 会话无效,强制登出 httpContext.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie); filterContext.Result = new RedirectResult("/Account/Login"); } } } base.OnActionExecuting(filterContext); } }
将该过滤器注册到全局或需要验证的控制器/Action上,确保每次请求都经过会话有效性校验。
补充说明
- 定期清理数据库中过期的无效会话,避免数据冗余。
- 如果使用JWT令牌而非Cookie认证,逻辑类似:将会话ID存入JWT的Claims中,修改密码后将其他会话ID加入黑名单,在令牌验证时检查是否在黑名单内。
内容的提问来源于stack exchange,提问作者Sebi O.
相关产品推荐
相关产品推荐

