设置HttpClient的Authorization请求头后返回500内部服务器错误
问题现象
为HttpClient设置Authorization请求头后,调用后端API返回500内部服务器错误,请求无法抵达API端点;关闭授权校验、不发送令牌时所有功能正常运行。
相关代码模块
Auth控制器登录方法
namespace MagicVilla_Web.Controllers { public class AuthController : Controller { private readonly IAuthService _authService; public AuthController(IAuthService authService) { _authService = authService; } [HttpGet] public IActionResult Login() { LoginRequestDTO loginRequestDTO = new LoginRequestDTO(); return View(loginRequestDTO); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Login(LoginRequestDTO loginRequestDTO) { APIResponse response = await _authService.LoginAsync<APIResponse>(loginRequestDTO); if (response != null && response.IsSuccess) { LoginResponseDTO model = JsonConvert.DeserializeObject<LoginResponseDTO>(Convert.ToString(response.Result)); var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.Name, model.User.Name)); identity.AddClaim(new Claim(ClaimTypes.Role, model.User.Role)); var principal = new ClaimsPrincipal(identity); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal); HttpContext.Session.SetString(SD.SessionToken, model.Token); return RedirectToAction("Index","Home"); } else { ModelState.AddModelError("CustomError", response.ErrorMessages.FirstOrDefault()); return View(loginRequestDTO); } } [HttpGet] public IActionResult Register() { return View(); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Register(RegisterationRequestDTO registerationRequestDTO) { APIResponse result = await _authService.RegisterAsync<APIResponse>(registerationRequestDTO); if (result != null && result.IsSuccess) { return RedirectToAction("Login"); } return View(); } [HttpGet] public async Task<IActionResult> Logout() { await HttpContext.SignOutAsync(); HttpContext.Session.SetString(SD.SessionToken, ""); return RedirectToAction("Index", "Home"); } [HttpGet] public async Task<IActionResult> AccessDenied() { return View(); } } }
Home控制器Index方法
namespace MagicVilla_Web.Controllers { public class HomeController : Controller { private readonly IVillaService villaService; private readonly IMapper mapper; public HomeController(IVillaService villaService, IMapper mapper) { this.villaService = villaService; this.mapper = mapper; } public async Task<IActionResult> Index() { List<VillaDTO> list = new(); var response = await villaService.GetAllAsync<APIResponse>(HttpContext.Session.GetString(SD.SessionToken)); if (response != null && response.IsSuccess) { list = JsonConvert.DeserializeObject<List<VillaDTO>>(Convert.ToString(response.Result)); } return View(list); } } }
VillaService的GetAllAsync方法
public class VillaService : BaseService , IVillaService { private readonly IHttpClientFactory clientFactory; private string villaUrl; public VillaService(IHttpClientFactory clientFactory, IConfiguration configuration) : base(clientFactory) { this.clientFactory = clientFactory; this.villaUrl = configuration.GetValue<string>("ServiceUrls:VillaAPI"); } public Task<T> GetAllAsync<T>(string token) { return SendAsync<T>(new APIRequest { ApiType = SD.ApiType.GET, Url = this.villaUrl + "/api/VillaAPI", Token = token }); } }
BaseService的SendAsync方法
namespace MagicVilla_Web.Services { public class BaseService : IBaseServices { public APIResponse APIResponse { get; set; } public IHttpClientFactory httpClient { get; set; } public BaseService(IHttpClientFactory httpClient) { this.APIResponse = new(); this.httpClient = httpClient; } public async Task<T> SendAsync<T>(APIRequest apiRequest) { try { var client = httpClient.CreateClient("MagicAPI"); HttpRequestMessage request = new HttpRequestMessage(); request.Headers.Add("Accept", "application/json"); request.RequestUri = new Uri(apiRequest.Url); if (apiRequest.Data != null) { request.Content = new StringContent(JsonConvert.SerializeObject(apiRequest.Data), Encoding.UTF8, "application/json"); } switch (apiRequest.ApiType) { case SD.ApiType.POST: request.Method = HttpMethod.Post; break; case SD.ApiType.PUT: request.Method = HttpMethod.Put; break; case SD.ApiType.DELETE: request.Method = HttpMethod.Delete; break; default: request.Method = HttpMethod.Get; break; } HttpResponseMessage response = null; if (!string.IsNullOrEmpty(apiRequest.Token)) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiRequest.Token); } response = await client.SendAsync(request); var apiContent = await response.Content.ReadAsStringAsync(); try { APIResponse apiResponse = JsonConvert.DeserializeObject<APIResponse>(apiContent); if ((response.StatusCode == System.Net.HttpStatusCode.BadRequest || response.StatusCode == System.Net.HttpStatusCode.NotFound)) { apiResponse.StatusCode = System.Net.HttpStatusCode.BadRequest; apiResponse.IsSuccess = false; var apiResponseSerialized = JsonConvert.SerializeObject(apiResponse); var apiResponseDeserialized = JsonConvert.DeserializeObject<T>(apiResponseSerialized); return apiResponseDeserialized; } } catch (Exception e) { var exceptionResponse = JsonConvert.DeserializeObject<T>(apiContent); return exceptionResponse; } var APIResponse = JsonConvert.DeserializeObject<T>(apiContent); return APIResponse; } catch (Exception e) { var dto = new APIResponse { ErrorMessages = new List<string> { Convert.ToString(e.Message) }, IsSuccess = false }; var res = JsonConvert.SerializeObject(dto); var apiResponse = JsonConvert.DeserializeObject<T>(res); return apiResponse; } } } }
VillaAPI控制器
[Route("api/[controller]")] [ApiController] public class VillaAPIController : ControllerBase { public readonly IVillaRepository villaRepository; public readonly IMapper mapper; protected APIResponse response; public VillaAPIController(IVillaRepository villaRepository, IMapper mapper) { this.villaRepository = villaRepository; this.mapper = mapper; this.response = new(); } [HttpGet] [Authorize] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task<ActionResult<IEnumerable<APIResponse>>> GetVillas() { try { IEnumerable<Villa> villaList = await villaRepository.GetAllAsync(); response.Result = mapper.Map<List<VillaDTO>>(villaList); response.StatusCode = System.Net.HttpStatusCode.OK; } catch (Exception ex) { response.IsSuccess = false; response.ErrorMessages = new List<string> { ex.Message }; } return Ok(response); } }
API端Program.cs
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddDbContext<ApplicationDbContext>(o => { o.UseSqlServer(builder.Configuration.GetConnectionString("DefaultSQLConnection")); }); builder.Services.AddAutoMapper(typeof(MappingConfig)); builder.Services.AddScoped<IVillaRepository, VillaRepository>(); builder.Services.AddScoped<IVillaNumberRepository, VillaNumberRepository>(); builder.Services.AddScoped<IUserRepository, UserRepository>(); builder.Services.AddControllers(o => { //o.ReturnHttpNotAcceptable = true; }).AddNewtonsoftJson().AddXmlDataContractSerializerFormatters(); builder.Services.AddEndpointsApiExplorer(); //get secret key from appSittings.json var key = builder.Configuration.GetValue<string>("ApiSettings:Secret"); //add Authentication Configurations builder.Services.AddAuthentication(x => { //configure the default authentication scheme and the default challenge scheme. Both of them we want to use JWT bearer defaults x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) //we have something called as add JWT bearer and then we can configure options on that. .AddJwtBearer(x => { x.RequireHttpsMetadata = false; //we have the required https metadata x.SaveToken = true; //save the token x.Authority = "https://localhost:7003/"; x.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = false, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(key)), ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = false, }; }); //to enable bearer in swagger builder.Services.AddSwaggerGen(options => { options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n " + "Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\n" + "Example: \"Bearer 12345abcdef\"", Name = "Authorization", In = ParameterLocation.Header, Scheme = "Bearer" }); options.AddSecurityRequirement(new OpenApiSecurityRequirement() { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }, Scheme = "oauth2", Name = "Bearer", In = ParameterLocation.Header }, new List<string>() } }); }); //app => configured application var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();
Web端Program.cs
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); builder.Services.AddAutoMapper(typeof(MappingConfig)); builder.Services.AddHttpClient<IVillaService, VillaService>(); builder.Services.AddScoped<IVillaService, VillaService>(); builder.Services.AddHttpClient<IVillaNumberService, VillaNumberService>(); builder.Services.AddScoped<IVillaNumberService, VillaNumberService>(); builder.Services.AddHttpClient<IAuthService, AuthService>(); builder.Services.AddScoped<IAuthService, AuthService>(); builder.Services.AddDistributedMemoryCache(); IdentityModelEventSource.ShowPII = true; builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.Cookie.HttpOnly = true; options.ExpireTimeSpan = TimeSpan.FromMinutes(30); options.SlidingExpiration = true; options.LoginPath = "/Auth/Login"; options.AccessDeniedPath = "/Auth/AccessDenied"; }); //add authorization //builder.Services.AddAuthorization(); builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(100); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseSession(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
排查与解决方案
核心问题定位
问题根源在API端的JWT认证配置错误,导致令牌验证过程中抛出未捕获的异常,返回500错误。
具体修复步骤
修正API端JWT认证配置
移除冲突的Authority配置,启用必要的令牌验证参数:builder.Services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(x => { x.RequireHttpsMetadata = false; x.SaveToken = true; // 移除Authority,对称密钥验证无需身份提供者 x.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, // 必须验证签名密钥 IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(key)), ValidateIssuer = false, // 可根据需求开启并配置Issuer ValidateAudience = false, // 可根据需求开启并配置Audience ValidateLifetime = true, // 必须验证令牌有效期 ClockSkew = TimeSpan.Zero // 关闭时钟偏差,严格校验 }; });完善API端异常日志
在VillaAPIController的异常捕获中记录完整堆栈信息,便于排查:catch (Exception ex) { response.IsSuccess = false; response.ErrorMessages = new List<string> { ex.ToString() }; // 记录完整异常信息 }验证令牌一致性
确保登录时生成Token使用的签名密钥与API端ApiSettings:Secret完全一致,避免因密钥不匹配导致验证失败。
内容的提问来源于stack exchange,提问作者Mohammad Zytoon
相关产品推荐
相关产品推荐

