You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Keycloak的Angular16+SpringBoot3.2授权配置问题咨询

解决Angular+Spring Boot+Keycloak授权码流重定向异常问题

问题出在哪

  1. 无状态会话搞砸了授权流程:你设置了SessionCreationPolicy.STATELESS,但OAuth2授权码流必须依赖会话存储state参数来防范CSRF攻击,没有会话的话后端无法跟踪授权状态,直接导致重定向逻辑混乱。
  2. XHR请求吞了重定向:前端用AJAX调用/api/me时,后端返回的302重定向会被XHR自动跟随,而非触发浏览器页面跳转,最终登录请求在XHR上下文里执行,无法正常显示Keycloak登录页。
  3. Realm配置不一致:资源服务器和客户端的issuer-uri分别指向timetable-local和my-realm,两者不统一会导致JWT验证与授权流程无法匹配。

Spring Boot 配置调整

1. 恢复会话状态支持

删掉SessionCreationPolicy.STATELESS,改用默认的IF_REQUIRED——授权码流必须靠会话存储状态,这是硬性要求:

.sessionManagement(manager -> manager.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))

2. 区分API与页面请求的未认证处理

对API请求返回401让前端处理跳转,仅对浏览器直接访问的页面返回302重定向到授权页,添加异常处理器实现:

.exceptionHandling(exceptions -> exceptions
    .authenticationEntryPoint((request, response, authException) -> {
        // 判断是否为XHR请求或API路径
        if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With")) || 
            request.getRequestURI().startsWith("/api/")) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
        } else {
            // 非API请求直接跳转到授权页
            response.sendRedirect("/oauth2/authorization/keycloak");
        }
    })
)

3. 统一Realm配置

将资源服务器和客户端的issuer-uri统一指向同一个Keycloak Realm,比如都用my-realm:

spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8000/auth/realms/my-realm

spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8000/auth/realms/my-realm

4. 配置正确的回调地址

指定授权成功后的跳转地址,若由后端处理回调则用默认地址,若直接跳回Angular则填写对应地址(需在Keycloak客户端的Valid Redirect URIs中添加该地址):

# 后端处理回调的默认地址
spring.security.oauth2.client.registration.keycloak.redirect-uri=http://localhost:8000/login/oauth2/code/keycloak
# 或直接跳转到Angular的回调页
# spring.security.oauth2.client.registration.keycloak.redirect-uri=http://localhost:4200/callback

Angular 配置调整

1. 用拦截器捕获401并触发跳转

添加HTTP拦截器,当API返回401时,直接让浏览器跳转到后端授权端点,避免XHR自动处理重定向:

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    return next.handle(request).pipe(
      catchError((error: HttpErrorResponse) => {
        if (error.status === 401) {
          // 直接跳转后端授权地址,触发浏览器页面跳转
          window.location.href = 'http://localhost:8000/oauth2/authorization/keycloak';
        }
        return throwError(() => error);
      })
    );
  }
}

2. 处理授权回调(若跳回Angular)

如果后端授权后跳转到Angular的/callback路由,创建对应组件处理回调逻辑,完成后跳回首页或之前的页面:

// 在路由模块中添加回调路由
const routes: Routes = [
  // 其他业务路由
  { path: 'callback', component: AuthCallbackComponent }
];

// AuthCallbackComponent 实现
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-auth-callback',
  template: '<p>登录中...</p>'
})
export class AuthCallbackComponent implements OnInit {
  constructor(private route: ActivatedRoute) {}

  ngOnInit(): void {
    // 可从路由参数获取状态,或调用后端接口获取用户信息
    // 完成后跳回目标页面
    window.location.href = '/';
  }
}

3. 登录按钮直接触发授权跳转

不要等API返回401再处理,直接给登录按钮绑定跳转事件,触发浏览器访问后端授权端点:

<button (click)="goToLogin()">登录</button>
goToLogin(): void {
  window.location.href = 'http://localhost:8000/oauth2/authorization/keycloak';
}

额外注意事项

  • 确保Keycloak客户端的Valid Redirect URIs包含你配置的回调地址,否则授权请求会被Keycloak拒绝。
  • CORS配置需允许Angular域名(如http://localhost:4200)的请求,并允许携带必要的请求头。
  • 若需前端携带JWT调用API,可在登录成功后通过后端接口获取JWT,前端存储后在API请求中添加Authorization: Bearer {token}头,后端作为资源服务器验证该JWT即可。

内容的提问来源于stack exchange,提问作者Dorin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 20:44:55