.NET5多项目JWT鉴权配置及MVC调用API报500错误问题咨询
问题根因汇总
- 三个项目的
Configure方法都缺失认证授权中间件注册,认证流程根本未生效 - JWT配置中Issuer是
http://localhost:44318,但API、MVC中JWT配置的Authority写为https://localhost:44318,发行方不匹配导致令牌验证失败 - API项目的JWT配置未指定
IssuerSigningKey和完整验证规则,无法正确校验令牌签名 - MVC项目是前端Web应用,不应该用Bearer作为默认认证方案,应该用Cookie认证存储用户身份和令牌,避免每次请求都手动传递token
各项目修正配置
1. JWT.IDP 项目
Configure方法补充中间件(ConfigureServices无需修改)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWT.IDP v1")); } app.UseHttpsRedirection(); app.UseRouting(); // 必须按顺序加:先认证、后授权 app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
确保IDP的实际访问地址和appsettings.json中JWT配置的Issuer完全一致,不要http和https混用
2. JWT.API 项目
ConfigureServices完整配置
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); // JWT认证配置 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(Convert.ToInt32(Configuration["JWT:DurationInMinutes"])), ValidIssuer = Configuration["JWT:Issuer"], ValidAudience = Configuration["JWT:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Key"])) }; }); // Swagger配置(可选新增Bearer认证支持) services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "JWT.API", Version = "v1" }); c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "输入格式:Bearer {token}", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, Scheme = "Bearer" }); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[]{} } }); }); }
Configure方法配置
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWT.API v1")); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
API项目的appsettings.json需要新增和IDP完全一致的JWT配置段
3. JWT.MVC 项目
ConfigureServices配置
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); // MVC用Cookie作为默认认证方案,存储用户状态和令牌 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = "/Login/DoLogin"; options.AccessDeniedPath = "/Home/AccessDenied"; }); // 注册HttpClient工厂,避免手动new HttpClient导致的资源泄漏 services.AddHttpClient(); }
Configure方法配置
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); }
控制器代码修正
using Microsoft.AspNetCore.Authentication; using System.Security.Claims; namespace JWT.MVC.Controllers { public class LoginController : Controller { // 注入HttpClient工厂 private readonly IHttpClientFactory _httpClientFactory; public LoginController(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public IActionResult DoLogin() { return View(); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> DoLogin([Bind("EmailOrName,Password")] LoginRequestModel loginRequestModel) { var apiName = "https://localhost:44318/api/User/login"; var httpClient = _httpClientFactory.CreateClient(); HttpResponseMessage response = await httpClient.PostAsJsonAsync(apiName, loginRequestModel); var jasonString = await response.Content.ReadAsStreamAsync(); var data = await JsonSerializer.DeserializeAsync<IEnumerable<AccessibleDb>> (jasonString, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); foreach (var item in data) { item.UserName = loginRequestModel.EmailOrName; } return View("SelectDatabase", data); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> PostLogin(string db, string user) { TokenRequestModel tokenRequestModel = new TokenRequestModel() { Database = db, UserName = user }; var apiName = "https://localhost:44318/api/User/tokenonly"; var httpClient = _httpClientFactory.CreateClient(); HttpResponseMessage response = await httpClient.PostAsJsonAsync(apiName, tokenRequestModel); var jasonString = await response.Content.ReadAsStreamAsync(); var data = await JsonSerializer.DeserializeAsync<AuthenticationModel> (jasonString, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); // 写入Cookie认证,把令牌存入用户Claim var claims = new List<Claim> { new Claim(ClaimTypes.Name, user), new Claim("Database", db), new Claim("AccessToken", data.Token) }; var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity)); ViewBag.SelectedDb = db; return View(data); } [Authorize] // 加认证校验,未登录自动跳转登录页 public async Task<IActionResult> GetWeatherData() { // 从当前用户Claim中取令牌,无需手动传参 var accessToken = User.FindFirstValue("AccessToken"); var apiName = "https://localhost:44338/weatherforecast"; var httpClient = _httpClientFactory.CreateClient(); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); HttpResponseMessage response = await httpClient.GetAsync(apiName); if (!response.IsSuccessStatusCode) { ViewBag.Error = response.StatusCode; return View("Weatherdata"); } var jasonString = await response.Content.ReadAsStreamAsync(); var data = await JsonSerializer.DeserializeAsync<WeatherForecast> (jasonString, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); return View("Weatherdata", data); } } }
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

