Angular前端对接Keycloak认证的SpringBoot微服务后端出现401 Unauthorized错误的配置方案求助
兄弟我太懂你这种Postman测通但前端调接口就401的郁闷了!我之前在做类似的微服务项目时也踩过一模一样的坑,大概率是Angular这边没正确集成Keycloak的认证逻辑,或者跨域配置没跟上。下面给你一步步捋清楚该怎么配置:
1. 先给Angular装上Keycloak官方依赖
别自己手动写认证逻辑,官方库已经把坑填得差不多了,直接装:
npm install keycloak-angular keycloak-js
2. 初始化Keycloak客户端配置
在app.module.ts里配置Keycloak的连接信息,必须和你Keycloak控制台里的客户端配置完全对应!比如:
import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular'; import { APP_INITIALIZER } from '@angular/core'; // 初始化Keycloak的工厂函数 function initializeKeycloak(keycloak: KeycloakService) { return () => keycloak.init({ config: { url: 'http://你的Keycloak地址:8080/realms', // 新版Keycloak路径是/realms,旧版是/auth/realms,别写错! realm: '你的Realm名称', clientId: '你的Angular客户端ID' // 这个要在Keycloak控制台单独创建,Access Type设为Public }, initOptions: { onLoad: 'login-required', // 页面加载时自动跳Keycloak登录页 checkLoginIframe: false // 关闭iframe检查避免兼容性问题 } }); } @NgModule({ imports: [KeycloakAngularModule], providers: [ { provide: APP_INITIALIZER, useFactory: initializeKeycloak, multi: true, deps: [KeycloakService] } ] }) export class AppModule {}
⚠️ 重点提醒:你的Angular客户端在Keycloak控制台里必须设为Public类型(前端没法安全存储Secret,不能用Confidential),同时把Valid Redirect URIs设为你的Angular地址(比如http://localhost:4200/*),Web Origins也设为http://localhost:4200(生产环境别用*)。
3. 配置HTTP拦截器自动携带Token
这是解决401的核心!Postman里你手动加了Authorization头,但Angular请求默认不会带,得写个拦截器自动把Keycloak的token加到请求头里:
先创建拦截器文件keycloak.interceptor.ts:
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; import { Observable } from 'rxjs'; import { KeycloakService } from 'keycloak-angular'; @Injectable() export class KeycloakInterceptor implements HttpInterceptor { constructor(private keycloakService: KeycloakService) {} intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { // 只给你的后端API加Token,别给Keycloak自己的请求加 if (request.url.startsWith('http://你的后端API地址')) { const authReq = request.clone({ setHeaders: { Authorization: `Bearer ${this.keycloakService.getToken()}` } }); return next.handle(authReq); } return next.handle(request); } }
然后在app.module.ts的providers里注册这个拦截器:
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { KeycloakInterceptor } from './keycloak.interceptor'; @NgModule({ providers: [ // ...其他已有的providers { provide: HTTP_INTERCEPTORS, useClass: KeycloakInterceptor, multi: true } ] })
4. 排查后端的CORS配置
Postman不会有跨域问题,但浏览器会!你得确保SpringBoot后端的CORS配置允许携带Authorization头,比如加个配置类:
@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("http://localhost:4200"); // 你的Angular地址 config.addAllowedHeader("*"); config.addAllowedMethod("*"); // 关键:必须允许Authorization头暴露给前端 config.addExposedHeader("Authorization"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }
5. 验证Token是否有效
启动Angular后,打开浏览器开发者工具(F12),看Network标签里的请求:
- 如果请求头里没有
Authorization: Bearer <你的Token>:要么是拦截器没生效,要么是Keycloak没成功获取Token,检查初始化配置 - 如果有Token,把它复制到Postman里测试:如果Postman也401,那就是Token本身的问题——比如Realm不对、客户端权限不够,或者后端资源服务器配置的Audience和Token里的aud不匹配
最后再查几个常见坑
- 你的SpringBoot后端如果用的是
oauth2-resource-server依赖,要确保配置文件里的issuer URI正确:spring.security.oauth2.resourceserver.jwt.issuer-uri=http://你的Keycloak地址/realms/你的Realm名称 - 别把后端Confidential客户端的Secret用到Angular里,前端根本存不住,会泄露!
- 新版Keycloak(17+)的路径是
/realms,不是旧版的/auth/realms,别写错URL!
备注:内容来源于stack exchange,提问作者ch aymen

