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

ASP.NET Core认证:如何拦截SignInManager的AccessTokenResponse

解决方案

方法一:直接通过IAuthenticationService获取令牌并映射自定义类

PasswordSignInAsync本质是验证凭据后触发认证流程生成令牌,但不会直接返回令牌对象。你可以跳过该方法,直接用IAuthenticationService手动完成认证,直接拿到AccessTokenResponse并映射到自定义类。

步骤

  1. 向处理器注入IAuthenticationService和IHttpContextAccessor
  2. 验证用户密码后,手动触发认证流程生成令牌
  3. 从认证结果中提取令牌信息,映射到自定义类

修改后的处理器代码

public class LoginUserHandler : IRequestHandler<LoginUserCommand, BaseResponse<CustomTokenResponse>>
{
    private readonly UserManager<PublicUser> _userManager;
    private readonly IAuthenticationService _authenticationService;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public LoginUserHandler(UserManager<PublicUser> userManager, IAuthenticationService authenticationService, IHttpContextAccessor httpContextAccessor)
    {
        _userManager = userManager;
        _authenticationService = authenticationService;
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<BaseResponse<CustomTokenResponse>> Handle(LoginUserCommand request, CancellationToken cancellationToken)
    {
        var response = new BaseResponse<CustomTokenResponse>();

        var user = await _userManager.FindByNameAsync(request.Username);
        if (user == null)
        {
            response.Success = false;
            response.Message = "Login Failed";
            return response;
        }

        // 验证密码有效性
        var isPasswordValid = await _userManager.CheckPasswordAsync(user, request.Password);
        if (!isPasswordValid)
        {
            response.Success = false;
            response.Message = "Login Failed";
            return response;
        }

        // 更新安全戳并创建用户身份标识
        await _userManager.UpdateSecurityStampAsync(user);
        var claimsPrincipal = await _userManager.CreateUserPrincipalAsync(user);

        // 触发Bearer认证流程
        var authProperties = new AuthenticationProperties
        {
            IsPersistent = false,
            AllowRefresh = true
        };
        await _authenticationService.SignInAsync(_httpContextAccessor.HttpContext, IdentityConstants.BearerScheme, claimsPrincipal, authProperties);

        // 获取认证结果并提取令牌信息
        var authResult = await _authenticationService.AuthenticateAsync(_httpContextAccessor.HttpContext, IdentityConstants.BearerScheme);
        if (authResult.Succeeded && authResult.Properties != null)
        {
            response.Data = new CustomTokenResponse
            {
                AccessToken = authResult.Properties.GetTokenValue("access_token"),
                ExpiresIn = int.Parse(authResult.Properties.GetTokenValue("expires_in")),
                TokenType = authResult.Properties.GetTokenValue("token_type")
            };
            response.Success = true;
        }
        else
        {
            response.Success = false;
            response.Message = "Failed to generate token";
        }

        return response;
    }
}

自定义类示例

// 自定义令牌响应类
public class CustomTokenResponse
{
    public string AccessToken { get; set; }
    public int ExpiresIn { get; set; }
    public string TokenType { get; set; }
}

// 带数据的基础响应类
public class BaseResponse<T> : BaseResponse
{
    public T Data { get; set; }
}

// 基础响应类
public class BaseResponse
{
    public bool Success { get; set; }
    public string Message { get; set; }
}

修改后的控制器代码

[HttpPost("Login")]
[AllowAnonymous]
public async Task<Results<Ok<CustomTokenResponse>, ProblemHttpResult>> Login(LoginUserCommand command)
{
    var response = await Mediator.Send(command);
    if (!response.Success)
    {
        return TypedResults.Problem(response.Message);
    }
    return TypedResults.Ok(response.Data);
}

方法二:通过认证事件拦截AccessTokenResponse

如果想保留PasswordSignInAsync的使用方式,可以在JWT认证配置中添加事件,将令牌信息存入HttpContext,之后在处理器中取出映射。

步骤

  1. 在Program.cs中配置JWT认证事件,存储令牌信息
  2. 在处理器中通过IHttpContextAccessor获取令牌并映射

认证配置代码(Program.cs)

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        // 你的JWT基础配置(如密钥、颁发者等)
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
        };

        // 添加认证成功事件,存储令牌信息
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationSucceeded = context =>
            {
                if (context.Properties != null)
                {
                    context.HttpContext.Items["AccessTokenResponse"] = new CustomTokenResponse
                    {
                        AccessToken = context.Properties.GetTokenValue("access_token"),
                        ExpiresIn = int.Parse(context.Properties.GetTokenValue("expires_in")),
                        TokenType = context.Properties.GetTokenValue("token_type")
                    };
                }
                return Task.CompletedTask;
            }
        };
    });

修改后的处理器代码

public class LoginUserHandler : IRequestHandler<LoginUserCommand, BaseResponse<CustomTokenResponse>>
{
    private readonly UserManager<PublicUser> _userManager;
    private readonly SignInManager<PublicUser> _signInManager;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public LoginUserHandler(UserManager<PublicUser> userManager, SignInManager<PublicUser> signInManager, IHttpContextAccessor httpContextAccessor)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _signInManager.AuthenticationScheme = IdentityConstants.BearerScheme;
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<BaseResponse<CustomTokenResponse>> Handle(LoginUserCommand request, CancellationToken cancellationToken)
    {
        var response = new BaseResponse<CustomTokenResponse>();

        var user = await _userManager.FindByNameAsync(request.Username);
        if (user == null)
        {
            response.Success = false;
            response.Message = "Login Failed";
            return response;
        }

        var result = await _signInManager.PasswordSignInAsync(user, request.Password, false, true);
        if (!result.Succeeded)
        {
            response.Success = false;
            response.Message = "Login Failed";
            return response;
        }

        // 从HttpContext中取出拦截到的令牌信息
        if (_httpContextAccessor.HttpContext.Items.TryGetValue("AccessTokenResponse", out var tokenObj) && tokenObj is CustomTokenResponse tokenResponse)
        {
            response.Data = tokenResponse;
            response.Success = true;
        }
        else
        {
            response.Success = false;
            response.Message = "Failed to retrieve token";
        }

        return response;
    }
}

关键说明

  • 两种方式都能实现令牌信息的获取与自定义类映射,让Swagger识别强类型响应
  • 方法一更直接,手动控制认证流程,适合需要完全掌控令牌逻辑的场景
  • 方法二更贴合现有代码结构,改动量小,适合保留原有PasswordSignInAsync调用的场景

内容的提问来源于stack exchange,提问作者Matt MacDonald

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 04:42:25