我有一个使用IdentityServer4进行令牌验证的API.我想用内存中的TestServer对这个API进行单元测试.我想将IdentityServer托管在内存中的TestServer中.
我已经设法从IdentityServer创建一个令牌.
这是我到底有多远,但是我收到错误"无法从http:// localhost:54100/.well-known/openid-configuration获取配置 "
Api使用[授权] - 属性与不同的策略.这是我想测试的.
可以这样做,我做错了什么?我试图查看IdentityServer4的源代码,但没有遇到过类似的集成测试场景.
protected IntegrationTestBase()
{
var startupAssembly = typeof(Startup).GetTypeInfo().Assembly;
_contentRoot = SolutionPathUtility.GetProjectPath(@"<my project path>", startupAssembly);
Configure(_contentRoot);
var orderApiServerBuilder = new WebHostBuilder()
.UseContentRoot(_contentRoot)
.ConfigureServices(InitializeServices)
.UseStartup<Startup>();
orderApiServerBuilder.Configure(ConfigureApp);
OrderApiTestServer = new TestServer(orderApiServerBuilder);
HttpClient = OrderApiTestServer.CreateClient();
}
private void InitializeServices(IServiceCollection services)
{
var cert = new X509Certificate2(Path.Combine(_contentRoot, "idsvr3test.pfx"), "idsrv3test");
services.AddIdentityServer(options =>
{
options.IssuerUri = "http://localhost:54100";
})
.AddInMemoryClients(Clients.Get())
.AddInMemoryScopes(Scopes.Get())
.AddInMemoryUsers(Users.Get())
.SetSigningCredential(cert);
services.AddAuthorization(options =>
{
options.AddPolicy(OrderApiConstants.StoreIdPolicyName, policy => policy.Requirements.Add(new StoreIdRequirement("storeId")));
});
services.AddSingleton<IPersistedGrantStore, InMemoryPersistedGrantStore>();
services.AddSingleton(_orderManagerMock.Object);
services.AddMvc();
}
private void ConfigureApp(IApplicationBuilder app)
{
app.UseIdentityServer();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var options = new IdentityServerAuthenticationOptions
{
Authority = _appsettings.IdentityServerAddress,
RequireHttpsMetadata = false,
ScopeName = _appsettings.IdentityServerScopeName,
AutomaticAuthenticate = false
};
app.UseIdentityServerAuthentication(options);
app.UseMvc();
}
Run Code Online (Sandbox Code Playgroud)
在我的单元测试中:
private HttpMessageHandler _handler;
const string TokenEndpoint = "http://localhost/connect/token";
public Test()
{
_handler = OrderApiTestServer.CreateHandler();
}
[Fact]
public async Task LeTest()
{
var accessToken = await GetToken();
HttpClient.SetBearerToken(accessToken);
var httpResponseMessage = await HttpClient.GetAsync("stores/11/orders/asdf"); // Fails on this line
}
private async Task<string> GetToken()
{
var client = new TokenClient(TokenEndpoint, "client", "secret", innerHttpMessageHandler: _handler);
var response = await client.RequestClientCredentialsAsync("TheMOON.OrderApi");
return response.AccessToken;
}
Run Code Online (Sandbox Code Playgroud)
Jam*_*era 21
您在初始问题中发布的代码处于正确的轨道上.
所述IdentityServerAuthenticationOptions对象具有属性覆盖默认HttpMessageHandlers它使用反向信道的通信.
将此与TestServer对象上的CreateHandler()方法结合使用后,您将获得:
//build identity server here
var idBuilder = new WebBuilderHost();
idBuilder.UseStartup<Startup>();
//...
TestServer identityTestServer = new TestServer(idBuilder);
var identityServerClient = identityTestServer.CreateClient();
var token = //use identityServerClient to get Token from IdentityServer
//build Api TestServer
var options = new IdentityServerAuthenticationOptions()
{
Authority = "http://localhost:5001",
// IMPORTANT PART HERE
JwtBackChannelHandler = identityTestServer.CreateHandler(),
IntrospectionDiscoveryHandler = identityTestServer.CreateHandler(),
IntrospectionBackChannelHandler = identityTestServer.CreateHandler()
};
var apiBuilder = new WebHostBuilder();
apiBuilder.ConfigureServices(c => c.AddSingleton(options));
//build api server here
var apiClient = new TestServer(apiBuilder).CreateClient();
apiClient.SetBearerToken(token);
//proceed with auth testing
Run Code Online (Sandbox Code Playgroud)
这允许Api项目中的AccessTokenValidation中间件直接与内存中的IdentityServer通信,而无需跳过箍.
作为旁注,对于Api项目,我发现使用TryAddSingleton将IdentityServerAuthenticationOptions添加到Startup.cs中的服务集合而不是内联创建它是有用的:
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton(new IdentityServerAuthenticationOptions
{
Authority = Configuration.IdentityServerAuthority(),
ScopeName = "api1",
ScopeSecret = "secret",
//...,
});
}
public void Configure(IApplicationBuilder app)
{
var options = app.ApplicationServices.GetService<IdentityServerAuthenticationOptions>()
app.UseIdentityServerAuthentication(options);
//...
}
Run Code Online (Sandbox Code Playgroud)
这允许您在测试中注册IdentityServerAuthenticationOptions对象,而无需更改Api项目中的代码.
我知道需要一个比@james-fera发布的答案更完整的答案.我从他的回答中学到了一个包含测试项目和API项目的github项目.代码应该是不言自明的,不难理解.
https://github.com/emedbo/identityserver-test-template
https://github.com/emedbo/identityserver-test-template/blob/master/tests/API.Tests/Config/IdentityServerSetup.cs这个IdentityServerSetup.cs
类可以被抽象掉,例如NuGetted,离开基类IntegrationTestBase.cs
其实质是可以使测试IdentityServer像普通的IdentityServer一样工作,具有用户,客户端,范围,密码等.我已经使用DELETE方法[Authorize(Role ="admin)]来证明这一点.
我不建议在这里发布代码,而是推荐阅读@james-fera的帖子来获取基础知识然后拉动我的项目并运行测试.
IdentityServer是一个非常棒的工具,并且能够使用TestServer框架,它变得更好.
我认为您可能需要为您的授权中间件制作一个测试双重伪造,具体取决于您想要多少功能。因此,基本上您需要一个中间件来完成授权中间件所做的所有事情,减去对发现文档的反向通道调用。
IdentityServer4.AccessTokenValidation 是两个中间件的包装器。中间件JwtBearerAuthentication
和OAuth2IntrospectionAuthentication
中间件。这两者都通过 http 获取发现文档以用于令牌验证。如果您想进行内存中的独立测试,这是一个问题。
如果您想解决这个问题,您可能需要制作一个假版本,app.UseIdentityServerAuthentication
该版本不会执行获取发现文档的外部调用。它仅填充 HttpContext 主体,以便可以测试您的 [Authorize] 策略。
在这里查看 IdentityServer4.AccessTokenValidation 的主要内容。接下来看看 JwtBearer Middleware 的外观
归档时间: |
|
查看次数: |
6750 次 |
最近记录: |