我构建了一个简单的AspNetCore 2.2
API,它使用IdentityServer 4
来处理OAuth。它运行得很好,但是我现在想添加集成测试和最近发现的这。我使用它构建了一些测试,这些测试都很好--只要我的控制器上没有[Authorize]
属性--但是很明显,这个属性需要存在。
我偶然发现了这个堆叠溢出问题,在给出的答案中,我尝试将一个测试放在一起,但是当我尝试运行测试时,我仍然得到了一个Unauthorized
响应。
请注意:我真的不知道在创建客户机时应该使用哪些细节。
也是在构建IdentityServerWebHostBuilder
时
.AddApiResources
?(也许是个愚蠢的问题,但这有关系吗?)如果有人能指导我,我们将不胜感激。
这是我的测试:
[Fact]
public async Task Attempt_To_Test_InMemory_IdentityServer()
{
// Create a client
var clientConfiguration = new ClientConfiguration("MyClient", "MySecret");
var client = new Client
{
ClientId = clientConfiguration.Id,
ClientSecrets = new List<Secret>
{
new Secret(clientConfiguration.Secret.Sha256())
},
AllowedScopes = new[] { "api1" },
AllowedGrantTypes = new[] { GrantType.ClientCredentials },
AccessTokenType = AccessTokenType.Jwt,
AllowOfflineAccess = true
};
var webHostBuilder = new IdentityServerWebHostBuilder()
.AddClients(client)
.AddApiResources(new ApiResource("api1", "api1name"))
.CreateWebHostBuilder();
var identityServerProxy = new IdentityServerProxy(webHostBuilder);
var tokenResponse = await identityServerProxy.GetClientAccessTokenAsync(clientConfiguration, "api1");
// *****
// Note: creating an IdentityServerProxy above in order to get an access token
// causes the next line to throw an exception stating: WebHostBuilder allows creation only of a single instance of WebHost
// *****
// Create an auth server from the IdentityServerWebHostBuilder
HttpMessageHandler handler;
try
{
var fakeAuthServer = new TestServer(webHostBuilder);
handler = fakeAuthServer.CreateHandler();
}
catch (Exception e)
{
throw;
}
// Create an auth server from the IdentityServerWebHostBuilder
HttpMessageHandler handler;
try
{
var fakeAuthServer = new TestServer(webHostBuilder);
handler = fakeAuthServer.CreateHandler();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
// Set the BackChannelHandler of the 'production' IdentityServer to use the
// handler form the fakeAuthServer
Startup.BackChannelHandler = handler;
// Create the apiServer
var apiServer = new TestServer(new WebHostBuilder().UseStartup<Startup>());
var apiClient = apiServer.CreateClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var user = new User
{
Username = "simonlomax@ekm.com",
Password = "Password-123"
};
var req = new HttpRequestMessage(new HttpMethod("GET"), "/api/users/login")
{
Content = new StringContent(JsonConvert.SerializeObject(user), Encoding.UTF8, "application/json"),
};
// Act
var response = await apiClient.SendAsync(req);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
我的创业课:
public class Startup
{
public IConfiguration Configuration { get; }
public static HttpMessageHandler BackChannelHandler { get; set; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
ConfigureAuth(services);
services.AddTransient<IPassportService, PassportService>();
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
}
protected virtual void ConfigureAuth(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServerAuthority");
options.Audience = Configuration.GetValue<string>("IdentityServerAudience");
options.BackchannelHttpHandler = BackChannelHandler;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseMvc();
app.UseExceptionMiddleware();
}
}
发布于 2019-04-09 17:18:44
编辑:
以下建议是一个问题。由于试图构建WebHostBuilder
两次,原始源代码因异常而失败。其次,配置文件只存在于API项目中,而不在测试项目中,这就是为什么没有设置权限的原因。
而不是这样做
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServerAuthority");
options.Audience = Configuration.GetValue<string>("IdentityServerAudience");
options.BackchannelHttpHandler = BackChannelHandler;
});
你必须这样做:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServerAuthority");
options.JwtBackChannelHandler = BackChannelHandler;
});
您可以找到一个样本这里。
希望能帮上忙,为我工作过!
发布于 2019-10-29 22:50:54
不影响生产代码的解决方案:
public class TestApiWebApplicationFactory<TStartup>
: WebApplicationFactory<TStartup> where TStartup : class
{
private readonly HttpClient _identityServerClient;
public TestApiWebApplicationFactory(HttpClient identityServerClient)
{
_identityServerClient = identityServerClient;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureServices(
s =>
{
s.AddSingleton<IConfigureOptions<JwtBearerOptions>>(services =>
{
return new TestJwtBearerOptions(_identityServerClient);
});
});
}
}
它的用途是:
_factory = new WebApplicationFactory<Startup>()
{
ClientOptions = {BaseAddress = new Uri("http://localhost:5000/")}
};
_apiFactory = new TestApiWebApplicationFactory<SampleApi.Startup>(_factory.CreateClient())
{
ClientOptions = {BaseAddress = new Uri("http://localhost:5001/")}
};
TestJwtBearerOptions只是代理对identityServerClient的请求。您可以在这里找到的实现:https://gist.github.com/ru-sh/048e155d73263912297f1de1539a2687
发布于 2019-10-01 19:09:54
如果您不希望依赖静态变量来保存HttpHandler,那么我发现下面的内容是可行的。我觉得它更干净了。
首先,创建一个可以在创建TestHost之前实例化的对象。这是因为在创建HttpHandler之前不会有TestHost,所以需要使用包装器。
public class TestHttpMessageHandler : DelegatingHandler
{
private ILogger _logger;
public TestHttpMessageHandler(ILogger logger)
{
_logger = logger;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
_logger.Information($"Sending HTTP message using TestHttpMessageHandler. Uri: '{request.RequestUri.ToString()}'");
if (WrappedMessageHandler == null) throw new Exception("You must set WrappedMessageHandler before TestHttpMessageHandler can be used.");
var method = typeof(HttpMessageHandler).GetMethod("SendAsync", BindingFlags.Instance | BindingFlags.NonPublic);
var result = method.Invoke(this.WrappedMessageHandler, new object[] { request, cancellationToken });
return await (Task<HttpResponseMessage>)result;
}
public HttpMessageHandler WrappedMessageHandler { get; set; }
}
然后
var testMessageHandler = new TestHttpMessageHandler(logger);
var webHostBuilder = new WebHostBuilder()
...
services.PostConfigureAll<JwtBearerOptions>(options =>
{
options.Audience = "http://localhost";
options.Authority = "http://localhost";
options.BackchannelHttpHandler = testMessageHandler;
});
...
var server = new TestServer(webHostBuilder);
var innerHttpMessageHandler = server.CreateHandler();
testMessageHandler.WrappedMessageHandler = innerHttpMessageHandler;
https://stackoverflow.com/questions/55590928
复制相似问题