适用于 Angular 8 / Java 应用程序的 Keycloak 8:登录时出现错误“发生错误,请重新登录..”。所有在带有 Nginx、ssl、https、dns 的 vps 上进行 Docker 化

Joh*_*ent 6 ssl https docker keycloak angular

首先,我告诉你,我是一名初级开发人员。所以我请求你对我的问题宽容:)

我想做的是一个带有 Keycloak 的简单产品 POC,用于带有 Java 后端和 Postgres 数据库的 Angular 8 前端。我正在使用 OVH VPS,使用 Nginx / Certbot 进行 ssl 和 https 管理。

什么不起作用: - 访问私有 URL(受 keycloak 保护,在我的 Angular 应用程序中使用 [AppAuthGuard] :Keycloak 在登录页面上正确重定向,但登录不起作用。我从 Keycloak 收到错误:“我们抱歉...发生错误,请通过您的应用程序重新登录。”。

我的浏览器显示:“状态代码:400 错误请求”当我检查 Keycloak 容器日志时,出现警告:

WARN  [org.keycloak.events] (default task-1) type=LOGIN_ERROR, realmId=TheLibrary, clientId=null, userId=null, ipAddress=192.168.80.1, error=invalid_code
Run Code Online (Sandbox Code Playgroud)

话虽这么说,我的网址确实有领域和 clientId,它必须在...

什么工作正常: - 使用我在 VPS 上创建的子域进行 url 重定向, - 访问我的 Keycloak 管理控制台, - 访问我的前端的公共 url(我的意思是没有 [AppAuthGuard] 的 url) - 一切,如果我运行我的本地环境中的后端/前端/Keycloak 代码,无需 ssl 或 https。

四天以来我一直在为登录错误而苦苦挣扎。这让我发疯……我几乎没有任何线索可以调查。

我在网上没有找到类似的问题。

我注意到的唯一一件事是,我的本地 poc 工作正常(使用本地独立的 Keycloak)之间的区别是,在成功登录后似乎会加载一个 cookie,而当我在我的应用程序中收到错误时,它不会加载。 prod poc(所有内容都在我的 vps 上在线)。我可以在 Chrome > 网络 > 全部 > ... 中使用开发者模式看到它

这是我的代码和配置:

前端

应用程序路由.module.ts

import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {BooksComponent} from './shared/books/books.component';
import {AppAuthGuard} from './app-auth.guard';

const routes: Routes = [
  {
    path: 'books', component: BooksComponent
  },
  {
    path: 'search',
    loadChildren: () => import('./search/search.module').then(mod => mod.SearchModule),
    // canActivate: [AppAuthGuard]
  },
  {
    path: 'last-release',
    loadChildren: () => import('./last-release/last-release.module').then(mod => mod.LastReleaseModule),
    // canActivate: [AppAuthGuard]
  },
  {
    path: 'loan',
    loadChildren: () => import('./loan/loan.module').then(mod => mod.LoanModule),
    canActivate: [AppAuthGuard],
    data: { roles: ['user'] }
  },
  {
    path: '', redirectTo: '/books',
    pathMatch: 'full'
  },
  {
    path: '**',
    redirectTo: '/'
  }
];

@NgModule({
  declarations: [],
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
Run Code Online (Sandbox Code Playgroud)

应用程序验证.guard.ts

import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, Router} from '@angular/router';
import {KeycloakAuthGuard, KeycloakService} from 'keycloak-angular';

@Injectable({
  providedIn: 'root'
})
export class AppAuthGuard extends KeycloakAuthGuard {

  constructor(protected router: Router,
              protected keycloakAngular: KeycloakService) {
      super(router, keycloakAngular);
    }

  isAccessAllowed(route: ActivatedRouteSnapshot): Promise<boolean> {
    return new Promise(async (resolve, reject) => {
      const requiredRoles = route.data.roles;

      console.log('Class: AppAuthGuard, Function: , Line 19 (): '
      , route);

      if (route.url[0].path === '/books') {
        return resolve(true);
      } else if (!this.authenticated ) {
        this.keycloakAngular.login();
        return resolve(true);
      }
      if (!requiredRoles || requiredRoles.length === 0) {
        return resolve(true);
      } else {
        if (!this.roles || this.roles.length === 0) {
          resolve(false);
        }
        let granted = false;
        for (const requiredRole of requiredRoles) {
          if (this.roles.indexOf(requiredRole) > -1) {
            granted = true;
            break;
          }
        }
        resolve(granted);
      }
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

应用程序模块.ts

import {BrowserModule} from '@angular/platform-browser';
import {APP_INITIALIZER, ApplicationRef, DoBootstrap, NgModule} from '@angular/core';
import {AppComponent} from './app.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {AppRoutingModule} from './app-routing.module';
import {BooksComponent} from './shared/books/books.component';
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {ReactiveFormsModule} from '@angular/forms';
import {KeycloakAngularModule, KeycloakService} from 'keycloak-angular';
import {environment} from '../environments/environment';
import {HttpErrorInterceptor} from './shared/http-error.interceptor';
import {HeaderComponent} from './shared/header/header.component';
import {SideNavComponent} from './shared/side-nav/side-nav.component';
import {initializer} from './app.init';

const keycloakService = new KeycloakService();

@NgModule({
  declarations: [
    AppComponent,
    BooksComponent,
    HeaderComponent,
    SideNavComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    HttpClientModule,
    AppRoutingModule,
    ReactiveFormsModule,
    KeycloakAngularModule
  ],
  providers: [
    {
      provide: KeycloakService,
      useValue: keycloakService
    }
  ],
  entryComponents: [AppComponent]
})
export class AppModule implements DoBootstrap {
  ngDoBootstrap(appRef: ApplicationRef) {
    keycloakService
      .init({
        config: environment.keycloak,
        initOptions: {
          checkLoginIframe: false
        },
      })
      .then(() => {
        console.log('[ngDoBootstrap] bootstrap app');
        appRef.bootstrap(AppComponent);
      })
      .catch(error => console.error('[ngDoBootstrap] init Keycloak failed', error));
  }
}
Run Code Online (Sandbox Code Playgroud)

环境.产品.ts

export const baseUrls = {
  catalog: 'https://thelibrary.ms.catalog.mypoc.online/api'
};

export const environment = {
  production: true,
  authServiceApiUrl: 'https://auth.thelibrary.mypoc.online/auth',
  keycloak: {
    url: 'https://auth.thelibrary.mypoc.online/auth',
    realm: 'TheLibrary',
    clientId: 'thelibrary-app',
    'ssl-required': 'all',
    'public-client': true
  },
  baseUrl: {
    catalog: {
      getBooks: baseUrls.catalog + '/books'
    }
  }
};
Run Code Online (Sandbox Code Playgroud)

Dockerfile 前端

# base image
FROM node:latest

# install chrome for protractor tests
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
RUN apt-get update && apt-get install -yq google-chrome-stable

# set working directory
WORKDIR /app

# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH

# install and cache app dependencies
COPY build/front/package.json /app/package.json
RUN npm install
RUN npm install -g @angular/cli@8.3.24

# add app
COPY build/front/. /app

# start app
RUN pwd
RUN ls

CMD ng serve --disable-host-check --configuration=production --proxy-config proxy.conf.json --host 0.0.0.0 --public-host https://www.thelibrary.mypoc.online
Run Code Online (Sandbox Code Playgroud)

顺便问一下,我的“ngserve”行可以吗?

Docker-compose.yml


version: '3.7'

services:
    db-thelibrary:
        build:
            context: ./db
            dockerfile: Dockerfile-db
        container_name: cont-db-thelibrary
        restart: unless-stopped
        ports:
            - 5432:5432
        environment:
            POSTGRES_DB: ########
            POSTGRES_USER: ########
            POSTGRES_PASSWORD: ########
        volumes:
            - db_data:/var/lib/postgres/data
        networks:
            - network-thelibrary

    back-thelibrary:
        depends_on:
            - db-thelibrary
        build:
            context: ./build/back
            dockerfile: Dockerfile
        container_name: cont-back-thelibrary
        ports:
            - '127.0.0.1:8090:8090'
        environment:
            SPRING_PROFILES_ACTIVE: prod
            SPRING_DATASOURCE_USERNAME: ########
            SPRING_DATASOURCE_PASSWORD: ########
            SPRING_DATASOURCE_URL: jdbc:postgresql://db-thelibrary:5432/db_thelibrary?currentSchema=dev
        networks:
            - network-thelibrary

    front-thelibrary:
        build:
            context: build/front
            dockerfile: Dockerfile-front
        container_name: cont-front-thelibrary
        ports:
            - '127.0.0.1:4200:4200'
        networks:
            - network-thelibrary

networks:
    network-thelibrary:
        driver: bridge

volumes:
    front:
    db_data:
Run Code Online (Sandbox Code Playgroud)

钥匙斗篷

Docker-compose.yml


version: '3.7'

services:

    db-keycloak:
        image: postgres
        container_name: cont-db-keycloak
        restart: always
        volumes:
            - postgres_data:/var/lib/postgresql/data
        environment:
            POSTGRES_DB: #######
            POSTGRES_USER: ########
            POSTGRES_PASSWORD: #########
        networks:
           - network-keycloak

    keycloak:
        depends_on:
            - db-keycloak
        image: jboss/keycloak
        container_name: cont-keycloak
        environment:
            KEYCLOAK_HOSTNAME: www.auth.thelibrary.mypoc.online
            KEYCLOAK_ALWAYS_HTTPS: 'true'
            PROXY_ADDRESS_FORWARDING: 'true'
            KEYCLOAK_LOGLEVEL: DEBUG
            KEYCLOAK_USER: ######
            KEYCLOAK_PASSWORD: ##########
            DB_VENDOR: POSTGRES
            DB_ADDR: db-keycloak
            DB_PORT: 5432
            DB_DATABASE: keycloak
            DB_USER: keycloak
            DB_SCHEMA: public
            DB_PASSWORD: ###
            # Uncomment the line below if you want to specify JDBC parameters. The parameter below is just an example, and it shouldn't be used in production without knowledge. It is highly recommended that you read the PostgreSQL JDBC driver documentation in order to use it.
#            JDBC_PARAMS: "ssl=true"
        command:
            -Djboss.socket.binding.port-offset=001
        ports:
            - '127.0.0.1:8081:8081'
            - '127.0.0.1:8443:8443'
        volumes:
            - config:/config/
        networks:
            - network-keycloak

networks:
    network-keycloak:
        driver: bridge

volumes:
    config:
        driver: local
        driver_opts:
            type: 'none'
            o: 'bind'
            device: '/srv/keycloak/config'
    postgres_data:
        driver: local
        driver_opts:
            type: 'none'
            o: 'bind'
            device: '/srv/keycloak/postgres_data'
Run Code Online (Sandbox Code Playgroud)

Keycloak客户端设置:

- Realm : TheLibrary
- Client ID : thelibrary-app
- Root URL : https://www.thelibrary.mypoc.online
- Valid Redirect URIs : https://www.thelibrary.mypoc.online/*
- Base URL : https://www.thelibrary.mypoc.online
- Web Origins : https://www.thelibrary.mypoc.online
Run Code Online (Sandbox Code Playgroud)

当然,我正确创建了两个角色,管理员(复合角色,包括用户)和用户,链接到我的客户端。

NGINX

我的 VPS 运行 Debian 9 Stretch。我在上面安装了 Nginx 和 Certbot,并进行了非常基本的设置,以处理https://my.sub.url在https://ipAdress:port上的重定向。我按照此链接执行此操作: https: //certbot.eff.org/lets-encrypt/debianstretch-nginx 我在 /etc/nginx/sites-available/ 中创建了以下 .conf 文件

nginx.conf


version: '3.7'

services:
    db-thelibrary:
        build:
            context: ./db
            dockerfile: Dockerfile-db
        container_name: cont-db-thelibrary
        restart: unless-stopped
        ports:
            - 5432:5432
        environment:
            POSTGRES_DB: ########
            POSTGRES_USER: ########
            POSTGRES_PASSWORD: ########
        volumes:
            - db_data:/var/lib/postgres/data
        networks:
            - network-thelibrary

    back-thelibrary:
        depends_on:
            - db-thelibrary
        build:
            context: ./build/back
            dockerfile: Dockerfile
        container_name: cont-back-thelibrary
        ports:
            - '127.0.0.1:8090:8090'
        environment:
            SPRING_PROFILES_ACTIVE: prod
            SPRING_DATASOURCE_USERNAME: ########
            SPRING_DATASOURCE_PASSWORD: ########
            SPRING_DATASOURCE_URL: jdbc:postgresql://db-thelibrary:5432/db_thelibrary?currentSchema=dev
        networks:
            - network-thelibrary

    front-thelibrary:
        build:
            context: build/front
            dockerfile: Dockerfile-front
        container_name: cont-front-thelibrary
        ports:
            - '127.0.0.1:4200:4200'
        networks:
            - network-thelibrary

networks:
    network-thelibrary:
        driver: bridge

volumes:
    front:
    db_data:
Run Code Online (Sandbox Code Playgroud)

库文件


version: '3.7'

services:

    db-keycloak:
        image: postgres
        container_name: cont-db-keycloak
        restart: always
        volumes:
            - postgres_data:/var/lib/postgresql/data
        environment:
            POSTGRES_DB: #######
            POSTGRES_USER: ########
            POSTGRES_PASSWORD: #########
        networks:
           - network-keycloak

    keycloak:
        depends_on:
            - db-keycloak
        image: jboss/keycloak
        container_name: cont-keycloak
        environment:
            KEYCLOAK_HOSTNAME: www.auth.thelibrary.mypoc.online
            KEYCLOAK_ALWAYS_HTTPS: 'true'
            PROXY_ADDRESS_FORWARDING: 'true'
            KEYCLOAK_LOGLEVEL: DEBUG
            KEYCLOAK_USER: ######
            KEYCLOAK_PASSWORD: ##########
            DB_VENDOR: POSTGRES
            DB_ADDR: db-keycloak
            DB_PORT: 5432
            DB_DATABASE: keycloak
            DB_USER: keycloak
            DB_SCHEMA: public
            DB_PASSWORD: ###
            # Uncomment the line below if you want to specify JDBC parameters. The parameter below is just an example, and it shouldn't be used in production without knowledge. It is highly recommended that you read the PostgreSQL JDBC driver documentation in order to use it.
#            JDBC_PARAMS: "ssl=true"
        command:
            -Djboss.socket.binding.port-offset=001
        ports:
            - '127.0.0.1:8081:8081'
            - '127.0.0.1:8443:8443'
        volumes:
            - config:/config/
        networks:
            - network-keycloak

networks:
    network-keycloak:
        driver: bridge

volumes:
    config:
        driver: local
        driver_opts:
            type: 'none'
            o: 'bind'
            device: '/srv/keycloak/config'
    postgres_data:
        driver: local
        driver_opts:
            type: 'none'
            o: 'bind'
            device: '/srv/keycloak/postgres_data'
Run Code Online (Sandbox Code Playgroud)

keycloak.conf

- Realm : TheLibrary
- Client ID : thelibrary-app
- Root URL : https://www.thelibrary.mypoc.online
- Valid Redirect URIs : https://www.thelibrary.mypoc.online/*
- Base URL : https://www.thelibrary.mypoc.online
- Web Origins : https://www.thelibrary.mypoc.online
Run Code Online (Sandbox Code Playgroud)

我的每个组件都使用 Docker compose 运行。

有关更多详细信息,这里是我的 github: https: //github.com/TheLibraryGroup。它包含我的大部分配置文件。

如果有人能帮助我,那就太好了。我很确定这是一件很小的事情,但我无法弄清楚,因为我对 devops 和网络主题非常陌生。

非常感谢您抽出时间!