在WebApplicationFactory<Program>的XUnit测试中Mock JWT令牌验证控制器
ASP.NET Core 6集成测试:模拟JWT授权测试带权限控制器
1. 自定义WebApplicationFactory,替换认证配置
继承WebApplicationFactory<Program>后,在ConfigureWebHost方法中替换原有JWT认证服务,改用测试专用的认证方案,直接注入自定义Claims,无需真实签发JWT令牌:
public class CustomWebApplicationFactory : WebApplicationFactory<Program> { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { // 移除原认证服务配置 var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IAuthenticationService)); if (descriptor != null) { services.Remove(descriptor); } // 添加测试用认证服务 services.AddAuthentication("TestScheme") .AddScheme<AuthenticationSchemeOptions, TestAuthenticationHandler>("TestScheme", options => { }); }); // 保留原有内存数据库等测试配置 base.ConfigureWebHost(builder); } } // 自定义测试认证处理器,直接读取预定义的ClaimsPrincipal public class TestAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> { public TestAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override Task<AuthenticateResult> HandleAuthenticateAsync() { if (Context.Items.TryGetValue("TestUser", out var user) && user is ClaimsPrincipal claimsPrincipal) { var ticket = new AuthenticationTicket(claimsPrincipal, Scheme.Name); return Task.FromResult(AuthenticateResult.Success(ticket)); } return Task.FromResult(AuthenticateResult.Fail("No test user provided")); } }
2. 扩展HttpClient,快速添加测试用户Claims
创建扩展方法,方便给HttpClient附加不同权限的ClaimsPrincipal:
public static class HttpClientExtensions { public static HttpClient WithTestUser(this HttpClient client, params Claim[] claims) { var claimsIdentity = new ClaimsIdentity(claims, "TestScheme"); var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); // 通过自定义Handler将ClaimsPrincipal注入HttpContext var handler = new TestRequestHandler(claimsPrincipal) { InnerHandler = new HttpClientHandler() }; return new HttpClient(handler) { BaseAddress = client.BaseAddress }; } } public class TestRequestHandler : DelegatingHandler { private readonly ClaimsPrincipal _testUser; public TestRequestHandler(ClaimsPrincipal testUser) { _testUser = testUser; } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.Properties.TryGetValue("Microsoft.AspNetCore.Http.HttpContext", out var contextObj) && contextObj is HttpContext context) { context.Items["TestUser"] = _testUser; } return await base.SendAsync(request, cancellationToken); } }
3. 编写测试方法,验证不同权限场景
针对[Authorize(Roles = "User")]和[Authorize(Policy = "IsConfirmed")]的控制器,生成对应Claims的测试用户,验证请求结果:
public class AuthorizedControllerTests : IClassFixture<CustomWebApplicationFactory> { private readonly HttpClient _client; public AuthorizedControllerTests(CustomWebApplicationFactory factory) { _client = factory.CreateClient(); } [Fact] public async Task Get_WithUserRoleAndConfirmedClaim_ReturnsOk() { var authorizedClient = _client.WithTestUser( new Claim(ClaimTypes.Role, "User"), new Claim("IsConfirmed", "true") // 匹配自定义策略的Claim,需和项目中策略定义一致 ); var response = await authorizedClient.GetAsync("/api/authorized/confirmed-user"); response.EnsureSuccessStatusCode(); } [Fact] public async Task Get_OnlyUserRoleWithoutConfirmedClaim_ReturnsForbidden() { var unconfirmedClient = _client.WithTestUser( new Claim(ClaimTypes.Role, "User") ); var response = await unconfirmedClient.GetAsync("/api/authorized/confirmed-user"); Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } [Fact] public async Task Get_WithoutUserRole_ReturnsUnauthorized() { var anonymousClient = _client.WithTestUser(); var response = await anonymousClient.GetAsync("/api/authorized/user-only"); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } }
可选:模拟真实JWT令牌授权
如果需要完全模拟真实JWT流程,可直接生成签名令牌并添加到请求头:
var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes("your-test-jwt-secret"); // 需和项目配置中的JWT密钥一致 var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Role, "User"), new Claim("IsConfirmed", "true") }), Expires = DateTime.UtcNow.AddHours(1), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); var tokenString = tokenHandler.WriteToken(token); _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenString);
内容的提问来源于stack exchange,提问作者Silny ToJa
相关产品推荐
相关产品推荐

