You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 02:56:14