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

Minimal API认证端点类重构:移除WebApplicationBuilder报错解决

重构JWT Token端点以移除WebApplicationBuilder依赖

问题背景

原本在Program.cs中实现的JWT Token获取端点,迁移到SecurityEndpoints静态类后,因在GetToken方法中依赖WebApplicationBuilder引发InvalidOperationException,需要重构移除该依赖并正确获取JWT配置项。


方案一:直接注入IConfiguration获取配置

IConfiguration已经默认注册到ASP.NET Core的DI容器中,可直接在端点方法里注入使用,完全替代WebApplicationBuilder的配置读取能力:

public static class SecurityEndpoints
{
    public static void MapSecurityEndpoints(this WebApplication app)
    {
        app.MapPost("/api/security/getToken", GetToken)
           .AllowAnonymous()
           .WithTags("Security");
        app.MapPost("/api/security/createUser", CreateUser).AllowAnonymous();
    }

    internal static async Task<IResult> GetToken(
        UserManager<IdentityUser> userMgr,
        [FromBody] UserLoginDTO user,
        IConfiguration config)
    {
        var identityUser = await userMgr.FindByEmailAsync(user.Email);
        if (identityUser == null || !await userMgr.CheckPasswordAsync(identityUser, user.Password))
        {
            return Results.Unauthorized();
        }

        // 从IConfiguration读取JWT配置
        var issuer = config["Jwt:Issuer"];
        var audience = config["Jwt:Audience"];
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:Key"]));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Email, identityUser.Email),
            new Claim(ClaimTypes.GivenName, identityUser.UserName)
        };

        // 建议添加Token过期时间,提升安全性
        var token = new JwtSecurityToken(
            issuer: issuer,
            audience: audience,
            claims: claims,
            signingCredentials: credentials,
            expires: DateTime.UtcNow.AddHours(1)
        );

        var stringToken = new JwtSecurityTokenHandler().WriteToken(token);
        return Results.Ok(stringToken);
    }

    // CreateUser方法实现...
}

方案二:绑定配置到强类型类(更优雅的类型安全方案)

硬编码配置键容易出错,将JWT配置绑定到强类型类,既能获得类型提示,也方便后续维护:

  1. 定义JWT配置强类型类
public class JwtSettings
{
    public string Issuer { get; set; } = string.Empty;
    public string Audience { get; set; } = string.Empty;
    public string Key { get; set; } = string.Empty;
}
  1. 在Program.cs中注册配置绑定
// 将appsettings.json中的Jwt节点绑定到JwtSettings类,并注册到DI容器
builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("Jwt"));
  1. 修改GetToken方法注入IOptions
internal static async Task<IResult> GetToken(
    UserManager<IdentityUser> userMgr,
    [FromBody] UserLoginDTO user,
    IOptions<JwtSettings> jwtSettings)
{
    var settings = jwtSettings.Value;
    var identityUser = await userMgr.FindByEmailAsync(user.Email);
    
    if (identityUser == null || !await userMgr.CheckPasswordAsync(identityUser, user.Password))
    {
        return Results.Unauthorized();
    }

    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(settings.Key));
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.Email, identityUser.Email),
        new Claim(ClaimTypes.GivenName, identityUser.UserName)
    };

    var token = new JwtSecurityToken(
        issuer: settings.Issuer,
        audience: settings.Audience,
        claims: claims,
        signingCredentials: credentials,
        expires: DateTime.UtcNow.AddHours(1)
    );

    var stringToken = new JwtSecurityTokenHandler().WriteToken(token);
    return Results.Ok(stringToken);
}

方案三:提取Token生成逻辑到独立服务(彻底解耦)

如果想让端点逻辑更简洁,同时便于单独测试Token生成逻辑,可以把这部分代码抽成独立服务:

  1. 定义Token服务接口
public interface ITokenService
{
    string GenerateToken(IdentityUser user);
}
  1. 实现Token服务
public class TokenService : ITokenService
{
    private readonly JwtSettings _jwtSettings;

    public TokenService(IOptions<JwtSettings> jwtSettings)
    {
        _jwtSettings = jwtSettings.Value;
    }

    public string GenerateToken(IdentityUser user)
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Key));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Email, user.Email),
            new Claim(ClaimTypes.GivenName, user.UserName)
        };

        var token = new JwtSecurityToken(
            issuer: _jwtSettings.Issuer,
            audience: _jwtSettings.Audience,
            claims: claims,
            signingCredentials: credentials,
            expires: DateTime.UtcNow.AddHours(1)
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}
  1. 在Program.cs中注册服务
builder.Services.AddScoped<ITokenService, TokenService>();
  1. 简化GetToken方法
internal static async Task<IResult> GetToken(
    UserManager<IdentityUser> userMgr,
    [FromBody] UserLoginDTO user,
    ITokenService tokenService)
{
    var identityUser = await userMgr.FindByEmailAsync(user.Email);
    if (identityUser == null || !await userMgr.CheckPasswordAsync(identityUser, user.Password))
    {
        return Results.Unauthorized();
    }

    var token = tokenService.GenerateToken(identityUser);
    return Results.Ok(token);
}

内容的提问来源于stack exchange,提问作者pușigreen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 11:05:26