首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为8月使用AspNetCore 4的IdentityServer API构建集成测试

为8月使用AspNetCore 4的IdentityServer API构建集成测试
EN

Stack Overflow用户
提问于 2019-04-09 10:41:28
回答 3查看 2.1K关注 0票数 2

我构建了一个简单的AspNetCore 2.2 API,它使用IdentityServer 4来处理OAuth。它运行得很好,但是我现在想添加集成测试和最近发现的。我使用它构建了一些测试,这些测试都很好--只要我的控制器上没有[Authorize]属性--但是很明显,这个属性需要存在。

我偶然发现了这个堆叠溢出问题,在给出的答案中,我尝试将一个测试放在一起,但是当我尝试运行测试时,我仍然得到了一个Unauthorized响应。

请注意:我真的不知道在创建客户机时应该使用哪些细节。

  • 允许的范围应该是什么?(它们是否与真正的作用域匹配)

也是在构建IdentityServerWebHostBuilder

  • 我应该把什么传递给.AddApiResources?(也许是个愚蠢的问题,但这有关系吗?)

如果有人能指导我,我们将不胜感激。

这是我的测试:

代码语言:javascript
运行
复制
[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);

}

我的创业课:

代码语言:javascript
运行
复制
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();
    }
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-04-09 17:18:44

编辑:

以下建议是一个问题。由于试图构建WebHostBuilder 两次,原始源代码因异常而失败。其次,配置文件只存在于API项目中,而不在测试项目中,这就是为什么没有设置权限的原因。

而不是这样做

代码语言:javascript
运行
复制
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
   .AddJwtBearer(options =>
   {
       options.Authority = Configuration.GetValue<string>("IdentityServerAuthority");
       options.Audience = Configuration.GetValue<string>("IdentityServerAudience");
       options.BackchannelHttpHandler = BackChannelHandler;
   });

你必须这样做:

代码语言:javascript
运行
复制
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
   .AddIdentityServerAuthentication(options =>
   {
      options.Authority = Configuration.GetValue<string>("IdentityServerAuthority");
      options.JwtBackChannelHandler = BackChannelHandler;
    });

您可以找到一个样本这里

希望能帮上忙,为我工作过!

票数 4
EN

Stack Overflow用户

发布于 2019-10-29 22:50:54

不影响生产代码的解决方案:

代码语言:javascript
运行
复制
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);
                });
            });
    }
}

它的用途是:

代码语言:javascript
运行
复制
 _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

票数 1
EN

Stack Overflow用户

发布于 2019-10-01 19:09:54

如果您不希望依赖静态变量来保存HttpHandler,那么我发现下面的内容是可行的。我觉得它更干净了。

首先,创建一个可以在创建TestHost之前实例化的对象。这是因为在创建HttpHandler之前不会有TestHost,所以需要使用包装器。

代码语言:javascript
运行
复制
    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; }
    }

然后

代码语言:javascript
运行
复制
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;
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55590928

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档