You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core集成测试本地通过但GitHub Actions报403 Forbidden

ASP.NET Core 8 Web API集成测试GitHub Actions 403 Forbidden问题排查

问题背景

我正在为ASP.NET Core 8 Web API项目运行集成测试,某测试在本地Visual Studio Test Explorer中可正常通过,但在GitHub Actions执行时出现403 Forbidden错误。

相关代码

GenerateToken.cs(ProSys.Tests.Integration项目)

public class GenerateToken
{
  private readonly HttpClient _client;

  public GenerateToken(CustomWebApplicationFactory<Program> factory)
  {
     _client = factory.CreateClient();
  }

  public async Task<string> GetJwtToken()
  {
     var loginPayload = new { username = "owner", password = "P@$$w0rd" };
     var response = await _client.PostAsJsonAsync("/api/v1/account/login", 
       loginPayload);

    response.EnsureSuccessStatusCode();

    var responseContent = await response.Content.ReadAsStringAsync();

    var json = JsonSerializer.Deserialize<ApiResponseWrapper<LoginResponseResult>> 
       (responseContent,
          new JsonSerializerOptions
          {
            PropertyNameCaseInsensitive = true,
            Converters = { new JsonStringEnumConverter() }
          });

    if (json?.Value?.Token == null)
    {
        throw new Exception($"Token generation failed. Response: {responseContent}");
    }

    return json.Value.Token;
   }
}

AuthTests.cs

public class AuthTests : IClassFixture<CustomWebApplicationFactory<Program>>
{
  private readonly HttpClient _client;
  private readonly GenerateToken _generateToken;

  public AuthTests(CustomWebApplicationFactory<Program> factory)
  {
      _client = factory.CreateClient();
      _generateToken = new GenerateToken(factory);
  }

  [Fact]
  public async Task Get_Protected_Endpoint_Should_Return_Unauthorized_Without_Token()
  {
      var response = await _client.GetAsync("/api/v1/userType/retrieve");
    response.StatusCode.Should().Be(System.Net.HttpStatusCode.Unauthorized);
  }

  [Fact]
  public async Task Get_Protected_Endpoint_Should_Return_Success_With_Dynamic_Token()
  {
      var token = await _generateToken.GetJwtToken();
      _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

      var response = await _client.GetAsync("/api/v1/userType/retrieve");

      response.EnsureSuccessStatusCode();
  }
}

问题详情

测试流程如下:

  1. 使用有效凭据(owner / P@$$w0rd)调用/api/v1/account/login端点获取JWT令牌
  2. 将令牌放入Authorization头,调用受保护的/api/v1/userType/retrieve端点
  3. 本地测试返回200 OK符合预期,但GitHub Actions中返回403 Forbidden

已确认GitHub Actions中数据库表创建和数据插入正常,owner用户凭据正确且拥有该端点访问权限(Postman携带令牌调用正常)。令牌采用RSA算法签发验证,公私钥存储在GitHub Secrets的secrets.json中,API登录流程可正常使用该密钥处理JWT。

GitHub Actions错误信息

ProSys.Tests.Integration.Tests.AuthTests.AuthTests.Get_Protected_Endpoint_Should_Return_Success_With_Dynamic_Token [FAIL]
    [xUnit.net 00:00:12.08] System.Net.Http.HttpRequestException : 
    Response status code does not indicate success: 403 (Forbidden).

疑问

  1. 如何调试GitHub Actions中是否正确读取到secrets.json?
  2. 排查本地可用但GitHub Actions中令牌被拒绝(403 Forbidden)的最佳方法有哪些?

解答

1. 验证GitHub Actions是否读取到secrets.json

  • 在GitHub Actions workflow中添加临时步骤,将secrets内容输出到日志(调试完成后立即移除,避免密钥泄露):
    - name: Debug secrets.json content
      run: echo "${{ secrets.YOUR_SECRETS_JSON_KEY }}"
      shell: bash
    
  • 在测试代码的配置加载逻辑中添加临时日志,打印RSA密钥片段(比如前20个字符),确认是否正确加载:
    // 在CustomWebApplicationFactory或Program.cs配置中添加
    var rsaPublicKey = Configuration["Jwt:PublicKey"];
    Console.WriteLine($"Loaded RSA Public Key (partial): {rsaPublicKey?.Substring(0, 20)}...");
    
  • 检查workflow中是否将secrets.json写入到项目的正确路径,示例:
    - name: Create secrets.json
      run: echo "${{ secrets.SECRETS_JSON }}" > ./ProSys.Api/appsettings.secrets.json
      shell: bash
    

2. 排查令牌在GitHub Actions中被拒绝的方法

  • 打印并验证令牌内容:在GenerateToken.cs的GetJwtToken方法中临时打印生成的令牌,拿到后用JWT解析工具检查声明(claims)是否与本地一致,重点确认权限相关的claim(如role、permissions):
    // 在GenerateToken.cs中添加临时日志
    Console.WriteLine($"Generated Token: {json.Value.Token}");
    
  • 检查令牌验证逻辑一致性:确认GitHub Actions环境中,API的JWT验证配置与本地一致:
    • 验证算法是否为RS256,公钥是否正确加载
    • 检查令牌过期时间(exp claim),是否因环境时区差异导致令牌提前过期
  • 输出授权上下文日志:在受保护端点的授权过滤器或方法中添加日志,打印当前用户的身份信息和权限,确认与本地测试时一致:
    // 在授权逻辑中添加
    var user = HttpContext.User;
    Console.WriteLine($"Authenticated User: {user.Identity.Name}, Roles: {string.Join(",", user.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value))}");
    
  • 配置转发头:若API在GitHub Actions中使用HTTPS,确保正确配置转发头,避免请求上下文异常导致验证失败:
    // 在Program.cs中添加
    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    });
    
  • 对比环境变量:在workflow中打印所有相关环境变量,确认与本地开发环境配置一致:
    - name: Print environment variables
      run: printenv | grep -E "JWT|ASPNETCORE"
      shell: bash
    

内容的提问来源于stack exchange,提问作者Hadi Soufan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 14:30:54