How to get/set the principal and session attributes from Spring 4 stomp websocket methods

后端 未结 2 942
[愿得一人]
[愿得一人] 2021-02-02 15:33

I\'m doing experiments with Spring 4 websockets and stomp, and I have a hard time figuring out how to get/set the current user and other session attributes in a message handling

相关标签:
2条回答
  • 2021-02-02 16:08

    This is impossible for the time being (Spring 4.0). An issue has been opened (and considered) at Spring: https://jira.springsource.org/browse/SPR-11228

    0 讨论(0)
  • 2021-02-02 16:13

    UPDATE: With Spring 4.1 it is possible to set the user on the handshake for #1 from above. Per the Spring documentation you can create a new class which extends DefaultHandshakeHandler and override the determineUser method. Additionally you can also create a security filter which sets the principal as well if you have a token. I have implemented the second one myself and I include some sample code for both below.

    For #2 and #3 I do not think that it is possible still. For #4 Spring intentionally ignores these per the documentation here.

    SAMPLE CODE FOR DefaultHandshakeHandler SUBCLASS:

    @Configuration
    @EnableWebSocketMessageBroker
    public class ApplicationWebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
    
        public class MyHandshakeHandler extends DefaultHandshakeHandler {
    
            @Override
            protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, 
                                              Map<String, Object> attributes) {
                // add your own code to determine the user
                return null;
            }
        }
    
        @Override
        public void registerStompEndpoints(StompEndpointRegistry registry) {
    
            registry.addEndpoint("/myEndPoint").setHandshakeHandler(new MyHandshakeHandler());
    
        }
    }
    

    SAMPLE CODE FOR SECURITY FILTER:

    public class ApplicationSecurityTokenFilter extends GenericFilterBean {
    
        private final static String AUTHENTICATION_PARAMETER = "authentication";
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            if (servletRequest instanceof HttpServletRequest) {
                // check to see if already authenticated before trying again
                Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
                if ((existingAuth == null) || !existingAuth.isAuthenticated()) {
                    HttpServletRequest request = (HttpServletRequest)servletRequest;
                    UsernamePasswordAuthenticationToken token = extractToken(request);
                    // dump token into security context (for authentication-provider to pick up)
                    if (token != null) {  // if it exists
                        SecurityContextHolder.getContext().setAuthentication(token);
                    }
                }
            }
            filterChain.doFilter(servletRequest,servletResponse);
        }
    
        private UsernamePasswordAuthenticationToken extractToken( HttpServletRequest request ) {
            UsernamePasswordAuthenticationToken authenticationToken = null;
            // do what you need to extract the information for a token
            // in this example we assume a query string that has an authenticate
            // parameter with a "user:password" string.  A new UsernamePasswordAuthenticationToken
            // is created and then normal authentication happens using this info.
            // This is just a sample and I am sure there are more secure ways to do this.
            if (request.getQueryString() != null) {
                String[] pairs = request.getQueryString().split("&");
                for (String pair : pairs) {
                    String[] pairTokens = pair.split("=");
                    if (pairTokens.length == 2) {
                        if (AUTHENTICATION_PARAMETER.equals(pairTokens[0])) {
                            String[] tokens = pairTokens[1].split(":");
                            if (tokens.length == 2) {
                                log.debug("Using credentials: " + pairTokens[1]);
                                authenticationToken = new UsernamePasswordAuthenticationToken(tokens[0], tokens[1]);
                            }
                        }
                    }
                }
            }
            return authenticationToken;
        }
    }
    
    // set up your web security for the area in question
    @Configuration
    public class SubscriptionWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
    
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .requestMatchers().antMatchers("/myEndPoint**","/myEndPoint/**").and()
                    .addFilterBefore(new ApplicationSecurityTokenFilter(), UsernamePasswordAuthenticationFilter.class)
                    .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                    .httpBasic()  // leave this if you want non web browser clients to connect and add an auth header
                    .and()
                    .csrf().disable();
        }
    }
    

    ** NOTE: ** DO NOT declare your filter as a Bean. If you do then it will also be picked up (at least using Spring Boot) in the generic filters so it will fire on every request.

    0 讨论(0)
提交回复
热议问题