在测试期间跳过 JWT 身份验证 ASP.Net Core 3.1 Web Api
Skip JWT Auth during Tests ASP.Net Core 3.1 Web Api
我有一个非常简单的应用程序,带有一个经过 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
};
});
在测试期间,我希望用户一直处于 "authenticated" 状态,这样 [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));
}
}
所以现在我的测试 class 看起来像这样:
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);
}
}
它仍然失败,我不知道我做错了什么。
您需要设置DefaultAuthenticateScheme
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(options =>
{
x.DefaultAuthenticateScheme = TestAuthHandler.DefaultScheme;
x.DefaultScheme = TestAuthHandler.DefaultScheme;
}).AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
TestAuthHandler.DefaultScheme, options => { });
});
我以前在 Microsoft 示例中遇到过类似情况,可以向您保证它会让您头疼,它可能适用于特定的 Core 版本,但我已经放弃了。我已经这样解决了。
我的目标是在测试时授权系统,而不是在我们的测试中使用 AddAuthentication
我们只是创建一个 FakePolicyEvaluator
class 并将其作为单例添加到我们的测试。
所以让我们的 FakePolicyEvaluator class:
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<IPolicyEvaluator, FakePolicyEvaluator>();
所以在你的测试代码中是这样的:
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);
}
就是这样。现在当你测试时,它会绕过身份验证。我已经使用提供的控制器对其进行了测试并且它有效。
也可以在应用程序启动时放置伪造的,既可用于测试,也可在开发环境下运行。查看引用的文章。
Disclaimer: I have wrote in more depth article about this in my personal website Reference you can download a github version of this https://github.com/maythamfahmi/BlogExamples/tree/master/BypassAuthorization
我有一个非常简单的应用程序,带有一个经过 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
};
});
在测试期间,我希望用户一直处于 "authenticated" 状态,这样 [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));
}
}
所以现在我的测试 class 看起来像这样:
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);
}
}
它仍然失败,我不知道我做错了什么。
您需要设置DefaultAuthenticateScheme
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(options =>
{
x.DefaultAuthenticateScheme = TestAuthHandler.DefaultScheme;
x.DefaultScheme = TestAuthHandler.DefaultScheme;
}).AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
TestAuthHandler.DefaultScheme, options => { });
});
我以前在 Microsoft 示例中遇到过类似情况,可以向您保证它会让您头疼,它可能适用于特定的 Core 版本,但我已经放弃了。我已经这样解决了。
我的目标是在测试时授权系统,而不是在我们的测试中使用 AddAuthentication
我们只是创建一个 FakePolicyEvaluator
class 并将其作为单例添加到我们的测试。
所以让我们的 FakePolicyEvaluator class:
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<IPolicyEvaluator, FakePolicyEvaluator>();
所以在你的测试代码中是这样的:
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);
}
就是这样。现在当你测试时,它会绕过身份验证。我已经使用提供的控制器对其进行了测试并且它有效。
也可以在应用程序启动时放置伪造的,既可用于测试,也可在开发环境下运行。查看引用的文章。
Disclaimer: I have wrote in more depth article about this in my personal website Reference you can download a github version of this https://github.com/maythamfahmi/BlogExamples/tree/master/BypassAuthorization