在测试 ASP.Net Core 3.1 Web Api 期间跳过 JWT Auth

Posted

技术标签:

【中文标题】在测试 ASP.Net Core 3.1 Web Api 期间跳过 JWT Auth【英文标题】:Skip JWT Auth during Tests ASP.Net Core 3.1 Web Api 【发布时间】:2020-08-29 08:14:03 【问题描述】:

我有一个非常简单的应用程序,其中包含一个经过 JWT 身份验证的控制器:

[ApiController]
[Authorize]
[Route("[controller]")]
public class JwtController : ControllerBase


    public JwtController()  

    [HttpGet]
    public ActionResult Get() => Ok("Working!");

身份验证配置为:

services.AddAuthentication(x =>

    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
)
.AddJwtBearer(x =>

    x.RequireHttpsMetadata = false;
    x.SaveToken = true;
    x.TokenValidationParameters = new TokenValidationParameters
    
        ValidateIssuer = false,
        ValidateAudience = false
    ;
);

在测试期间,我希望用户始终被“认证”,以便跳过 [Authorize]。

[Fact]
public async Task JwtIsSkipped()

    var response = (await _Client.GetAsync("/jwt")).EnsureSuccessStatusCode();
    var stringResponse = await response.Content.ReadAsStringAsync();

    Assert.Equal("Working!", stringResponse);

像这样运行测试会失败,所以我按照this doc 添加了这个简单的身份验证处理程序:

public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>

    public const string DefaultScheme = "Test";
    public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
        : base(options, logger, encoder, clock)
    
    

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    
        var claims = new[]  new Claim(ClaimTypes.Name, "Test user") ;
        var identity = new ClaimsIdentity(claims, DefaultScheme);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, DefaultScheme);

        return Task.FromResult(AuthenticateResult.Success(ticket));
    

所以现在我的测试类看起来像这样:

public class UnitTest : IClassFixture<WebApplicationFactory<Startup>>

    private readonly WebApplicationFactory<Startup> _Factory;
    private readonly HttpClient _Client;

    public UnitTest(WebApplicationFactory<Startup> factory)
    
        _Factory = factory;
        _Client = _Factory.WithWebHostBuilder(builder =>
        
            builder.ConfigureTestServices(services =>
            
                services.AddAuthentication(TestAuthHandler.DefaultScheme)
                        .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
                            TestAuthHandler.DefaultScheme, options =>  );
            );
        ).CreateClient();

        _Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(TestAuthHandler.DefaultScheme);
    

    [Fact]
    public async Task JwtIsSkipped()
    
        var response = (await _Client.GetAsync("/jwt")).EnsureSuccessStatusCode();
        var stringResponse = await response.Content.ReadAsStringAsync();

        Assert.Equal("Working!", stringResponse);
    

它仍然失败,我不知道我做错了什么。

【问题讨论】:

如果您的应用 ConfigureServices 在测试 ConfigureTestServices 之后运行,您可以试试吗? 我刚刚检查过,ConfigureTestServices 在 ConfigureServices 之后运行 好的,那么我认为它应该覆盖设置。您能否尝试在 UseAuthentication() 之后添加一个中间件并检查 HttpContext.User 是否具有您期望的声明? 不,似乎自定义的 HandleAuthenticateAsync 甚至没有命中。 【参考方案1】:

你需要设置DefaultAuthenticateScheme

builder.ConfigureTestServices(services =>

    services.AddAuthentication(options =>
    
        x.DefaultAuthenticateScheme = TestAuthHandler.DefaultScheme;
        x.DefaultScheme = TestAuthHandler.DefaultScheme;
    ).AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
           TestAuthHandler.DefaultScheme, options =>  );
);

【讨论】:

是的,这是诀窍,只需添加另一个处理程序并在您的测试设置中设置为默认值【参考方案2】:

我之前在 Microsoft 示例中遇到过类似的情况,可以向您保证它会让人头疼,它可能适用于特定的 Core 版本,但我已经放弃了。我已经这样解决了。

我的目标是在测试时授权系统,而不是在我们的测试中使用AddAuthentication,我们只是创建一个FakePolicyEvaluator 类并将其作为单例添加到我们的测试中。

所以放开我们的 FakePolicyEvaluator 类:

public class FakePolicyEvaluator : IPolicyEvaluator

    public virtual async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
    
        var principal = new ClaimsPrincipal();
        principal.AddIdentity(new ClaimsIdentity(new[] 
            new Claim("Permission", "CanViewPage"),
            new Claim("Manager", "yes"),
            new Claim(ClaimTypes.Role, "Administrator"),
            new Claim(ClaimTypes.NameIdentifier, "John")
        , "FakeScheme"));
        return await Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal,
            new AuthenticationProperties(), "FakeScheme")));
    

    public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy,
        AuthenticateResult authenticationResult, HttpContext context, object resource)
    
        return await Task.FromResult(PolicyAuthorizationResult.Success());
    

然后在我们的ConfigureTestServices 中添加了services.AddSingleton&lt;IPolicyEvaluator, FakePolicyEvaluator&gt;();

所以在你的测试代码中是这样的:

private readonly HttpClient _client;

public UnitTest(WebApplicationFactory<Startup> factory)

    _client = factory.WithWebHostBuilder(builder =>
    
        builder.ConfigureTestServices(services =>
        
            services.AddSingleton<IPolicyEvaluator, FakePolicyEvaluator>();
        );
    ).CreateClient();


[Fact]
public async Task JwtIsSkipped()

    var response = (await _client.GetAsync("/jwt")).EnsureSuccessStatusCode();
    var stringResponse = await response.Content.ReadAsStringAsync();

    Assert.Equal("Working!", stringResponse);

就是这样。现在,当您进行测试时,它将绕过身份验证。我已经使用提供的控制器对其进行了测试,并且可以正常工作。

也可以将 fake 放置在应用程序启动中,它既可用于测试,又可在开发环境下工作。检查引用的文章。

免责声明:我已在我的个人网站Reference 上写过更深入的文章,您可以下载此https://github.com/maythamfahmi/BlogExamples/tree/master/BypassAuthorization 的 github 版本

【讨论】:

ClaimsPrincipal 中缺少声明。如果您检查_httpContext.HttpContext.User,您会注意到其中没有包含任何声明 检查github.com/maythamfahmi/BlogExamples/issues/1

以上是关于在测试 ASP.Net Core 3.1 Web Api 期间跳过 JWT Auth的主要内容,如果未能解决你的问题,请参考以下文章

Wcf 服务在 .NET Core 3.1 控制台应用程序中工作,但在 ASP.NET Core 3.1 Web API 中无法工作

Asp.net core 3.1 保护 API 和 Web 应用程序

ASP.Net Core 3.1 - Web API 中的 Post 参数始终为 NULL

我应该如何保护我的 Web 应用程序(ASP.Net Core 3.1 MVC)?

我需要在启动时在 ASP .Net Core 3.1 Web API 中执行异步方法

ASP.NET Core--.net core 3.1 部署到 IIS