JHipster Angular调用Spring后端Kafka Webflux接口401无Bearer令牌问题
问题根因
原生浏览器EventSource API 本身不支持自定义Authorization这类请求头,且它发出的所有请求不会经过Angular的HTTP拦截器链路,你为HttpClient配置的全局Bearer令牌注入逻辑对SSE请求完全不生效,这就是接口返回401的核心原因。
符合Angular依赖注入规范的两种解决方案
两种方案都可以直接复用JHipster自带的AuthServerProvider组件获取当前登录用户的令牌,不需要额外实现独立的HTTP客户端。
方案一:令牌拼入URL参数(改造成本最低)
后端适配支持从查询参数读取access_token做鉴权即可,前端改动极小:
- 先在
MqconnectService中注入JHipster自带的AuthServerProvider获取令牌,把令牌拼到请求URL后
import { AuthServerProvider } from 'app/core/auth/auth-server-provider.service'; @Injectable({ providedIn: 'root', }) export class MqconnectService { url: string = KAFKA_CONSUME_MSG; constructor(private authServerProvider: AuthServerProvider) {} getMessagesStream(): Observable<StandardMessage> { return new Observable<StandardMessage>(observer => { // 从JHipster自带的认证服务拿当前登录用户的令牌 const token = this.authServerProvider.getToken(); // 令牌拼入URL参数 const urlWithToken = `${this.url}?access_token=${token}`; const eventSource = new EventSource(urlWithToken, {withCredentials: true}); // 原有onmessage、onerror逻辑保持不变 eventSource.onmessage = event => { console.debug('received event: ', event); const json = JSON.parse(event.data); observer.next( new StandardMessage( json['Id'], // OMITTED ) ); }; eventSource.onerror = error => { if (eventSource.readyState === 0) { console.log('The stream has been closed by the server.'); eventSource.close(); observer.complete(); } else { observer.error('EventSource error: ' + `${String(error)}`); } }; }); } }
- 后端Spring Boot资源服务器适配,支持从参数读取令牌:
@Configuration public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.oauth2ResourceServer() .bearerTokenResolver(new DefaultBearerTokenResolver() { @Override public String resolve(HttpServletRequest request) { // 优先从请求头取令牌,取不到再从查询参数取 String token = super.resolve(request); if (token == null) { token = request.getParameter("access_token"); } return token; } }); // 保留原有其他安全配置 return http.build(); } }
方案二:用支持自定义头的SSE polyfill(安全性更高)
如果担心令牌出现在URL中会被日志记录泄露,可以使用兼容原生API的eventsource polyfill库,支持设置请求头,不需要修改后端鉴权逻辑:
- 安装依赖:
npm install eventsource - 前端改造代码:
import * as EventSource from 'eventsource'; import { AuthServerProvider } from 'app/core/auth/auth-server-provider.service'; @Injectable({ providedIn: 'root', }) export class MqconnectService { url: string = KAFKA_CONSUME_MSG; constructor(private authServerProvider: AuthServerProvider) {} getMessagesStream(): Observable<StandardMessage> { return new Observable<StandardMessage>(observer => { const token = this.authServerProvider.getToken(); // 自定义请求头塞入Bearer令牌 const initOptions = { withCredentials: true, headers: { 'Authorization': `Bearer ${token}` } }; const eventSource = new EventSource(this.url, initOptions); // 原有业务逻辑保持不变 }); } }
补充注意事项
如果SSE长连接期间令牌过期,后端会主动断开连接,你可以在onerror回调中判断返回状态为401时,复用JHipster自带的令牌刷新逻辑,拿到新令牌后自动重连即可,不需要额外改造认证体系。
内容的提问来源于stack exchange,提问作者Semo
相关产品推荐
相关产品推荐

