You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker部署IdentityServer4报IDX20803连接拒绝配置获取失败错误

问题背景
  • 正在学习IdentityServer4,此前已开发完成包含API、客户端、IdentityServer4、API网关的简易测试项目,近期尝试将项目迁移至Docker容器环境部署。
  • 预期流程:客户端应用启动后自动跳转至IdentityServer登录页面
  • 实际现象:运行时抛出连接拒绝异常,无法完成跳转
错误信息
SocketException: Connection refused
System.Net.Sockets.Socket+AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)

HttpRequestException: Connection refused (identityserver4:9001)
System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(string host, int port, HttpRequestMessage initialRequest, bool async, CancellationToken cancellationToken)

IOException: IDX20804: Unable to retrieve document from: 'http://identityserver4:9001/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(string address, CancellationToken cancel)

InvalidOperationException: IDX20803: Unable to obtain configuration from: 'http://identityserver4:9001/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.GetConfigurationAsync(CancellationToken cancel)
现有核心配置

1. IdentityServer中Config.cs的客户端配置

new Client
{
     ClientId = "razorClient",
     ClientName = "RAZOR Client App",
     AllowedGrantTypes= GrantTypes.Hybrid,
     RequirePkce = false,
     AllowRememberConsent = false,
     RedirectUris = new List<string>()
     {
         "http://clientapp:5001/signin-oidc"
     },
     PostLogoutRedirectUris = new List<string>()
     {
         "http://clientapp:5001/signout-callback-oidc"
     },
     ClientSecrets = new List<Secret>
     {
        new Secret("secret".Sha256())
     },
     AllowedScopes = new List<string>()
     {
         IdentityServerConstants.StandardScopes.OpenId,
         IdentityServerConstants.StandardScopes.Profile,
         "MYAPI"

     },
     AllowAccessTokensViaBrowser = true
     
}

2. IdentityServer服务Program.cs配置

using IdentityServer;

var builder = WebApplication.CreateBuilder(args);

Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;

builder.Services.AddControllersWithViews();

builder.Services.AddIdentityServer()
    .AddInMemoryClients(Config.Clients)
    .AddInMemoryIdentityResources(Config.IdentityResources)
    //.AddInMemoryApiResources(Config.ApiResources)
    .AddInMemoryApiScopes(Config.ApiScopes)
    .AddTestUsers(Config.TestUsers)
    .AddDeveloperSigningCredential();


var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseHttpsRedirection();
}

app.UseStaticFiles();
app.UseRouting();

app.UseIdentityServer();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapDefaultControllerRoute();
});

app.Run();

3. 客户端应用Program.cs配置

using ClientApp.Data;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Mvc.Authorization;

var builder = WebApplication.CreateBuilder(args);

Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;

// 向容器添加服务
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>(); 

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    options.Authority = "http://identityserver4:9001";
    options.MetadataAddress = "http://identityserver4:9001/.well-known/openid-configuration";

    //options.Authority = "http://localhost:9001";
    //options.MetadataAddress = "http://localhost:9001/.well-known/openid-configuration";

    options.RequireHttpsMetadata = false;

    options.ClientId = "razorClient";
    options.ClientSecret = "secret";
    options.ResponseType = "code id_token";

    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("MYAPI");

    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;
});

builder.Services.AddMvcCore(options =>
{
    var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
});

var app = builder.Build();

// 配置HTTP请求管道
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHttpsRedirection();
    // 默认HSTS值为30天,生产场景可根据需求调整
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

4. docker-compose.override配置

version: '3.4'

services:
  identityserver4:
    container_name: identityserver4
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
    ports:
      - "9001:80"

  clientapp:
    container_name: clientapp
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
    ports:
      - "5001:80"
    depends_on:
      - identityserver4

  apiresource:
    container_name: apiresource
    environment:
      - ASPNETCORE_ENVIRONMENT=Development 
    ports:
      - "8001:80"
问题原因

核心是Docker网络端口映射规则混淆+内外网访问地址不统一:

  • docker-compose中ports字段配置格式为[宿主机端口]:[容器内部监听端口],当前配置9001:80代表仅宿主机的9001端口会映射到identityserver4容器的80端口。容器之间通过Docker内部网桥通信时,直接通过服务名访问,走容器内部监听端口,不会经过宿主机的端口映射。当前客户端容器内配置的IdentityServer地址是http://identityserver4:9001,但identityserver4容器内部实际只监听80端口,9001端口无服务监听,直接触发连接拒绝错误,这是当前报错的直接原因。
  • 即使修复容器内访问的端口问题,默认配置下还会遇到浏览器无法解析容器服务名、回调地址不匹配的问题:用户通过宿主机浏览器访问服务时,浏览器无法识别identityserver4、clientapp这类Docker内部服务域名,同时IdentityServer返回的跳转地址、元数据签发者默认使用容器内地址,会导致跳转失败、令牌验证不通过。
修复方案

两种方案二选一即可,推荐开发环境使用方案一,配置成本最低。

方案一:统一端口配置(开发环境推荐)

通过统一容器内监听端口和宿主机映射端口,配合本地hosts配置,实现容器内、宿主机访问地址完全一致,不需要额外编写跳转逻辑:

  1. 修改docker-compose.override.yml,将三个容器的内部监听端口和宿主机映射端口设为一致,通过ASPNETCORE_URLS指定容器内监听端口:
version: '3.4'

services:
  identityserver4:
    container_name: identityserver4
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://+:9001
    ports:
      - "9001:9001"

  clientapp:
    container_name: clientapp
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://+:5001
    ports:
      - "5001:5001"
    depends_on:
      - identityserver4

  apiresource:
    container_name: apiresource
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://+:8001
    ports:
      - "8001:8001"
  1. 修改宿主机hosts文件(Windows路径为C:\Windows\System32\drivers\etc\hosts,Linux/macOS路径为/etc/hosts),添加以下条目,让宿主机可以将Docker服务名解析到本地:
127.0.0.1 identityserver4
127.0.0.1 clientapp
127.0.0.1 apiresource
  1. 现有代码中的客户端OIDC配置、IdentityServer客户端回调地址不需要修改,重新构建镜像启动容器,直接在浏览器访问http://clientapp:5001即可正常跳转登录。

方案二:分离内外网访问地址(无需修改hosts)

如果不想修改本地hosts,可以将后端服务间通信地址和前端浏览器访问地址分开配置:

  1. 保持原有docker-compose的端口映射不变,给IdentityServer配置固定的签发者地址(即浏览器能访问的宿主机地址),修改IdentityServer的Program.cs,添加IssuerUri配置:
builder.Services.AddIdentityServer(options =>
{
    options.IssuerUri = "http://localhost:9001";
})
.AddInMemoryClients(Config.Clients)
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddTestUsers(Config.TestUsers)
.AddDeveloperSigningCredential();
  1. 修改客户端Program.cs的OIDC配置,将后端访问IdentityServer的地址改为容器内可访问的80端口,同时添加跳转事件,将给浏览器的跳转地址改为宿主机可访问的9001端口:
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    // 后端服务间通信走容器内网络,使用内部80端口
    options.Authority = "http://identityserver4";
    options.MetadataAddress = "http://identityserver4/.well-known/openid-configuration";
    options.RequireHttpsMetadata = false;

    options.ClientId = "razorClient";
    options.ClientSecret = "secret";
    options.ResponseType = "code id_token";

    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("MYAPI");

    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;

    // 修改跳转逻辑,给浏览器返回宿主机可访问的IdentityServer地址
    options.Events.OnRedirectToIdentityProvider = context =>
    {
        context.ProtocolMessage.IssuerAddress = context.ProtocolMessage.IssuerAddress.Replace("http://identityserver4", "http://localhost:9001");
        return Task.CompletedTask;
    };
});
  1. 修改IdentityServer项目Config.cs中的客户端回调、登出回调地址为浏览器可访问的宿主机地址:
RedirectUris = new List<string>()
{
    "http://localhost:5001/signin-oidc"
},
PostLogoutRedirectUris = new List<string>()
{
    "http://localhost:5001/signout-callback-oidc"
},
  1. 重新构建镜像启动容器,在浏览器访问http://localhost:5001即可正常跳转登录。

内容的提问来源于stack exchange,提问作者Rouzbeh Zarandi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 15:48:15