我是第一次尝试集成KeyCloak。这是我在我的 Angular webapp
app.module.ts
文件中为了初始化 KeyCloak.js 所写的内容:
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { RouteReuseStrategy } from '@angular/router';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CalendarModule, DateAdapter } from 'angular-calendar';
import { adapterFactory } from 'angular-calendar/date-adapters/date-fns';
import {NgxPaginationModule} from 'ngx-pagination';
import { NgxGoogleAnalyticsModule } from 'ngx-google-analytics';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { environment } from '../environments/environment.prod';
import { KeycloakService } from 'keycloak-angular';
import { LoginInfo } from '../main';
const initializeKeycloak = (keycloak: KeycloakService) => async () =>
keycloak.init({
config: {
url: (JSON.parse(localStorage.getItem('loginInfo')) as LoginInfo).baseUrl,
realm: (JSON.parse(localStorage.getItem('loginInfo')) as LoginInfo).realm,
clientId: (JSON.parse(localStorage.getItem('loginInfo')) as LoginInfo).client
},
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri:
window.location.origin + '/assets/silent-check-sso.html'
},
shouldAddToken: (request) => {
const { method, url } = request;
const isGetRequest = 'GET' === method.toUpperCase();
const acceptablePaths = ['/assets', '/clients/public'];
const isAcceptablePathMatch = acceptablePaths.some((path) => url.includes(path));
return !(isGetRequest && isAcceptablePathMatch);
}
});
@NgModule({
declarations: [AppComponent],
// eslint-disable-next-line max-len
imports: [FormsModule, BrowserModule, IonicModule.forRoot(), AppRoutingModule, HttpClientModule, BrowserAnimationsModule, CalendarModule.forRoot({ provide: DateAdapter, useFactory: adapterFactory }), NgxPaginationModule,
NgxGoogleAnalyticsModule.forRoot(environment.ga4MeasurementId)],
providers: [KeycloakService,
{
provide: APP_INITIALIZER,
useFactory: initializeKeycloak,
multi: true,
deps: [KeycloakService]
}, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
bootstrap: [AppComponent]
})
export class AppModule {}
这是引用的
/assets/silent-check-sso.html
文件:
<html>
<body>
<script>
parent.postMessage(location.href, location.origin);
</script>
</body>
</html>
然后我有
route-guard.service.ts
文件:
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, Router } from '@angular/router';
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
@Injectable({
providedIn: 'root'
})
export class RouteGuardService extends KeycloakAuthGuard implements CanActivate {
constructor(
protected readonly router: Router,
protected readonly keycloak: KeycloakService) {
super(router, keycloak);
}
public async isAccessAllowed(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
) {
// Force the user to log in if currently unauthenticated.
if (!this.authenticated) {
await this.keycloak.login({
redirectUri: window.location.origin + state.url
});
}
// Get the roles required from the route.
const requiredRoles = route.data.roles;
// Allow the user to proceed if no additional roles are required to access the route.
if (!(requiredRoles instanceof Array) || requiredRoles.length === 0) {
return true;
}
// Allow the user to proceed if all the required roles are present.
return requiredRoles.every((role) => this.roles.includes(role));
}
}
这是我的
app-routing.module.ts
文件:
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
import { RouteGuardService } from './guard/route-guard.service';
const routes: Routes = [
{
path: '',
redirectTo: 'prenotazioni',
pathMatch: 'full',
},
{
path: 'prenotazioni',
loadChildren: () => import('./prenotazioni/prenotazioni.module').then( m => m.PrenotazioniPageModule),
canActivate: [RouteGuardService]
},
{
path: 'agenda',
loadChildren: () => import('./agenda/agenda.module').then( m => m.AgendaPageModule),
canActivate: [RouteGuardService]
},
{
path: 'configuratore',
loadChildren: () => import('./configuratore/configuratore.module').then( m => m.ConfiguratorePageModule),
canActivate: [RouteGuardService]
},
{
path: 'login',
loadChildren: () => import('./login/login.module').then( m => m.LoginPageModule)
},
{
path: 'customer-sat-page',
loadChildren: () => import('./customer-sat-page/customer-sat-page.module').then( m => m.CustomerSatPagePageModule)
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
],
exports: [RouterModule]
})
export class AppRoutingModule {}
我几乎从在线教程中学习了所有内容。老实说,我 100% 都不懂……无论如何,当我尝试浏览我的 webapp 时,它正确地将我重定向到 KeyCloak。我可以登录,但在我被重定向回我的 webapp 后,我只得到一个空白页面。浏览器控制台日志显示:
Failed to load resource: the server responded with a status of 401 ()
失败的资源是
https://my.keycloack.domain/realms/myRealm/protocol/openid-connect/token
KC日志仅显示:
2023-03-03 18:39:00,770 WARN [org.keycloak.events] (executor-thread-62) type=CODE_TO_TOKEN_ERROR, realmId=myRealm, clientId=myClient, userId=null, ipAddress=x.y.z.t, error=invalid_client_credentials, grant_type=authorization_code
但我确定我输入了正确的密码。
我发现其他一些线程也有类似的问题(HTTP 401 错误),但它们都有针对其配置的不同解决方案。我找不到适合我的 Angular 15 + KeyCloak 20 设置的任何东西,也找不到在我的案例中尝试这些解决方案的方法。
你能帮我理解为什么 KC 不喜欢令牌请求吗?