出現 401 Unauthorized 錯誤通常表示在授權過程中出現問題。以下是一些常見的原因以及解決方案: JWT 沒有正確附加到請求標頭:確認 JWT 被正確地從 Session 中獲取並附加到請求的授權標頭中。 API URL 錯誤或過期:確認 API 的 URL 正確並且沒有過期。 JWT 過期:確認 JWT 沒有過期,如果過期需要重新獲取。 CORS 問題:如果你從不同的域名訪問 API,確保 API 設置了正確的 CORS 政策。 API 配置問題:確保 API 正確配置了身份驗證和授權策略。
以下是一些診斷和解決這些問題的步驟: 檢查 JWT 是否正確附加到請求標頭 確保 AuthorizedHttpClientHandler 中的 JWT 確實被附加到請求標頭中。你可以添加一些調試輸出來確認這一點: csharp public class AuthorizedHttpClientHandler : DelegatingHandler { private readonly IHttpContextAccessor _httpContextAccessor; public AuthorizedHttpClientHandler(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var token = _httpContextAccessor.HttpContext.Session.GetString("JWT"); if (!string.IsNullOrEmpty(token)) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); } else { // 記錄沒有獲取到 Token 的情況 Console.WriteLine("No JWT token found in session."); } // 記錄請求詳細信息 Console.WriteLine($"Request: {request.Method} {request.RequestUri}"); Console.WriteLine($"Authorization: {request.Headers.Authorization}"); return await base.SendAsync(request, cancellationToken); } }
檢查 API URL 和配置 確保你使用的是正確的 API URL,並且 API 正確配置了身份驗證和授權策略。 確認 JWT 沒有過期 如果 JWT 過期,你需要重新獲取新的 JWT。你可以在 API 的響應中檢查是否返回了過期錯誤信息。 配置 CORS 如果你從不同的域名訪問 API,請確保 API 設置了正確的 CORS 政策。例如: csharp // 在 API 的 Startup.cs 中配置 CORS public void ConfigureServices(IServiceCollection services) { services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", builder => builder.WithOrigins("https://yourclientapp.com") .AllowAnyHeader() .AllowAnyMethod()); }); services.AddControllers(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseCors("AllowSpecificOrigin"); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } 驗證整個流程 確保整個流程沒有問題,從登入到 API 請求。
完整示例 以下是更新後的完整示例: csharp // Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddHttpClient("AuthorizedClient") .AddHttpMessageHandler<AuthorizedHttpClientHandler>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddTransient<AuthorizedHttpClientHandler>(); services.AddControllersWithViews(); services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); } 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.UseSession(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } // AccountController.cs public class AccountController : Controller { private readonly IHttpClientFactory _httpClientFactory; public AccountController(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } [HttpGet] public IActionResult Login() { return View(); } [HttpPost] public async Task<IActionResult> Login(LoginViewModel model) { if (!ModelState.IsValid) { return View(model); } var client = _httpClientFactory.CreateClient(); var loginUrl = "https://yourapi.com/api/auth/login"; var loginData = new { Username = model.Username, Password = model.Password }; var content = new StringContent(JsonSerializer.Serialize(loginData), Encoding.UTF8, "application/json"); var response = await client.PostAsync(loginUrl, content); if (response.IsSuccessStatusCode) { var responseString = await response.Content.ReadAsStringAsync(); var jwt = JsonSerializer.Deserialize<JwtResponse>(responseString); // 存儲 JWT 到 Session HttpContext.Session.SetString("JWT", jwt.Token); return RedirectToAction("Index", "Home"); } ModelState.AddModelError(string.Empty, "Invalid login attempt."); return View(model); } } // AuthorizedHttpClientHandler.cs public class AuthorizedHttpClientHandler : DelegatingHandler { private readonly IHttpContextAccessor _httpContextAccessor; public AuthorizedHttpClientHandler(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var token = _httpContextAccessor.HttpContext.Session.GetString("JWT"); if (!string.IsNullOrEmpty(token)) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); } else { Console.WriteLine("No JWT token found in session."); } Console.WriteLine($"Request: {request.Method} {request.RequestUri}"); Console.WriteLine($"Authorization: {request.Headers.Authorization}"); return await base.SendAsync(request, cancellationToken); } } // SomeController.cs public class SomeController : Controller { private readonly IHttpClientFactory _httpClientFactory; public SomeController(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public async Task<IActionResult> SomeAction() { var client = _httpClientFactory.CreateClient("AuthorizedClient"); var response = await client.GetAsync("https://yourapi.com/api/protectedresource"); if (response.IsSuccessStatusCode) { var data = await response.Content.ReadAsStringAsync(); // 處理數據 } else { // 處理錯誤 } return View(); } }
總結 這些步驟應該可以幫助你診斷和解決 401 Unauthorized 錯誤。請確保 JWT 被正確地附加到每個需要授權的請求中,並且 API 端配置正確。