Tenho uma aplicação web Java com Tomcat e Keycloak para autenticação. Ambos os serviços (o aplicativo e o Keycloak) estão sendo executados em contêineres Docker separados, conectados por uma rede Docker Bridge
keycloak
o serviço é executadohttp://keycloak:8080/auth
dentro do Docker.- Meu aplicativo java com tomcat expondo portas internas e host em 8090
- O frontend é JS
No meu aplicativo o perfil foi configurado com keycloak.json como:
{
"realm": "${realm}",
"auth-server-url": "http://keycloak:8080/auth",
"ssl-required": "none",
"resource": "${client_id}",
"public-client": true,
"confidential-port": 0
}
Tive um problema ao tentar acessar http://localhost:8090/system
, o aplicativo redirecionou o navegador para esta URL do Keycloak:
http://keycloak:8080/auth/realms/tdoc-api/protocol/openid-connect/auth...
O que falhou no navegador ERR_CONNECTION_REFUSED
, pois entendo que esse serviço não pode ser resolvido externamente ao host.
Também tentei alterar keycloak.json para:
{
"realm": "${realm}",
"auth-server-url": "http://localhost:8012/auth",
"ssl-required": "none",
"resource": "${client_id}",
"public-client": true,
"confidential-port": 0
}
Mas meu aplicativo falhou ao tentar efetuar login, mostrando um erro interno do servidor 500:
[WARN ] 12:08:46.150 org.keycloak.adapters.KeycloakDeployment.resolveUrls() - Failed to load URLs from http://localhost:8012/auth/realms/${realm}/.well-known/openid-configuration
java.net.ConnectException: Connection refused (Connection refused)
Porque não pode resolver de dentro do contêiner meu serviço exposto localmente na porta 8012. Que eu acho que foi a resposta fornecida neste post: https://stackoverflow.com/a/74929659
Alguém sabe por que essa solução não funcionou para mim?
Consegui consertar configurando um ambiente em docker-compose.yml:
- KEYCLOAK_FRONTEND_URL=http://localhost:8012/auth
Mais contexto do meu ambiente docker:
version: '3'
services:
app-db:
image: mysql:5.7
ports:
- "3309:3306"
environment:
MYSQL_ROOT_PASSWORD: example
networks:
- my-network
my-app:
build: .
ports:
- "8090:8080"
- "8000:8000"
environment:
- JAVA_OPTS=...
volumes:
- ./app:/usr/local/tomcat/webapps/my-app
depends_on:
- app-db
networks:
- my-network
keycloak:
image: quay.io/keycloak/keycloak:16.1.1
ports:
- "8012:8080"
environment:
- DB_VENDOR=mysql
- DB_ADDR=keycloak-db
- DB_PORT=3306
- DB_USER=keycloak_user
- DB_PASSWORD=keycloak_pass
- DB_DATABASE=keycloak
- PROXY_ADDRESS_FORWARDING=true
- KEYCLOAK_FRONTEND_URL=http://localhost:8012/auth
volumes:
- ./themes:/opt/jboss/keycloak/themes
depends_on:
- keycloak-db
networks:
- my-network
keycloak-db:
image: mysql:5.7
ports:
- "3307:3306"
environment:
- MYSQL_ROOT_PASSWORD=keycloak_pass
- MYSQL_DATABASE=keycloak
- MYSQL_USER=keycloak_user
- MYSQL_PASSWORD=keycloak_pass
volumes:
- ./volumes/mysql:/var/lib/mysql
networks:
- my-network
networks:
my-network:
external: true
name: my-network
Por que
http://keycloak:8080/auth
não funcionou?Como você notou, isso funciona somente dentro do Docker, onde
keyclock
é resolvível pela rede interna do Docker. Mas quando o navegador é redirecionado para essa URL, ele tenta resolverhttp://keycloak:8080
do seu sistema operacional host, o que falha — porquekeycloak
não é um nome de host real na sua máquina host. É por isso que acaba comERR_CONNECTION_REFUSED
erro.Por que
http://localhost:8012/auth
não funcionou?Com isso
auth-server-url
, seu aplicativo (dentro domy-app
contêiner) tentou se conectar alocalhost:8012
, mas no mundo dos contêineres,localhost
significa "dentro deste contêiner", não da sua máquina host.Então ele tenta encontrar o Keycloak dentro do mesmo contêiner (onde ele não existe) e falha com
Connection refused
exceção.Por que
KEYCLOAK_FRONTEND_URL=http://localhost:8012/auth
funciona como esperado?Ao definir
KEYCLOAK_FRONTEND_URL
, você disse ao próprio Keycloak: "Ao gerar URLs (como o URI de redirecionamento), usehttp://localhost:8012/auth
."Isso funciona porque:
Seu aplicativo Java se comunica com o Keycloak usando a
http://keycloak:8080/auth
URL interna.O navegador é redirecionado para a URL correta do host:
http://localhost:8012/auth
.Isso resolve ambos os lados:
contêiner interno -> nome do host do contêiner
navegador -> serviço vinculado ao host local