Keycloak 8 для Angular 8 / Java app: получить ошибку «Произошла ошибка, пожалуйста, войдите снова ..» при входе в систему. Все Dockerized на vps с Nginx, ssl, https, днс - PullRequest
2 голосов
/ 10 февраля 2020

Во-первых, я сообщаю вам, что я младший разработчик. Поэтому я прошу вашей терпимости в отношении моего вопроса:)

Я пытаюсь сделать простой продукт ПО C с Keycloak для интерфейса Angular 8 с Java бэкэндом и Postgres дБ Я использую OVH VPS с Nginx / Certbot для управления ssl и https.

Что не работает: - доступ к частному URL (защищен с помощью keycloak, используя [AppAuthGuard] в моем приложении Angular: 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

При этом у моих URL-адресов действительно есть realm и clientId, где он должен быть ...

Что работает нормально: - перенаправление URL-адресов с использованием субдомена, созданного на моем VPS, - доступ к моему Консоль администратора Keycloak, - доступ к публичному c URL моего Frontend (я имею в виду URL без [AppAuthGuard]) - все, если я запускаю свой код backend / frontend / Keycloak в локальной среде, без ssl или https.

Я борюсь за этот логин ошибка с 4 дня. Это сводит меня с ума ... и у меня нет практически никаких улик для расследования.

Я не нашел подобной проблемы в Интернете.

Единственное, что я заметил, как разница между моими местными po c, который работает нормально (с локальным автономным Keycloak), является поваром ie, который, кажется, загружается после успешного входа в систему, и не загружается, когда я получаю ошибку в моем prod po c ( со всем онлайн на моем VPS). Я вижу это в режиме разработчика в Chrome> Сеть> все> ...

Вот мой код и конфигурация:

FRONTEND

app-routing.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 { }

app-auth.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);
      }
    });
  }
}

app.module.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));
  }
}

environment.prod.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'
    }
  }
};

Dockerfile-front

# 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

Кстати, с моей линией "ng serve" все в порядке?

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:

KEYCLOAK

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'

Настройки клиента 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

Конечно я правильно создал две роли, admin (составная роль) , включая пользователя) и пользователя, связанные с моим клиентом.

NGINX

Мой VPS работает с Debian 9 Stretch. Я установил Nginx и Certbot на нем, с очень базовыми настройками c, чтобы справиться с тем, что https://my.sub.url хорошо перенаправлены на https://ipAdress: порт . Я перешел по этой ссылке, чтобы сделать это: https://certbot.eff.org/lets-encrypt/debianstretch-nginx Я создал следующие файлы .conf в / etc / nginx / sites-available /

nginx .conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 768;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";

    ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

thelibrary.conf


server {

    server_name www.thelibrary.mypoc.online thelibrary.mypoc.online;

    location / {
      proxy_pass http://127.0.0.1:4200;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection 'upgrade';
      proxy_set_header Host $host;
      proxy_http_version 1.1;
      add_header Set-Cookie cip=$remote_addr;
      add_header Set-Cookie chost=$Host;
    }


    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/thelibrary.mypoc.online/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/thelibrary.mypoc.online/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = www.thelibrary.mypoc.online) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = thelibrary.mypoc.online) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;

    server_name www.thelibrary.mypoc.online thelibrary.mypoc.online;
    return 404; # managed by Certbot
}

keycloak.conf

server {
    server_name www.auth.thelibrary.mypoc.online auth.thelibrary.mypoc.online;

    location / {
      proxy_pass http://127.0.0.1:8081;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection 'upgrade';
      proxy_set_header Host $host;
      proxy_http_version 1.1;
      add_header Set-Cookie cip=$remote_addr;
      add_header Set-Cookie chost=$Host;
    }

    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/thelibrary.mypoc.online/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/thelibrary.mypoc.online/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = www.auth.thelibrary.mypoc.online) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = auth.thelibrary.mypoc.online) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;

    server_name www.auth.thelibrary.mypoc.online auth.thelibrary.mypoc.online;
    return 404; # managed by Certbot
}

Каждый из моих компонентов работает с Docker compose.

Для получения более подробной информации, вот мой github: https://github.com/TheLibraryGroup. Он содержит большинство моих конфигурационных файлов.

Было бы очень хорошо, если бы кто-то мог мне помочь. Я почти уверен, что это очень маленькая вещь, но я не могу понять это, так как я действительно новичок в devops и сетях. Topi c.

Большое спасибо за ваше время!

...