.NET集成测试:指定认证方案的授权端点测试及JWT模拟问题
ASP.NET Web API 集成测试授权端点问题
我正在为带有授权端点的ASP.NET Web API设置集成测试,之前参考微软文档添加了模拟认证,让测试客户端能访问授权端点:
builder.ConfigureTestServices(services => { services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder() .AddAuthenticationSchemes("Test") .RequireAuthenticatedUser() .Build(); }); }
这个方案在使用默认认证方案时可行,但我的授权端点明确指定了AuthenticationSchemes,测试方案无法通过授权:
[Authorize(AuthenticationSchemes = "Scheme1,Scheme2")] public class AppVersionController : ControllerBase { ... }
我试过通过环境变量动态把测试方案添加到授权端点,用自定义属性实现:
public class AuthorizeAll : AuthorizeAttribute { public AuthorizeAll() { var authenticationSchemes = "Scheme1,Scheme2"; if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Testing") { authenticationSchemes += ",Test"; } AuthenticationSchemes = authenticationSchemes; } }
但这种方式需要在业务层维护测试认证方案,有维护成本和安全顾虑,所以想咨询三个问题:
- 指定认证方案时,.NET集成测试中授权端点的最佳测试方式是什么?
- 单元测试中通过检查环境变量执行测试专属逻辑是否为最佳实践?
- 当前主认证方案为JWT,是否有更优的JWT模拟测试方案?
问题1:指定认证方案时,.NET集成测试中授权端点的最佳测试方式
完全不用修改业务代码,直接在测试项目的ConfigureTestServices里扩展或替换授权策略即可,推荐两种方案:
方案一:直接扩展目标授权策略
如果控制器是通过指定AuthenticationSchemes生成的临时策略,或者有明确的自定义策略,可以在测试配置里直接修改,把测试认证方案加入其中:
builder.ConfigureTestServices(services => { // 注册Test认证方案的处理器(需要自己实现TestAuthHandler,逻辑是直接通过认证) services.AddAuthentication("Test") .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", options => { }); services.AddAuthorization(options => { // 覆盖默认策略,加入Test方案 options.DefaultPolicy = new AuthorizationPolicyBuilder() .AddAuthenticationSchemes("Scheme1", "Scheme2", "Test") .RequireAuthenticatedUser() .Build(); // 如果是自定义策略,直接修改对应策略 options.AddPolicy("CustomSchemePolicy", policy => { policy.AddAuthenticationSchemes("Scheme1", "Scheme2", "Test") .RequireAuthenticatedUser(); }); }); });
方案二:自定义授权策略提供器
如果不想逐个修改策略,可以写一个全局的策略提供器,自动给所有授权策略添加Test认证方案:
public class TestAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider { public TestAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options) : base(options) { } public override async Task<AuthorizationPolicy> GetPolicyAsync(string policyName) { var policy = await base.GetPolicyAsync(policyName); if (policy == null) return null; // 将Test方案追加到原有认证方案列表并去重 var newSchemes = policy.AuthenticationSchemes.Append("Test").Distinct(); return new AuthorizationPolicyBuilder(newSchemes) .Combine(policy) .Build(); } }
然后在测试配置里注册这个提供器:
builder.ConfigureTestServices(services => { services.AddSingleton<IAuthorizationPolicyProvider, TestAuthorizationPolicyProvider>(); // 注册Test认证方案 services.AddAuthentication("Test") .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", options => { }); });
问题2:单元测试中通过检查环境变量执行测试专属逻辑是否为最佳实践
这不是最佳实践,主要问题有:
- 维护成本高:业务代码混入测试逻辑,后续修改测试方案或环境配置都要改动业务代码
- 安全风险:如果测试环境的配置意外流入生产,可能导致未授权访问漏洞
- 测试稳定性差:环境变量属于外部依赖,会让测试结果受外部配置影响
更好的做法:
- 集成测试:通过测试项目的
ConfigureTestServices覆盖或扩展服务,完全隔离业务代码和测试逻辑 - 单元测试:用依赖注入替换认证相关依赖,比如Mock认证服务,或者使用测试专属的配置类
问题3:JWT模拟测试的更优方案
不用自定义Test认证方案,直接模拟合法JWT令牌即可,推荐两种常用方式:
方案一:配置测试专用JWT验证
在测试项目里重新配置JWT认证,使用测试专属的密钥、Issuer和Audience,生成符合要求的JWT令牌:
builder.ConfigureTestServices(services => { // 移除原有JWT配置,避免冲突 services.RemoveAll<JwtBearerOptions>(); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = "test-issuer", ValidAudience = "test-audience", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("test-secret-key-1234567890")) }; }); });
测试时生成令牌并添加到请求头:
// 生成测试JWT令牌 var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes("test-secret-key-1234567890"); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "test-user"), new Claim(ClaimTypes.Role, "admin") }), Expires = DateTime.UtcNow.AddHours(1), Issuer = "test-issuer", Audience = "test-audience", SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); var jwtToken = tokenHandler.WriteToken(token); // 发送请求 var client = factory.CreateClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken); var response = await client.GetAsync("/api/appversion");
方案二:使用官方MockJwtBearer(ASP.NET Core 8+)
如果使用ASP.NET Core 8及以上版本,可以用官方提供的MockJwtBearer简化配置:
- 安装NuGet包:
Microsoft.AspNetCore.Authentication.JwtBearer.Testing - 测试配置里注册:
builder.ConfigureTestServices(services => { services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMockJwtBearer(); });
- 测试时直接生成令牌:
var client = factory.CreateClient(); var mockBearer = factory.Services.GetRequiredService<IMockJwtBearer>(); mockBearer.SetBearerToken(new[] { new Claim(ClaimTypes.Name, "test-user"), new Claim(ClaimTypes.Role, "admin") }); var response = await client.GetAsync("/api/appversion");
内容的提问来源于stack exchange,提问作者Jackson
相关产品推荐
相关产品推荐

