using Dpz.Core.Service.Mediator.Features.Security.Queries;
using Microsoft.AspNetCore.Authentication;
namespace Dpz.Core.Auth.Middleware;
/// <summary>
/// SecurityStamp验证中间件
/// 用于在密码修改后自动登出其他浏览器的会话
/// </summary>
public class SecurityStampValidationMiddleware(
RequestDelegate next,
ILogger<SecurityStampValidationMiddleware> logger
)
{
public async Task InvokeAsync(
HttpContext context,
IMediator mediator,
IAccountSessionService accountSessionService
)
{
// 只对已认证的用户进行验证
if (context.User.Identity?.IsAuthenticated == true)
{
var account = context.User.NameIdentifier;
var claimKey = context.User.FindFirst("Key")?.Value;
var sessionId = context.User.SessionId;
if (
!string.IsNullOrWhiteSpace(sessionId)
&& await accountSessionService.IsRevokedAsync(sessionId, context.RequestAborted)
)
{
logger.LogInformation(
"用户 {Account} 的会话 {SessionId} 已撤销",
account,
sessionId
);
await context.SignOutAsync();
context.Response.Redirect("/auth-status/session-expired");
return;
}
if (!string.IsNullOrEmpty(account) && !string.IsNullOrEmpty(claimKey))
{
try
{
var result = await mediator.Send(
new ValidateUserSecurityStampRequest
{
UserId = account,
SecurityStamp = claimKey,
}
);
var validationResult = result.Success
? result.Data
: UserSecurityValidationResult.SecurityStampInvalid;
// 根据不同的验证结果进行处理
if (validationResult != UserSecurityValidationResult.Valid)
{
logger.LogInformation(
"用户 {Account} 的验证失败,原因:{Reason}",
account,
validationResult
);
// 登出用户
await context.SignOutAsync();
// 根据不同的失败原因,重定向到不同的错误页面
var redirectUrl = validationResult switch
{
UserSecurityValidationResult.UserDisabled =>
"/auth-status/account-disabled",
UserSecurityValidationResult.SecurityStampInvalid =>
"/auth-status/session-expired",
UserSecurityValidationResult.UserNotFound =>
"/auth-status/user-not-found",
_ => "/auth-status/auth-error",
};
context.Response.Redirect(redirectUrl);
return;
}
}
catch (Exception ex)
{
logger.LogError(ex, "验证用户 {Account} 的SecurityStamp时发生错误", account);
}
}
}
await next(context);
}
}
/// <summary>
/// SecurityStamp验证中间件扩展方法
/// </summary>
public static class SecurityStampValidationMiddlewareExtensions
{
public static IApplicationBuilder UseSecurityStampValidation(this IApplicationBuilder builder)
{
return builder.UseMiddleware<SecurityStampValidationMiddleware>();
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
代码解释
这是一个 ASP.NET Core 中间件,用于实现基于 SecurityStamp(安全戳)的会话验证机制。主要功能是在用户修改密码或其他敏感操作后,自动使其他浏览器/设备上的会话失效。
核心组件
1. SecurityStampValidationMiddleware 中间件类
使用了 主构造函数语法(C# 12)接收依赖注入:
RequestDelegate next:管道中的下一个中间件ILogger<SecurityStampValidationMiddleware> logger:日志记录器
2. InvokeAsync 方法 - 中间件核心逻辑
public async Task InvokeAsync(
HttpContext context,
IMediator mediator, // 中介者模式,用于发送查询请求
IAccountSessionService accountSessionService // 会话服务
)
执行流程:
步骤 1:身份验证检查
if (context.User.Identity?.IsAuthenticated == true)
仅对已登录用户进行验证。
步骤 2:提取用户信息
var account = context.User.NameIdentifier; // 用户标识
var claimKey = context.User.FindFirst("Key")?.Value; // SecurityStamp
var sessionId = context.User.SessionId; // 会话ID
步骤 3:会话撤销检查
if (await accountSessionService.IsRevokedAsync(sessionId, ...))
{
await context.SignOutAsync(); // 登出
context.Response.Redirect("/auth-status/session-expired");
return;
}
检查会话是否被主动撤销(如管理员强制登出)。
步骤 4:SecurityStamp 验证
var result = await mediator.Send(
new ValidateUserSecurityStampRequest
{
UserId = account,
SecurityStamp = claimKey,
}
);
通过 MediatR 发送验证请求,检查用户的 SecurityStamp 是否仍然有效。
步骤 5:处理验证结果
if (validationResult != UserSecurityValidationResult.Valid)
{
await context.SignOutAsync(); // 登出
// 根据失败原因重定向
var redirectUrl = validationResult switch
{
UserSecurityValidationResult.UserDisabled => "/auth-status/account-disabled",
UserSecurityValidationResult.SecurityStampInvalid => "/auth-status/session-expired",
UserSecurityValidationResult.UserNotFound => "/auth-status/user-not-found",
_ => "/auth-status/auth-error",
};
context.Response.Redirect(redirectUrl);
return;
}
可能的验证失败原因:
- 账户被禁用
- SecurityStamp 不匹配(密码已修改)
- 用户不存在
步骤 6:继续管道
await next(context);
验证通过后,调用下一个中间件。
3. 扩展方法
public static IApplicationBuilder UseSecurityStampValidation(this IApplicationBuilder builder)
{
return builder.UseMiddleware<SecurityStampValidationMiddleware>();
}
提供了便捷的注册方式,在 Program.cs 或 Startup.cs 中使用:
app.UseSecurityStampValidation();
应用场景
- 密码修改后自动登出:用户在设备 A 修改密码后,设备 B 的会话自动失效
- 强制登出:管理员可以撤销特定会话
- 账户安全保护:账户被禁用时立即终止所有会话
- 分布式会话管理:配合
IAccountSessionService实现集中式会话控制
技术亮点
- ✅ 使用 MediatR 实现解耦的查询处理
- ✅ 支持 细粒度的会话管理(SessionId 级别)
- ✅ 提供 友好的错误页面重定向
- ✅ 完善的 日志记录
- ✅ 异步处理,性能友好
这是一个生产级的安全中间件实现,常用于需要高安全性的企业应用系统。
AI 正在分析代码…
评论加载中...