我希望能够从docker机器外部和内部对Identity Server(STS)进行身份验证。
我在设置容器内部和外部的正确权限时遇到问题。如果我将权限设置为内部名称mcoidentityserver:5000
,则API可以进行身份验证,但客户端无法获取令牌,因为客户端位于docker网络之外。如果我将权限设置为外部名称localhost:5000
,则客户端可以获取令牌,但API无法识别权限名称(因为在这种情况下localhost
是主机)。
我应该将权限设置为什么?或许我需要调整docker网络?
我正在设置一个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));
}
提前致谢。
答案 0 :(得分:6)
为了完成这项工作,我需要在身份服务器实例上的docker-compose.yml
和setup CORS中传入两个环境变量,以便允许API调用它。建立CORS不在这个问题的范围之内; this question很好地涵盖了它。
身份服务器需要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
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>();
}
API Startup.cs
我们现在可以将权限设置为环境变量。
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的整个过程。
答案 1 :(得分:1)
如果您在同一网络中运行 docker 容器,您可以执行以下操作:
services.AddIdentityServer(x =>
{
x.IssuerUri = "http://<your_identity_container_name>";
})
这将设置您的身份服务器的 URI。因此,您的其他 Web API 服务可以使用此 URI 来访问您的身份服务器。
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.Authority = "http://<your_identity_container_name>";
o.Audience = "api1"; // APi Resource Name
o.RequireHttpsMetadata = false;
o.IncludeErrorDetails = true;
});