如何在docker机器内外使用IdentityServer4?

问题描述 投票:20回答:2

我希望能够从docker机器外部和内部对Identity Server(STS)进行身份验证。

我在设置容器内部和外部的正确权限时遇到问题。如果我将权限设置为内部名称mcoidentityserver:5000,那么API可以进行身份​​验证,但客户端无法获取令牌,因为客户端位于docker网络之外。如果我将权限设置为外部名称localhost:5000,则客户端可以获取令牌,但API无法识别权限名称(因为在这种情况下localhost是主机)。

我该如何设置权限?或许我需要调整码头网络?

红色箭头是我遇到麻烦的部分。 Three docker containers in a network, a client and PostgreSQL Admin, their ports and a red arrow showing where I think the problem lies.

详情

我正在设置一个Windows 10 docker开发环境,它使用ASP.NET Core API(在Linux上),Identity Server 4(Linux上的ASP.NET Core)和PostgreSQL数据库。 PostgreSQL不是问题,包含在图表中以保证完整性。它被映射到9876,因为我现在还在主机上运行了一个PostgreSQL实例。 mco是我们公司的简称。

我一直在关注Identity Server 4 instructions起床和跑步。

我不包括docker-compose.debug.yml,因为它运行的命令仅适用于在Visual Studio中运行。

泊坞窗,compose.yml

version: '2'

services:
mcodatabase:
    image: mcodatabase
    build:
    context: ./Data
    dockerfile: Dockerfile
    restart: always
    ports:
    - 9876:5432
    environment:
    POSTGRES_USER: mcodevuser
    POSTGRES_PASSWORD: password
    POSTGRES_DB: mcodev
    volumes:
    - postgresdata:/var/lib/postgresql/data
    networks:
    - mconetwork

mcoidentityserver:
    image: mcoidentityserver
    build:
    context: ./Mco.IdentityServer
    dockerfile: Dockerfile
    ports:
    - 5000:5000
    networks:
    - mconetwork

mcoapi:
    image: mcoapi
    build:
    context: ./Mco.Api
    dockerfile: Dockerfile
    ports:
    - 56107:80
    links:
    - mcodatabase
    depends_on:
    - "mcodatabase"
    - "mcoidentityserver"
    networks:
    - mconetwork

volumes:
postgresdata:

networks:
mconetwork:
    driver: bridge

泊坞窗,compose.override.yml

这是由Visual Studio插件创建的,用于注入额外的值。

version: '2'

services:
mcoapi:
    environment:
    - ASPNETCORE_ENVIRONMENT=Development
    ports:
    - "80" 

mcoidentityserver:
    environment:
    - ASPNETCORE_ENVIRONMENT=Development
    ports:
    - "5000" 

API Dockerfile

FROM microsoft/aspnetcore:1.1
ARG source
WORKDIR /app
EXPOSE 80
COPY ${source:-obj/Docker/publish} .
ENTRYPOINT ["dotnet", "Mco.Api.dll"]

Identity Server Dockerfile

FROM microsoft/aspnetcore:1.1
ARG source
WORKDIR /app
COPY ${source:-obj/Docker/publish} .
EXPOSE 5000
ENV ASPNETCORE_URLS http://*:5000
ENTRYPOINT ["dotnet", "Mco.IdentityServer.dll"]

API Startup.cs

我们告诉API使用Identity Server并设置权限。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
    {
        // This can't work because we're running in docker and it doesn't understand what localhost:5000 is!
        Authority = "http://localhost:5000", 
        RequireHttpsMetadata = false,

        ApiName = "api1"
    });

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Identity Server Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddIdentityServer()
            .AddTemporarySigningCredential()
            .AddInMemoryApiResources(Config.GetApiResources())
            .AddInMemoryClients(Config.GetClients());
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseIdentityServer();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    }
}

Identity Server Config.cs

public class Config
{
    public static IEnumerable<ApiResource> GetApiResources()
    {
        return new List<ApiResource>
        {
            new ApiResource("api1", "My API")
        };
    }

    public static IEnumerable<Client> GetClients()
    {
        return new List<Client>
        {
            new Client
            {
                ClientId = "client",

                // no interactive user, use the clientid/secret for authentication
                AllowedGrantTypes = GrantTypes.ClientCredentials,

                // secret for authentication
                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },

                // scopes that client has access to
                AllowedScopes = { "api1" }
            }
        };
    }
}

客户

在控制台应用程序中运行。

var discovery = DiscoveryClient.GetAsync("localhost:5000").Result;
var tokenClient = new TokenClient(discovery.TokenEndpoint, "client", "secret");
var tokenResponse = tokenClient.RequestClientCredentialsAsync("api1").Result;

if (tokenResponse.IsError)
{
    Console.WriteLine(tokenResponse.Error);
    return 1;
}

var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);

var response = client.GetAsync("http://localhost:56107/test").Result;
if (!response.IsSuccessStatusCode)
{
    Console.WriteLine(response.StatusCode);
}
else
{
    var content = response.Content.ReadAsStringAsync().Result;
    Console.WriteLine(JArray.Parse(content));
}

提前致谢。

docker docker-compose identityserver4
2个回答
10
投票

确保IssuerUri设置为显式常量。我们遇到类似的问题,通过IP /主机名访问Identity Server实例并以这种方式解决它:

services.AddIdentityServer(x =>
{
    x.IssuerUri = "my_auth";
})

附:为什么不将权限URL统一到hostname:5000?是的,如果符合以下条件,则客户端和API都可以调用相同的URL hostname:5000

  • 5000端口暴露(我看到它没关系)
  • DNS在docker容器内解析。
  • 您可以访问hostname:5000(检查防火墙,网络拓扑等)

DNS是最棘手的部分。如果您遇到任何问题,我建议您尝试通过其暴露的IP而不是解析hostname来访问Identity Server。


6
投票

为了完成这项工作,我需要在docker-compose.yml中传入两个环境变量,并在身份服务器实例上设置CORS,以便允许API调用它。建立CORS不在这个问题的范围之内; this question很好地涵盖了它。

Docker-Compose更改

身份服务器需要IDENTITY_ISSUER,这是身份服务器将给自己的名称。在这种情况下,我使用了docker主机的IP和身份服务器的端口。

  mcoidentityserver:
    image: mcoidentityserver
    build:
      context: ./Mco.IdentityServer
      dockerfile: Dockerfile
    environment:
      IDENTITY_ISSUER: "http://10.0.75.1:5000"
    ports:
       - 5000:5000
    networks:
     - mconetwork

API需要知道权限的位置。我们可以使用docker网络名称作为权限,因为调用不需要在docker网络之外,API只调用身份服务器来检查令牌。

  mcoapi:
    image: mcoapi
    build:
      context: ./Mco.Api
      dockerfile: Dockerfile
    environment:
      IDENTITY_AUTHORITY: "http://mcoidentityserver:5000"
    ports:
       - 56107:80
    links:
     - mcodatabase
     - mcoidentityserver
    depends_on:
     - "mcodatabase"
     - "mcoidentityserver"
    networks:
     - mconetwork

在C#中使用这些值

Identity Server.cs

您在ConfigureServices中设置了Identity Issuer名称:

    public void ConfigureServices(IServiceCollection services)
    {
        var sqlConnectionString = Configuration.GetConnectionString("DefaultConnection");

        services
            .AddSingleton(Configuration)
            .AddMcoCore(sqlConnectionString)
            .AddIdentityServer(x => x.IssuerUri = Configuration["IDENTITY_ISSUER"])
            .AddTemporarySigningCredential()
            .AddInMemoryApiResources(Config.GetApiResources())
            .AddInMemoryClients(Config.GetClients())
            .AddCorsPolicyService<InMemoryCorsPolicyService>()
            .AddAspNetIdentity<User>();
    }

API Startup.cs

我们现在可以将Authority设置为环境变量。

app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
    {
        Authority = Configuration["IDENTITY_AUTHORITY"],
        RequireHttpsMetadata = false,
        ApiName = "api1"
    });

缺点

如此处所示,docker-compose不适合生产,因为硬编码身份发布者是本地IP。相反,您需要一个正确的DNS条目,该条目将映射到docker实例,并在其中运行标识服务器。为此,我将创建一个docker-compose覆盖文件,并使用重写值构建生产。

感谢ilya-chumakov的帮助。

编辑

除此之外,我已经在我的博客上写了构建Linux docker + ASP.NET Core 2 + OAuth with Identity Server的整个过程。

© www.soinside.com 2019 - 2024. All rights reserved.