It is an error when I want to add websocket into my web.
This is my WebSocketConfig
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/socket").setAllowedOrigins("*").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/message");
config.setApplicationDestinationPrefixes("/app");
}
}
This is my Spring SercurityConfig
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(authenticationManagerBean());
customAuthenticationFilter.setFilterProcessesUrl("/api/login");
http.csrf().disable().cors().configurationSource(request -> {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.addAllowedHeader("*");
return configuration;
}).and()
// .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// .authorizeHttpRequests().antMatchers("/api/login/**", "/api/token/refresh/**", "/api/user/save/**","/socket/**", "/socket/**").permitAll().and()
// .authorizeHttpRequests().antMatchers(HttpMethod.GET, "/api/users/**").hasAnyAuthority("ROLE_USER").and()
// .authorizeHttpRequests().antMatchers(HttpMethod.GET, "/api/posts/**").hasAnyAuthority("ROLE_USER").and()
// .authorizeHttpRequests().anyRequest().authenticated().and()
.authorizeRequests().anyRequest().permitAll().and()
.addFilter(customAuthenticationFilter)
.addFilterBefore(new CustomAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
This is my CustomAuthorizationFilter
public class CustomAuthorizationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (request.getServletPath().equals("/api/login") || request.getServletPath().equals("/api/token/refresh")) {
filterChain.doFilter(request, response);
} else {
String authorizationHeader = request.getHeader(AUTHORIZATION);
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
try {
String token = authorizationHeader.substring("Bearer ".length());
Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(token);
String username = decodedJWT.getSubject();
String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
Arrays.stream(roles).forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role));
});
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
} catch (Exception exception) {
log.error("Error logging in: {}", exception.getMessage());
response.setHeader("error", exception.getMessage());
response.setStatus(FORBIDDEN.value());
Map<String, String> error = new HashMap<>();
error.put("error_message", exception.getMessage());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), error);
}
} else {
filterChain.doFilter(request, response);
}
}
}
}
And interceptor token and service I use to connect socket socket in angular
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(private auth: AuthService, private router: Router) { }
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> | any {
const myToken = this.auth.getToken();
if (myToken) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${myToken}`
},
});
}
return next.handle(request).pipe(
catchError((err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
return this.handleUnAuthorizedError(request, next)
}
}
return throwError(() => new Error("Some other error occured"))
})
);
}
handleUnAuthorizedError(req: HttpRequest<any>, next: HttpHandler) {
let tokenApiModel = new TokenApiModel();
tokenApiModel.access_token = this.auth.getToken()!;
tokenApiModel.refresh_token = this.auth.getRefreshToken()!;
return this.auth.renewToken(tokenApiModel)
.pipe(
switchMap((data: TokenApiModel) => {
this.auth.storeRefreshToken(data.refresh_token)
this.auth.storeToken(data.access_token)
req = req.clone({
setHeaders: {
Authorization: `Bearer ${data.access_token}`
},
withCredentials: false
});
return next.handle(req)
}),
catchError((err) => {
return throwError(() => {
alert("Token is expired, login again")
this.router.navigate(["login"])
})
})
)
}
}
@Injectable({
providedIn: 'root'
})
export class SocketService {
public stompClient: any;
public msg: Array<any> = [];
constructor() {
const serverUrl = 'http://localhost:8080/socket';
const ws = new SockJS(serverUrl);
this.stompClient = Stomp.over(ws);
}
initializeWebSocketConnection() {
const that = this;
console.log(this.stompClient); // It work until here
this.stompClient.connect({}, function (frame: any) {
that.stompClient.subscribe('/message', (message: any) => {
console.log(message);
if (message.body) {
that.msg.push(message.body);
}
});
that.stompClient.subscribe('/messages', (message: any) => {
console.log(message);
if (message.body) {
that.msg.push(message.body);
}
});
});
}
sendMessage(message: any) {
this.stompClient.send('/app/send/message', {}, message);
}
}
This is logger error error
I think that it is maybe a conflict of spring security and websocket config
As the error said so I think you have to set configuration.setAllowCredentials(true);