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

Onion架构下JWT令牌解析失败问题求助

Onion架构下JWT令牌解析失败问题求助

各位大佬,我在基于Onion架构搭建的微服务里碰到了一个头疼的JWT解析问题,折腾好几天都没搞定,特意来求助!

情况是这样的:我按照常规方式实现了JWT认证,在单体架构项目里完全正常,但拆分成Onion架构之后,就出现了解析失败的错误。日志里明明显示收到了格式正确的Token,但系统就是报错说Token格式不对,没有分隔点。

先贴一下我的关键代码:

1. Program.cs 中的认证配置

// ...
builder.Services.AddControllers()
        .ConfigureApiBehaviorOptions(options =>
        {
            options.SuppressModelStateInvalidFilter = true;
        });

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddInfrastructureSwagger();

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = configuration["Jwt:Issuer"], // Correspondente ao issuer
            ValidAudience = configuration["Jwt:Audience"], // Correspondente à audience
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"]!))
        };

        // Adicionando logs de erro para melhor depuração
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                Console.WriteLine($"Authentication failed: {context.Exception.Message}");
                Console.WriteLine($"Token recebido: {context.Request.Headers["Authorization"]}");
                return Task.CompletedTask;
            }
        };
    });
// ...

2. TokenController.cs (生成Token的控制器)

namespace MyAPI.Core.Microservice.API.Controllers;
using MyAPI.Core.Microservice.Domain.Models.Entities;
using Microsoft.Extensions.Configuration;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Mvc;
using System.Text;
using Microsoft.Extensions.Logging;

/// <summary>
/// Controlador responsável pela gestão do token de autenticação.
/// </summary>
[ApiController]
[Route("api/auth-token")]
public class TokenController : ControllerBase
{
    private readonly string chaveSecreta;
    private readonly string issuer;
    private readonly string audience;
    private readonly string AdmUsername;
    private readonly string AdmPassword;
    private readonly ILogger<TokenController> _logger;

    /// <summary>
    /// Construtor do TokenController.
    /// </summary>
    /// <param name="configuration">Configurações da aplicação, contendo as informações de JWT e credenciais de administrador.</param>
    /// <param name="logger">Logger para registrar eventos e erros.</param>
    /// <exception cref="ArgumentNullException">Lançado quando as configurações de JWT ou administrador estão ausentes.</exception>
    public TokenController(IConfiguration configuration, ILogger<TokenController> logger)
    {
        issuer = configuration["Jwt:Issuer"]!;
        audience = configuration["Jwt:Audience"]!;
        chaveSecreta = configuration["Jwt:Key"]!;
        AdmUsername = configuration["Adm:Username"]!;
        AdmPassword = configuration["Adm:Password"]!;
        _logger = logger;
    }

    /// <summary>
    /// Criar um novo token
    /// </summary>
    /// <param name="admUser">Objeto contendo as credenciais do administrador</param>
    /// <returns>Token JWT gerado</returns>
    [HttpPost]
    [ProducesResponseType(typeof(string), 201)]  // Sucesso na criação do token: 200 Good Request
    [ProducesResponseType(typeof(object), 400)]  // Erro de validação: 400 Bad Request
    [ProducesResponseType(typeof(object), 500)]  // Erro do servidor: 500 Internal Server Error
    public ActionResult<string> GetToken(AdmUser admUser)
    {
        if(
            admUser.Username == AdmUsername && 
            admUser.Password == AdmPassword) 
        {
            var tokenJwt = GerarTokenJwt(admUser.Username); 
            return Ok(tokenJwt);
        }
        _logger.LogWarning("Administrative credentials incorrect for user: {Username}", admUser.Username);
        return BadRequest("Administrative credentials incorrect."); 
    }
    
    private string GerarTokenJwt(string username)
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(chaveSecreta));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
        var claims = new System.Security.Claims.Claim[]
        {
            new System.Security.Claims.Claim("username", username),
        };

        var token = new JwtSecurityToken(
            issuer: issuer,
            audience: audience,
            claims: claims,
            expires: DateTime.UtcNow.AddHours(1), 
            signingCredentials: credentials
        );
        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

3. DependencyInjectionSwagger.cs (注意:此处代码疑似粘贴错误,当前内容与TokenController完全一致)

namespace MyAPI.Core.Microservice.API.Controllers;
using MyAPI.Core.Microservice.Domain.Models.Entities;
using Microsoft.Extensions.Configuration;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Mvc;
using System.Text;
using Microsoft.Extensions.Logging;

/// <summary>
/// Controlador responsável pela gestão do token de autenticação.
/// </summary>
[ApiController]
[Route("api/auth-token")]
public class TokenController : ControllerBase
{
    private readonly string chaveSecreta;
    private readonly string issuer;
    private readonly string audience;
    private readonly string AdmUsername;
    private readonly string AdmPassword;
    private readonly ILogger<TokenController> _logger;

    /// <summary>
    /// Construtor do TokenController.
    /// </summary>
    /// <param name="configuration">Configurações da aplicação, contendo as informações de JWT e credenciais de administrador.</param>
    /// <param name="logger">Logger para registrar eventos e erros.</param>
    /// <exception cref="ArgumentNullException">Lançado quando as configurações de JWT ou administrador estão ausentes.</exception>
    public TokenController(IConfiguration configuration, ILogger<TokenController> logger)
    {
        issuer = configuration["Jwt:Issuer"]!;
        audience = configuration["Jwt:Audience"]!;
        chaveSecreta = configuration["Jwt:Key"]!;
        AdmUsername = configuration["Adm:Username"]!;
        AdmPassword = configuration["Adm:Password"]!;
        _logger = logger;
    }

    /// <summary>
    /// Criar um novo token
    /// </summary>
    /// <param name="admUser">Objeto contendo as credenciais do administrador</param>
    /// <returns>Token JWT gerado</returns>
    [HttpPost]
    [ProducesResponseType(typeof(string), 201)]  // Sucesso na criação do token: 200 Good Request
    [ProducesResponseType(typeof(object), 400)]  // Erro de validação: 400 Bad Request
    [ProducesResponseType(typeof(object), 500)]  // Erro do servidor: 500 Internal Server Error
    public ActionResult<string> GetToken(AdmUser admUser)
    {
        if(
            admUser.Username == AdmUsername && 
            admUser.Password == AdmPassword) 
        {
            var tokenJwt = GerarTokenJwt(admUser.Username); 
            return Ok(tokenJwt);
        }
        _logger.LogWarning("Administrative credentials incorrect for user: {Username}", admUser.Username);
        return BadRequest("Administrative credentials incorrect."); 
    }
    
    private string GerarTokenJwt(string username)
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(chaveSecreta));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
        var claims = new System.Security.Claims.Claim[]
        {
            new System.Security.Claims.Claim("username", username),
        };

        var token = new JwtSecurityToken(
            issuer: issuer,
            audience: audience,
            claims: claims,
            expires: DateTime.UtcNow.AddHours(1), 
            signingCredentials: credentials
        );
        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

收到的错误信息

Authentication failed: IDX14100: JWT is not well formed, there are no dots (.).
The token needs to be in JWS or JWE Compact Serialization Format. (JWS): 'EncodedHeader.EndcodedPayload.EncodedSignature'. (JWE): 'EncodedProtectedHeader.EncodedEncryptedKey.EncodedInitializationVector.EncodedCiphertext.EncodedAuthenticationTag'.
Token recebido: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluQHdhcnJlbi5kb21haW4uY29tIiwiZXhwIjoxNzQwNjA1MjkzLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAifQ.gZXRCeAbphb-WTqu3g3KQFjVyqJPULH31hRreQxnYpk


排查方向与解决方案建议:

  1. 修复DependencyInjectionSwagger.cs的错误内容
    这个文件应该是Swagger的配置扩展类(对应AddInfrastructureSwagger方法),但当前内容和TokenController完全重复,这绝对是问题核心之一。正确的Swagger配置需要包含JWT认证支持,示例如下:

    using Microsoft.OpenApi.Models;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace MyAPI.Core.Microservice.API.Client.DependencyInjection;
    
    public static class DependencyInjectionSwagger
    {
        public static IServiceCollection AddInfrastructureSwagger(this IServiceCollection services)
        {
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "MyAPI", Version = "v1" });
                
                // 配置JWT认证的Swagger支持
                var securityScheme = new OpenApiSecurityScheme
                {
                    Name = "Authorization",
                    Type = SecuritySchemeType.Http,
                    Scheme = "bearer",
                    BearerFormat = "JWT",
                    In = ParameterLocation.Header,
                    Description = "请输入Bearer {Token}格式的认证令牌",
                    Reference = new OpenApiReference
                    {
                        Type = ReferenceType.SecurityScheme,
                        Id = "Bearer"
                    }
                };
                
                c.AddSecurityDefinition("Bearer", securityScheme);
                
                // 全局要求认证
                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    { securityScheme, Array.Empty<string>() }
                });
            });
            
            return services;
        }
    }
    
  2. 检查中间件管道的执行顺序
    中间件顺序直接影响认证逻辑的生效,必须确保认证在授权之前执行,正确的Program.cs管道顺序应该是:

    // 注册完所有服务之后
    app.UseSwagger();
    app.UseSwaggerUI();
    
    app.UseHttpsRedirection();
    
    app.UseAuthentication(); // 先执行认证
    app.UseAuthorization();  // 再执行授权
    
    app.MapControllers();    // 最后映射控制器
    
  3. 验证JWT配置参数的一致性
    确保Program.cs和TokenController读取的Jwt配置(Issuer、Audience、Key)完全一致,检查appsettings.json中的配置是否正确:

    {
      "Jwt": {
        "Issuer": "http://localhost:5000",
        "Audience": "http://localhost:5000",
        "Key": "至少16位长度的密钥,比如yourStrongSecretKey12345"
      },
      "Adm": {
        "Username": "admin",
        "Password": "yourAdminPassword"
      }
    }
    

    注意密钥长度不能太短,否则HmacSha256算法会触发验证错误。

  4. 修正TokenController中的注释错误
    你在ProducesResponseType(201)的注释里写的是200,但实际返回的Ok()对应200状态码,建议统一改成[ProducesResponseType(typeof(string), StatusCodes.Status200OK)],避免混淆。


按照上面的步骤排查,应该能解决问题。我怀疑最核心的问题就是Swagger配置文件粘贴错误,导致Swagger没有正确处理JWT令牌的传递,或者中间件顺序不对。

备注:内容来源于stack exchange,提问作者rodrigo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 11:33:03