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

Dr *_*ang 22 docker docker-compose identityserver4

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

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

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

红色箭头是我遇到麻烦的部分. 网络中的三个docker容器,一个客户端和PostgreSQL Admin,它们的端口和一个红色箭头显示我认为问题所在.

详情

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

我一直在遵循Identity Server 4的说明来启动和运行.

我不包括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
Run Code Online (Sandbox Code Playgroud)

泊坞窗,compose.override.yml

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

version: '2'

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

mcoidentityserver:
    environment:
    - ASPNETCORE_ENVIRONMENT=Development
    ports:
    - "5000" 
Run Code Online (Sandbox Code Playgroud)

API Dockerfile

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

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"]
Run Code Online (Sandbox Code Playgroud)

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?}");
    });
}
Run Code Online (Sandbox Code Playgroud)

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!");
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

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" }
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

客户

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

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));
}
Run Code Online (Sandbox Code Playgroud)

提前致谢.

Ily*_*kov 12

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

services.AddIdentityServer(x =>
{
    x.IssuerUri = "my_auth";
})
Run Code Online (Sandbox Code Playgroud)

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

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

DNS是最棘手的部分,所以如果你发现它有任何问题,我建议先从机器外部IP开始,而不是hostname.


Dr *_*ang 6

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

Docker-Compose更改

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

  mcoidentityserver:
    image: mcoidentityserver
    build:
      context: ./Mco.IdentityServer
      dockerfile: Dockerfile
    environment:
      IDENTITY_ISSUER: "http://10.0.75.1:5000"
    ports:
       - 5000:5000
    networks:
     - mconetwork
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

在C#中使用这些值

Identity Server.cs

您将身份颁发者名称设置为ConfigureServices:

    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>();
    }
Run Code Online (Sandbox Code Playgroud)

API Startup.cs

我们现在可以将权限设置为环境变量.

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

缺点

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

感谢ilya-chumakov的帮助.

编辑

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

  • “我们可以使用 docker 网络名称作为权限,因为调用不需要到 docker 网络之外,API 只是调用身份服务器来检查令牌。” 授权码流程怎么样?我的浏览器不知道您设置的“docker 网络名称”是什么。:( (3认同)