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

.NET 8新增认证端点无登录Token返回及端点类型问题咨询

.NET 8 Web API 认证端点问题解答

1. 让登录接口返回Bearer Token的实现方案

当前你的配置默认使用Cookie认证方案(Identity.Bearer),而AddApiEndpoints提供的默认登录端点是基于Cookie设计的,所以验证成功后仅返回200 OK,不会生成JWT。要返回Bearer Token,推荐通过自定义登录端点实现,步骤如下:

步骤1:添加JWT配置与生成服务

首先定义JWT参数模型和Token生成服务,用于生成符合要求的JWT:

// JWT参数模型
public class JwtSettings
{
    public string SecretKey { get; set; }
    public string Issuer { get; set; }
    public string Audience { get; set; }
    public int ExpiresInMinutes { get; set; }
}

// JWT Token生成服务
public class JwtTokenService
{
    private readonly JwtSettings _jwtSettings;
    private readonly UserManager<ApplicationUser> _userManager;

    public JwtTokenService(JwtSettings jwtSettings, UserManager<ApplicationUser> userManager)
    {
        _jwtSettings = jwtSettings;
        _userManager = userManager;
    }

    public async Task<string> GenerateTokenAsync(ApplicationUser user)
    {
        // 构建用户声明
        var claims = new List<Claim>
        {
            new Claim(JwtRegisteredClaimNames.Sub, user.Id),
            new Claim(JwtRegisteredClaimNames.Email, user.Email!),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };

        // 添加用户角色声明
        var roles = await _userManager.GetRolesAsync(user);
        claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r)));

        // 生成签名密钥
        var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey));
        var signingCreds = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);

        // 构建JWT Token
        var token = new JwtSecurityToken(
            issuer: _jwtSettings.Issuer,
            audience: _jwtSettings.Audience,
            claims: claims,
            expires: DateTime.UtcNow.AddMinutes(_jwtSettings.ExpiresInMinutes),
            signingCredentials: signingCreds);

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

步骤2:注册JWT配置与服务

在Program.cs中注册JWT参数和生成服务(建议从appsettings.json读取配置,示例中暂用硬编码):

// 配置JWT参数
var jwtSettings = new JwtSettings
{
    SecretKey = "your-strong-secret-key-min-16-chars",
    Issuer = "https://localhost:5000",
    Audience = "client.mydomain.com",
    ExpiresInMinutes = 60
};
builder.Services.AddSingleton(jwtSettings);

// 注册Token生成服务
builder.Services.AddScoped<JwtTokenService>();

步骤3:调整认证方案默认配置

将默认认证方案改为JWT,确保后续接口验证使用Bearer Token:

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer("bearer", options =>
{
    options.Authority = "https://localhost:5000";
    options.Audience = "client.mydomain.com";
    options.RequireHttpsMetadata = true;
    options.SaveToken = true;
    options.IncludeErrorDetails = true;

    // 如果不依赖IdentityServer,手动配置Token验证参数
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidIssuer = jwtSettings.Issuer,
        ValidateAudience = true,
        ValidAudience = jwtSettings.Audience,
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey)),
        ClockSkew = TimeSpan.Zero
    };
});

步骤4:添加自定义登录端点

替换默认的Cookie登录端点,实现返回JWT的逻辑:

// 登录请求模型
public class LoginRequest
{
    [Required]
    public string Email { get; set; }
    [Required]
    public string Password { get; set; }
}

// 自定义登录端点
app.MapPost("/api/auth/login", async (LoginRequest request, UserManager<ApplicationUser> userManager, JwtTokenService tokenService) =>
{
    var user = await userManager.FindByEmailAsync(request.Email);
    if (user == null || !await userManager.CheckPasswordAsync(user, request.Password))
    {
        return Results.Unauthorized();
    }

    var accessToken = await tokenService.GenerateTokenAsync(user);
    return Results.Ok(new 
    { 
        AccessToken = accessToken, 
        TokenType = "Bearer",
        ExpiresIn = jwtSettings.ExpiresInMinutes * 60
    });
})
.WithName("ApiLogin")
.WithOpenApi();

完成上述配置后,调用该端点验证成功会返回包含AccessToken的JSON响应,可直接复制到Swagger的授权框中使用。


2. 生成GET和POST两种登录端点的原因

AddApiEndpoints(即MapIdentityApi<ApplicationUser>)添加的两个登录端点是.NET 8 Identity API的默认设计,分工不同:

  • GET /account/login:用于处理认证挑战场景。在Cookie认证模式下,当未认证用户访问受保护资源时,会重定向到该端点(返回登录页面);对于API场景,该端点通常返回401 Unauthorized,提示用户需要认证。
  • POST /account/login:是实际接收用户邮箱/密码、执行登录验证的端点,默认验证成功后会设置Cookie(符合传统Web应用的认证流程)。

这种设计是为了同时兼容传统Cookie认证的Web应用场景和API场景,但默认实现更偏向Web应用,因此需要我们自定义逻辑来适配JWT认证的API需求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 12:50:20