在测试 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<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);
就是这样。现在,当您进行测试时,它将绕过身份验证。我已经使用提供的控制器对其进行了测试,并且可以正常工作。
也可以将 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)?