Google Code Prettify

顯示具有 API 標籤的文章。 顯示所有文章
顯示具有 API 標籤的文章。 顯示所有文章

存取的 RESTful API 使用 JWT 作為身份驗證方式,出現 401 Unauthorized 錯誤

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

如何使用HttpClient存取具有Authorize的RESTfull API?

要使用 `HttpClient` 存取需要授權的 RESTful API,你通常需要在請求中附加適當的身份驗證憑證,例如 JWT(JSON Web Token)。以下是完整的步驟,包括如何生成 JWT、配置 `HttpClient` 並發送授權請求。

### 假設場景
- 你的 RESTful API 使用 JWT 作為身份驗證方式。
- 你已經擁有用戶憑證(如用戶名和密碼)並且能夠獲取 JWT。

### 步驟 1:生成 JWT
首先,你需要獲取 JWT,通常是通過登錄端點獲取。這個端點驗證用戶名和密碼並返回一個 JWT。


public async Task GetJwtTokenAsync(string username, string password)
{
    var client = new HttpClient();
    var loginUrl = "https://yourapi.com/api/auth/login";

    var loginData = new
    {
        Username = username,
        Password = password
    };

    var content = new StringContent(JsonSerializer.Serialize(loginData), Encoding.UTF8, "application/json");
    var response = await client.PostAsync(loginUrl, content);

    response.EnsureSuccessStatusCode();

    var responseString = await response.Content.ReadAsStringAsync();
    var jwt = JsonSerializer.Deserialize(responseString);

    return jwt.Token;
}

public class JwtResponse
{
    public string Token { get; set; }
}


### 步驟 2:配置 HttpClient 並附加 JWT
在獲取 JWT 之後,你可以在後續的請求中附加此 JWT 進行授權。


public async Task GetProtectedResourceAsync(string token)
{
    var client = new HttpClient();
    var requestUrl = "https://yourapi.com/api/protectedresource";

    // 附加 Bearer Token 到請求頭
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

    var response = await client.GetAsync(requestUrl);

    response.EnsureSuccessStatusCode();

    return await response.Content.ReadAsStringAsync();
}


### 完整示例
以下是完整的代碼,包含了從獲取 JWT 到使用 JWT 訪問受保護資源的流程:


using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

public class Program
{
    public static async Task Main(string[] args)
    {
        string username = "yourusername";
        string password = "yourpassword";

        string token = await GetJwtTokenAsync(username, password);
        string protectedResource = await GetProtectedResourceAsync(token);

        Console.WriteLine(protectedResource);
    }

    public static async Task GetJwtTokenAsync(string username, string password)
    {
        var client = new HttpClient();
        var loginUrl = "https://yourapi.com/api/auth/login";

        var loginData = new
        {
            Username = username,
            Password = password
        };

        var content = new StringContent(JsonSerializer.Serialize(loginData), Encoding.UTF8, "application/json");
        var response = await client.PostAsync(loginUrl, content);

        response.EnsureSuccessStatusCode();

        var responseString = await response.Content.ReadAsStringAsync();
        var jwt = JsonSerializer.Deserialize(responseString);

        return jwt.Token;
    }

    public static async Task GetProtectedResourceAsync(string token)
    {
        var client = new HttpClient();
        var requestUrl = "https://yourapi.com/api/protectedresource";

        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

        var response = await client.GetAsync(requestUrl);

        response.EnsureSuccessStatusCode();

        return await response.Content.ReadAsStringAsync();
    }
}

public class JwtResponse
{
    public string Token { get; set; }
}


### 注意事項
1. **安全性**:確保密碼和其他敏感信息的安全傳輸和存儲。
2. **錯誤處理**:添加適當的錯誤處理邏輯,例如處理登錄失敗或令牌過期的情況。
3. **Token 更新**:考慮在令牌過期後刷新令牌。

這樣,你就可以使用 `HttpClient` 訪問需要授權的 RESTful API。這種方式適用於各種需要身份驗證的 HTTP 請求場景。