6

I'm currently Building a web application that uses Spring to leverage websocket Support and security. The thing is, I don't want to use STOMP. It has not been updated for about 4 yours now and I don't need it. So I followed the answer to another Stackoverflow question to configure Spring for websockets with SockJS but without STOMP.

Now I want to integrate Spring Security for websocket authentication and authorisateion. Unfortunately the documentation is bound to configure Spring Security for STOMP-websockets.

I would highly appreciate any help with configuring Spring Security for my case. Does someone maybe know any tutorial or example for that? I did not find any yet.

Fencer
  • 1,026
  • 11
  • 27

2 Answers2

3

Websocket messaging session starts with a Http request , therefore spring security can be used as it's a framework for securing http requests. In spring security authenticated user and its associated security context are stored in session. Authenticated is accessible from Websocket handshake because it transports http request. Spring Websocket defines HttpSessionHandshakeInterceptor which can be registred in your configuration using addInterceptors method

    @EnableWebSocket
    @Configuration
    public class WebSocketConfig implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry 
       webSocketHandlerRegistry) {
    webSocketHandlerRegistry.addHandler(createHandler(), 
    "/handler").addInterceptors(new HttpSessionHandshakeInterceptor() 
      {
        @Override
        public void afterHandshake(ServerHttpRequest request, 
         ServerHttpResponse response, WebSocketHandler wsHandler, 
         @Nullable Exception ex) {

            super.afterHandshake(request, response, wsHandler, ex);

        }

        @Override
        public boolean beforeHandshake(ServerHttpRequest request, 
         ServerHttpResponse response, WebSocketHandler wsHandler, 
          Map<String, Object> attributes) throws Exception {
            boolean b = super.beforeHandshake(request, response, 
         wsHandler, attributes) && 
      ((UsernamePasswordAuthenticationToken) 
    request.getPrincipal()).isAuthenticated();
            return b;
        }
         }).withSockJS();
    }

     @Bean
     public WebSocketHandler createHandler() {

    return new MyHandler();

    }
   }

you can also verify authenticated use at Handler which is stored in websocket session

biiyamn
  • 476
  • 3
  • 9
  • Sorry for the delayed reaction. I had to dig deeper into the issue to valuate you answer correctly. I was looking for something more, but actually that's all there is, at least almost. – Fencer Apr 25 '18 at 18:21
  • 1
    Since all the convenient security configuration related to web-sockets is bound to the message protocol, i.e. Stomp, I can not utilise it, when not using that protocol. However the use of a `HandshakeInterceptor` points in the right direction. As the [doc](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket-server-handshake) states it can be used to place information into the attributes-map of the web-socket-session, for example the whole principal stored in the request. – Fencer Apr 25 '18 at 19:07
  • 1
    Fine-grained authorisation has then to be implemented corresponding to the principle "do it yourself". A `WebsocketHandlerDecorator` may help here. I have to say, your answer could have been a little more elaborated, but I'll accept it of course for pointing me in the right direction and so helping me a lot. Thanks! – Fencer Apr 25 '18 at 19:12
  • Thank you! I managed to solve a problem with the help by the indication id of a session. – mrchebik Apr 13 '19 at 21:02
  • hi @biiyamn i don't need to get authaiticated for ws but when in my WebSecurityConfigurerAdapter i add the ws restpoint to permitAll it going always in 401, any help pls – Alex Aug 11 '23 at 12:49
1

I will only add the answer above, having specified as I managed to solve it.

My problem was that request.getPrinciple() returned null. Having played from viewings of headings, I found cookie value. And having substituted the same cookie id value to websocket request, the server finds principle for this request.

For example, when we authorized, I make request to /auth/principal, to get data about player from database.

@GetMapping(value="/auth/principal")
    public ResponseEntity principal(HttpServletRequest request,
                                    Principal principal) {
        Player player = playerService.findByEmail(principal.getName());

        String jsonString = new JSONObject()
                .put("id", player.getId())
                .put("nickname", player.getNickname())
                .put("cookie", request.getHeader("cookie")).toString();

        return new ResponseEntity(jsonString, HttpStatus.OK);
    }

Then, on client side, when I want to connect to websocket, I specify headers of request, defining cookie (cookie: COOKIE-ID)

mrchebik
  • 109
  • 1
  • 11