c# - 在测试 ASP.Net Core 3.1 Web Api 期间跳过 JWT Auth
问题描述
我有一个非常简单的应用程序,其中包含一个经过 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);
}
像这样运行测试会失败,所以按照这个文档,我添加了这个简单的身份验证处理程序:
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);
}
}
它仍然失败,我不知道我做错了什么。
解决方案
我以前在 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);
}
这就对了。现在,当您进行测试时,它将绕过身份验证。我已经使用提供的控制器对其进行了测试,并且可以正常工作。
也可以将假的放在应用程序启动中,它既可用于测试又可在开发环境下工作。检查引用的文章。
免责声明:我已经在我的个人网站参考中写了更深入的文章,您可以下载此https://github.com/maythamfahmi/BlogExamples/tree/master/BypassAuthorization的 github 版本
推荐阅读
- python - 如何过滤多个数据框并将字符串附加到保存文件名?
- android - 如何以编程方式在我的世界中安装 mcpack android
- jenkins - 空值比较不适用于詹金斯 groovy 脚本
- javascript - Mapbox GL JS 是否具有渲染特征的“命中容差”属性?
- c# - OpenXml 从 Word、Excel 和 Powerpoint 中删除水印
- javascript - 通过输入文本和通过按钮单击选择类别来反应对象的过滤数组
- javascript - 如何将 JSON 对象转换为 Vue v-for 兼容格式
- django - 尽管已安装,但无法在 django 中导入熊猫
- mongodb - 当我尝试启动时,mongochart 获取数据库连接超时
- javascript - 在计算最小值时排除某些行