出現 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 端配置正確。