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

.NET 8自定义Identity登录接口:如何同时返回Bearer Token与用户ID

问题分析

你的代码出现格式异常的原因是:当SignInManager使用IdentityConstants.BearerScheme调用PasswordSignInAsync时,该方法会自动向HTTP响应流写入Bearer Token的JSON内容,随后你的TypedResults.Created又写入了user.Id,导致两个响应内容直接拼接,最终出现}1024这种非法JSON格式。

正确实现方式

要同时返回Bearer Token和用户ID,你需要手动生成Token并构造统一的响应对象,避免依赖PasswordSignInAsync的自动响应行为。以下是.NET 8中的完整实现:

1. 定义登录响应DTO

首先创建一个用于封装返回结果的类:

public class LoginResponse
{
    public string TokenType { get; set; } = "Bearer";
    public string AccessToken { get; set; } = string.Empty;
    public int ExpiresIn { get; set; }
    public string? RefreshToken { get; set; }
    public string UserId { get; set; } = string.Empty;
}

2. 修改自定义登录接口

替换你的/customlogin接口代码,手动生成JWT Token并返回包含用户ID的统一响应:

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens;

app.MapPost("/customlogin", async (
    [FromBody] LoginRequest cmd,
    SignInManager<CustomUser> signInManager,
    UserManager<CustomUser> userManager,
    IConfiguration config) =>
{
    // 查找用户
    var user = await userManager.FindByEmailAsync(cmd.Email);
    if (user == null)
    {
        return Results.BadRequest("无效的登录凭据");
    }

    // 验证密码
    var passwordCheckResult = await signInManager.CheckPasswordSignInAsync(user, cmd.Password, lockoutOnFailure: false);
    if (!passwordCheckResult.Succeeded)
    {
        var errorMsg = passwordCheckResult.IsNotAllowed 
            ? "账户未被允许登录" 
            : "邮箱或密码错误";
        return Results.BadRequest(errorMsg);
    }

    // 生成用户身份声明
    var userPrincipal = await signInManager.CreateUserPrincipalAsync(user);
    var jwtConfig = config.GetSection("Jwt");
    
    // 构建JWT Token
    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig["Key"]));
    var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

    var jwtToken = new JwtSecurityToken(
        issuer: jwtConfig["Issuer"],
        audience: jwtConfig["Audience"],
        claims: userPrincipal.Claims,
        expires: DateTime.UtcNow.AddMinutes(int.Parse(jwtConfig["ExpiresInMinutes"])),
        signingCredentials: signingCredentials);

    // 生成刷新Token(可选)
    var refreshToken = await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, "RefreshToken");
    await userManager.SetAuthenticationTokenAsync(user, TokenOptions.DefaultProvider, "RefreshToken", refreshToken);

    // 返回统一响应
    return TypedResults.Ok(new LoginResponse
    {
        AccessToken = new JwtSecurityTokenHandler().WriteToken(jwtToken),
        ExpiresIn = int.Parse(jwtConfig["ExpiresInMinutes"]) * 60,
        RefreshToken = refreshToken,
        UserId = user.Id
    });
});

3. 配置JWT参数

在appsettings.json中添加JWT配置:

"Jwt": {
  "Key": "YourSecureKeyWithAtLeast16Characters",
  "Issuer": "YourApplicationIssuer",
  "Audience": "YourApplicationAudience",
  "ExpiresInMinutes": 60
}

4. 配置JWT认证服务

在Program.cs中注册JWT认证服务:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        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"]))
        };
    });

// 确保添加授权中间件
app.UseAuthentication();
app.UseAuthorization();
说明
  • 改用CheckPasswordSignInAsync仅验证密码,避免PasswordSignInAsync自动写入响应流的行为。
  • 手动生成JWT Token可以完全控制返回内容,同时包含用户ID和标准的Bearer Token字段。
  • 刷新Token的生成和存储是可选功能,若不需要可移除相关代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 02:24:56