DEV Community

Problema com CORS e WebSocket no Angular

Olá, pessoal! Estou enfrentando um problema ao tentar fazer uma requisição de WebSocket no meu projeto Angular. A aplicação Angular está rodando no endereço http://localhost:4200/ e o servidor WebSocket está em http://localhost:8080/. Quando tento estabelecer a conexão, recebo o seguinte

erro:
Access to XMLHttpRequest at 'http://localhost:8080/ws/info?t=1741192064562' from origin 'http://localhost:4200/' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Além disso, o seguinte erro é registrado:
GET http://localhost:8080/ws/info?t=1741192064562 net::ERR_FAILED 404 (Not Found)

Código que estou utilizando no backend (Spring Boot)

WebSocketConfig.java (Configuração do WebSocket)

@Configuration
@EnableWebSocketMessageBroker
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    private final String allowedOrigin = "http://localhost:4200";

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws") // Endpoint WebSocket
          .setAllowedOrigins(allowedOrigin)  // Permite origens específicas
                .withSockJS(); // Permite fallback para SockJS
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new WebSocketAuthenticationPrincipalArgumentResolver());
    }

    @Override
    public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
        DefaultContentTypeResolver resolver = new DefaultContentTypeResolver();
        resolver.setDefaultMimeType(MediaType.APPLICATION_JSON);
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setObjectMapper(new ObjectMapper());
        converter.setContentTypeResolver(resolver);
        messageConverters.add(converter);
        return false;
    }
}
Enter fullscreen mode Exit fullscreen mode

SecurityConfig.java (Configuração de segurança com CORS)

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @SuppressWarnings("unused")
    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Autowired
    SecurityFilter securityFilter;

    @SuppressWarnings("removal")
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers(HttpMethod.POST, "/auth/login").permitAll()
                        .requestMatchers(HttpMethod.POST, "/auth/register").permitAll()
                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()  // Permite requisições OPTIONS
                        .requestMatchers("/ws/**").permitAll()  // Permite WebSocket
                        .anyRequest().authenticated()
                )
                .addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
                .cors().and();
        return http.build();
    }

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

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
}
Enter fullscreen mode Exit fullscreen mode

CorsConfig.java (Configuração do CORS)

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedOrigins("http://localhost:4200")  // Apenas Angular pode acessar
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // Métodos permitidos
            .allowedHeaders("*")  // Permite qualquer cabeçalho
            .allowCredentials(true); // Permite envio de cookies/tokens
    }
}
Enter fullscreen mode Exit fullscreen mode

Código no frontend (Angular)

WebSocketService.ts (Serviço WebSocket)

import { Injectable } from '@angular/core';
import SockJS from 'sockjs-client';
import { Client, over } from '@stomp/stompjs';

@Injectable({
  providedIn: 'root',
})
export class WebSocketService {
  private stompClient!: Client;
  private socketUrl = 'http://localhost:8080/ws'; // URL do WebSocket backend

  disconnect(): void {
    if (this.stompClient && this.stompClient.connected) {
      this.stompClient.disconnect(() => {
        console.log('Desconectado do WebSocket');
      });
    }
  }
  // Conectar ao WebSocket
  connect(callback: (message: any) => void): void {
    const socket = new SockJS(this.socketUrl);
    this.stompClient = over(socket);

    this.stompClient.connect({'Authorization:' : 'Bearer ' + localStorage.getItem('auth-token')}, () => {
      console.log('Conectado ao WebSocket');
      // Assina o tópico de atualizações
      this.stompClient.subscribe('/topic/updates', (message) => {
        callback(JSON.parse(message.body)); // Processa a mensagem recebida
      });
    });
  }

  sendMessage(content: string): void {
    if (this.stompClient && this.stompClient.connected) {
      const chatMessage = { content };
      this.stompClient.send('/app/message', {}, JSON.stringify(chatMessage));
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

WebSocketComponent.ts (Componente Angular para interação com o WebSocket)

import { Component, OnDestroy, OnInit } from '@angular/core';
import { WebSocketService } from '../../core/service/web-socket.service';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-web-socket',
  imports: [CommonModule],
  templateUrl: './web-socket.component.html',
  styleUrl: './web-socket.component.css'
})
export class WebsocketComponent implements OnInit, OnDestroy {
  messages: string[] = [];

  constructor(private webSocketService: WebSocketService) {}

  ngOnInit(): void {
    this.webSocketService.connect((message: string) => {
      this.messages.push(message);
    });
  }

  sendMessage(): void {
    this.webSocketService.sendMessage('Olá, WebSocket!');
  }

  ngOnDestroy(): void {
    this.webSocketService.disconnect();
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
erickac profile image
Erick Andrade

Invés de usar http://, utilize ws://