Angular14+Keycloak+angular-oauth2-oidc授权码流未请求AccessToken
问题分析与解决方案
你遇到的核心问题是:授权码成功获取后,Angular应用未向Keycloak发送AccessToken请求,导致AuthGuard始终判定无有效令牌,持续重定向到登录页。以下是针对性的修复步骤:
1. 修正OAuth配置(auth.config.ts)
手动指定tokenEndpoint会覆盖从Discovery Document自动获取的端点,容易引发冲突,建议移除该配置;同时确保redirectUri与Keycloak客户端配置完全一致:
import { AuthConfig } from "angular-oauth2-oidc"; export const oauthConfig: AuthConfig = { issuer: 'http://localhost:8080/realms/personal-account-manager', redirectUri: `${window.location.origin}/user-home`, // 动态适配当前域名 clientId: 'personal-account-manager-pkce-client', scope: 'openid profile', responseType: 'code', showDebugInformation: true, // 移除手动设置的tokenEndpoint,由Discovery Document自动加载 };
2. 优化OAuth初始化流程(app.component.ts)
loadDiscoveryDocumentAndTryLogin是异步操作,需要等待其完成后再处理令牌逻辑,同时添加登录成功后的跳转逻辑:
import { Component, OnInit } from '@angular/core'; import { OAuthService } from 'angular-oauth2-oidc'; import { Router } from '@angular/router'; import { oauthConfig } from './auth.config'; import { BehaviorSubject } from 'rxjs'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrl: './app.component.css' }) export class AppComponent implements OnInit { title = 'XYZ'; private authInitialized = new BehaviorSubject<boolean>(false); authInitialized$ = this.authInitialized.asObservable(); constructor(private oauthService: OAuthService, private router: Router) { this.configureAuthService(); } ngOnInit(): void {} configureAuthService() { console.log('configuring oauth2'); this.oauthService.configure(oauthConfig); // 先加载Discovery Document,再尝试登录 this.oauthService.loadDiscoveryDocument().then(() => { this.oauthService.tryLogin({ onTokenReceived: () => { // 登录成功后跳转到目标页面 this.router.navigate(['/user-home']); } }).finally(() => { // 标记初始化完成 this.authInitialized.next(true); }); }); } }
3. 修复AuthGuard逻辑(auth.guard.ts)
原Guard存在两个问题:一是有令牌时重复跳转引发循环,二是未等待OAuth初始化完成就判定令牌状态。修改后逻辑如下:
import { inject } from '@angular/core'; import { CanActivateFn, Router, UrlTree } from '@angular/router'; import { OAuthService } from 'angular-oauth2-oidc'; import { AppComponent } from './app.component'; import { filter, map, take } from 'rxjs'; export const authGuard: CanActivateFn = (route, state): boolean | UrlTree => { const oauthService: OAuthService = inject(OAuthService); const router: Router = inject(Router); const appComponent = inject(AppComponent); // 等待OAuth初始化完成后再判断权限 return appComponent.authInitialized$.pipe( filter(initialized => initialized), take(1), map(() => { if (oauthService.hasValidAccessToken() && oauthService.hasValidIdToken()) { return true; // 已有有效令牌,允许访问 } else { // 保存当前目标路由,登录后自动跳转回来 oauthService.initCodeFlow(state.url); return router.createUrlTree(['/login']); } }) ); };
4. 检查Keycloak客户端配置
确保Keycloak客户端的以下配置正确:
- Access Type:选择
public(PKCE模式推荐)或confidential - Valid Redirect URIs:添加
http://localhost:4200/*或精确的http://localhost:4200/user-home - Web Origins:添加
http://localhost:4200
5. 调整Login组件(可选优化)
在登录时传入目标路由,确保登录后回到用户原本要访问的页面:
// login.component.ts onLogin() { console.log('logging in...'); // 从路由状态获取目标路径,若没有则默认跳转到user-home const targetUrl = history.state?.redirectUrl || '/user-home'; this.oauthService.initCodeFlow(targetUrl); }
内容的提问来源于stack exchange,提问作者braveheartask123
相关产品推荐
相关产品推荐

