Во-первых, я сообщаю вам, что я младший разработчик. Поэтому я прошу вашей терпимости в отношении моего вопроса:)
Я пытаюсь сделать простой продукт ПО 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.
Большое спасибо за ваше время!