0

我正在使用 spring boot angular 创建一个演示聊天应用程序。我已经使用 Spring Security 实现了 JWT 身份验证。我能够成功进行身份验证,但无法建立成功的 WebSocket 连接。它失败并出现以下错误:

stomp.js:134 - Opening Web Socket...
zone-evergreen.js:2863 - GET http://localhost:8090/chatal/info?t=1617996229738 40
stomp.js:134 Whoops! Lost connection to http://localhost:8090/chatal

这很奇怪,因为在 WebSocket 配置中我启用了允许来源。

registry.addEndpoint("/chatal").setAllowedOrigins("*").withSockJS();

以下是后端的配置:

  • 使用 jwt 的应用程序安全配置
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/api/auth/**")
                .permitAll()
                .anyRequest()
                .authenticated();
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

  • 在 WEB MVC 配置中启用 CORS
@EnableWebMvc
@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry corsRegistry){
        corsRegistry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .maxAge(3600L)
                .allowedHeaders("*")
                .exposedHeaders("Authorization","X-Auth-Token")
                .allowCredentials(true);
    }
}
  • WebSocket 配置
@Configuration
@EnableWebSocketMessageBroker
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
@Slf4j
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/chatal").setAllowedOrigins("*").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.setApplicationDestinationPrefixes("/app");
        registry.enableSimpleBroker("/topic");
    }
}
  • WebSocket 安全配置
@Configuration
public class WebSocketSecurityConfiguration extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        messages.anyMessage().authenticated();
    }

    @Override
    protected boolean sameOriginDisabled() {
        return true;
    }
}
  • 负责与后端连接的 Angular 聊天服务:
import {Injectable, OnDestroy} from '@angular/core';
import {Client, Message, over} from "stompjs";
import {BehaviorSubject, Observable} from "rxjs";
import {SocketState} from "../model/SocketState";
import {environment} from "../../environments/environment";
import * as SockJS from "sockjs-client";
import {filter, first, switchMap} from "rxjs/operators";
import {StompSubscription} from "@stomp/stompjs";
import {LocalStorageService} from "./local-storage.service";
import {AppSettings} from "../app.settings";

@Injectable({
  providedIn: 'root'
})
export class ChatService implements OnDestroy {

  private client: Client;
  private state: BehaviorSubject<SocketState>;

  constructor(private _localStorageSvc:LocalStorageService) {
    let token = this._localStorageSvc.get(AppSettings.AUTH_RESPONSE,{}).authenticationToken;
    const customHeaders ={
      "Authorization": `${token}`
    };
    console.log(customHeaders);
    this.client = over(<WebSocket>new SockJS(environment.socketURL));
    this.state = new BehaviorSubject<SocketState>(SocketState.ATTEMPTING);
    this.client.connect(customHeaders, () => {
      this.state.next(SocketState.CONNECTED);
    });
  }

  private connect(): Observable<Client> {
    return new Observable<Client>(observer => {
      this.state.pipe(filter(state => state === SocketState.CONNECTED)).subscribe(() => {
        observer.next(this.client);
      })
    });
  }

  onMessage(topic: string, handler = ChatService.jsonHandler): Observable<any> {
    return this.connect().pipe(first(), switchMap(client => {
      return new Observable<any>(observer => {
        const subscription: StompSubscription = client.subscribe(topic, message => {
          observer.next(handler(message));
        });
        return () => client.unsubscribe(subscription.id);
      });
    }));
  }

  onPlainMessage(topic: string): Observable<string> {
    return this.onMessage(topic, ChatService.textHandler);
  }

  send(topic: string, payload: any): void {
    this.connect()
      .pipe<Client>(first())
      .subscribe(client => client.send(topic, {}, JSON.stringify(payload)));
  }

  static jsonHandler(message: Message): any {
    return JSON.parse(message.body);
  }

  static textHandler(message: Message): string {
    return message.body;
  }

  ngOnDestroy() {
    this.connect().pipe<Client>(first()).subscribe(client=> client.disconnect(null as any));
  }

}

  • 认证后,用户重定向了聊天组件:
export class ChatRoomComponent implements OnInit {

  constructor(private _chatService:ChatService) { }

  ngOnInit(): void {
  }

}

我已经尝试使用邮递员使用以下 URI:

  • 网址:http://localhost:8090/chatal
  • 方法:获取
  • 授权:不记名令牌...
  • 结果:欢迎使用 SockJS!

  • 网址:http://localhost:8090/chatal/info?t=1617904524809
  • 方法:获取
  • 授权:不记名令牌...
  • 结果: {"entropy":1335940544,"origins":[" : "],"cookie_needed":true,"websocket":true}

此解决方案不起作用:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
 registry.addEndpoint("/chatal")
         .setAllowedOriginPatterns("*")
         .withSockJS();
}

该方法setAllowedOriginPatterns不存在,如下所示:

在此处输入图像描述

4

1 回答 1

2

改变这个:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/chatal").setAllowedOrigins("*").withSockJS();
}

至:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
 registry.addEndpoint("/chatal")
         .setAllowedOriginPatterns("*")
         .withSockJS();
}
于 2021-08-20T20:01:31.863 回答