Docker(Spring Boot 或 Thorntail)和 Keycloak

Posted

技术标签:

【中文标题】Docker(Spring Boot 或 Thorntail)和 Keycloak【英文标题】:Docker (Spring Boot or Thorntail) and Keycloak 【发布时间】:2019-01-23 10:12:42 【问题描述】:

我在 docker 容器中运行 Spring Boot 和 Keycloak 时遇到问题。

我从 Keycloak 开始,使用 mysql 作为 db 在 docker 中运行。

services:
  mysql:
    image: mysql:5.7
    container_name: mysql
    volumes:
      - mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: keycloak
      MYSQL_USER: keycloak
      MYSQL_PASSWORD: password
    networks:
      - testNetwork

  keycloak:
    image: jboss/keycloak
    container_name: keycloak
    restart: on-failure
    volumes:
      - ./config:/config/
    environment:
      DB_VENDOR: MYSQL
      DB_ADDR: mysql
      DB_DATABASE: keycloak
      DB_USER: keycloak
      DB_PASSWORD: password
      KEYCLOAK_USER: xxx
      KEYCLOAK_PASSWORD: yyy
      KEYCLOAK_IMPORT_REALM: /keycloak/import/realm-import.json
    ports:
      - 8180:8080
    depends_on:
      - mysql
    networks:
      - testNetwork

然后我添加了我的领域 (SpringBootKeycloak)、我的客户端 (testclient) 和一个角色为“用户”的用户。 之后,我将 spring-security 添加到我的 Spring-boot-application 中。并编辑了我的 application.yml

spring:
  main:
    banner-mode: 'off'
  application:
    name: testclient
    version: @project.version@
  jpa:
    hibernate:
      ddl-auto: create
  datasource:
    url: jdbc:h2:mem:testclient;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: xxx
    password: xxx
keycloak:
  auth-server-url: http://localhost:8180/auth
  realm: SpringBootKeycloak
  resource: testclient
  public-client: true
  principal-attribute: preferred_username
  security-constraints:
    - authRoles:
      - user
      securityCollections:
        - patterns:
          - /*
server:
  port: $port:8090
  rest:
    path: testclient

据此,我添加了我的 SecurityConfig:

  /**
   * Secure appropriate endpoints
   */
  @Override
  protected void configure(HttpSecurity http) throws Exception 
    super.configure(http);
    http.authorizeRequests()
        .antMatchers("/*").hasRole("user") // only user with role user are allowed to access
        .anyRequest().permitAll();
  

在本地运行我的 SpringBoot-Application 工作正常。 我必须使用 keycloak 登录并重定向到 localhost:8090。 但是当我将 SpringBoot-Application 添加到我的 docker-compose 并在容器中启动它时,我仍然可以使用 keycloak 进行登录,但是当我应该重定向时,我会得到 403。

  testclient:
    image: testclient
    container_name: testclient
    environment:
      JAVA_OPTS: "-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n"
    build:
      context: testclient-application
    ports:
      - 8090:8090
      - 5006:5005
    networks:
      - testNetwork

带有以下容器日志:

"@timestamp":"2018-08-16T11:50:11.530+00:00","@version":"1","message":"failed to turn code into token","logger_name":"org.keycloak.adapters.OAuthRequestAuthenticator","thread_name":"http-nio-8090-exec-6","level":"ERROR","level_value":40000,"stack_trace":"java.net.ConnectException: Connection refused (Connection refused)\n\tat java.net.PlainSocketImpl.socketConnect(Native Method)\n\tat java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)\n\tat java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)\n\tat java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)\n\tat java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)\n\tat java.net.Socket.connect(Socket.java:589)\n\tat org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:121)\n\tat org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)\n\tat org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:144)\n\tat org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:134)\n\tat org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:610)\n\tat org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:445)\n\tat org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835)\n\tat org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)\n\tat org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)\n\tat org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)\n\tat org.keycloak.adapters.ServerRequest.invokeAccessCodeToToken(ServerRequest.java:111)\n\tat org.keycloak.adapters.OAuthRequestAuthenticator.resolveCode(OAuthRequestAuthenticator.java:336)\n\tat org.keycloak.adapters.OAuthRequestAuthenticator.authenticate(OAuthRequestAuthenticator.java:281)\n\tat org.keycloak.adapters.RequestAuthenticator.authenticate(RequestAuthenticator.java:139)\n\tat org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve.authenticateInternal(AbstractKeycloakAuthenticatorValve.java:203)\n\tat org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve.authenticate(KeycloakAuthenticatorValve.java:50)\n\tat org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve.doAuthenticate(KeycloakAuthenticatorValve.java:57)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:575)\n\tat org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve.invoke(AbstractKeycloakAuthenticatorValve.java:181)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.lang.Thread.run(Thread.java:748)\n","app":"testclient","version":"1.0.0-SNAPSHOT"

我不知道如何解决这个问题...

编辑 1: 更多信息:我在 Windows 上运行 docker。

编辑 2:解决方案

我的工作解决方案包含以下内容:

    步骤,将 keycloak 添加为主机

为了使事情正常进行,您需要确保将以下内容添加到您的主机文件中(Mac/Linux 上的 /etc/hosts,Windows 上的 c:\Windows\System32\Drivers\etc\hosts)。

127.0.0.1 密钥斗篷

这是因为您将使用机器上的浏览器(名称为 localhost 或 127.0.0.1)访问您的应用程序,但在 Docker 内部,它将在自己的容器中运行,名称为 keycloak。

    步骤

内部 Docker 端口和发布的端口需要相同:

services:
  mysql:
    image: mysql:5.7
    container_name: mysql
    volumes:
      - mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: keycloak
      MYSQL_USER: keycloak
      MYSQL_PASSWORD: password
    networks:
      - testNetwork

  keycloak:
    image: jboss/keycloak
    container_name: keycloak
    restart: on-failure
    volumes:
      - ./config:/config/
    environment:
      DB_VENDOR: MYSQL
      DB_ADDR: mysql
      DB_DATABASE: keycloak
      DB_USER: keycloak
      DB_PASSWORD: password
      KEYCLOAK_USER: xxx
      KEYCLOAK_PASSWORD: yyy
      KEYCLOAK_IMPORT_REALM: /keycloak/import/realm-import.json
    ports:
      - 8080:8080   <--- edited
    depends_on:
      - mysql
    networks:
      - testNetwork

第 3 步:在 application.yml 中为 Spring boot 编辑的 auth-server-url 定义 keycloak:

    keycloak:
  realm: SpringBootKeycloak
  auth-server-url: http://keycloak:8080/auth   <--- edited
  resource: testclient
  public-client: true
  security-constraints:
    - authRoles:
      - user
      securityCollections:
        - patterns:
          - /*
  ssl-required: external
  confidential-port: 0

这个解决方案带来的丑陋之处: 您不能将 Docker 端口映射到另一个端口以从 url 访问。 端口: - 8080:8080 我花了很多时间测试其他组合,结果访问 url 端口必须与内部 docker 端口(在我的情况下为 8080)相同。

编辑 4:

Thorntail 也是如此。

要更改 Keycloak 的端口,请添加...

environment:
  JAVA_OPTS: "-Djboss.socket.binding.port-offset=10 -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m
  -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true"

... 用于 docker-compose 中的 keycloak。 -Djboss.socket.binding.port-offset=10 设置默认端口(8080)+偏移量(10) 其余的是 keycloak 的默认值。 不要忘记编辑“ports”和“auth-server-url”

【问题讨论】:

【参考方案1】:

我认为你的问题是auth-server-url: http://localhost:8180/auth。当您的应用在 docker 容器中运行时,localhost 实际上具有不同的含义。

在容器内部,它必须是容器的名称,即keycloak。这有点尴尬,因为当您从主机连接到 keycloak 时,您想使用 localhost 但令牌颁发者 url 需要与请求令牌的 url 匹配(否则令牌被拒绝)所以你最终不得不将keycloak 放入您的 etc/hosts 文件中。

您在解决这个问题方面做得很好 - 我遇到了这个working with Activiti。你可以找到JHipster project dealing with it in the same way - 他们说:

为了使事情顺利进行,您需要确保将以下内容添加到您的主机文件中(/etc/hosts 在 Mac/Linux 上,c:\Windows\System32\Drivers\etc\hosts 在 Windows 上)。

127.0.0.1 keycloak

这是因为您将使用机器上的浏览器(名称为 localhost127.0.0.1)访问您的应用程序,但在 Docker 内部它将在自己的容器中运行,名称为 keycloak

【讨论】:

感谢您的快速回答。 jhipster.tech/docker-compose/#7 做到了我认为的伎俩。如前所述,我在 Windows 上的 c:\Windows\System32\Drivers\etc\hosts 中添加了一个主机('127.0.0.1 keycloak')。现在我遇到了下一个问题......我得到了一个无限的重定向循环。我的有效重定向 URI 是“localhost:8090*”...我仍在努力,但也许你会再这么快 ;-)【参考方案2】:

感谢您的提问和回答!我为此苦苦挣扎了很长一段时间,并且出现连接被拒绝错误。原因是我正在映射 8180:8080,而一旦我映射了 8080:8080,一切都开始工作了!再次感谢!

【讨论】:

以上是关于Docker(Spring Boot 或 Thorntail)和 Keycloak的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot教程 springboot程序构建一个docker镜像

Docker:无法连接 Spring Boot 和 MYSQL

Docker 中的 Consul 和 Spring Boot 服务 - 不取消注册

使用 Spring Boot 2 的 Docker 堆栈“配置”

Spring Boot Docker 应用程序

Docker 上的 Spring Boot Admin 看不到 Docker 上的其他应用程序