如何在Spring 4 stomp websocket方法中获取/设置principal和session属性

16

我正在使用Spring 4的Websockets和Stomp进行实验,并且在用@MessageMapping注释的消息处理方法中,我很难弄清楚如何获取/设置当前用户和其他会话属性。

文档说,消息处理方法可以将Principal作为参数,我发现Spring通过调用本机套接字会话上的getUserPrincipal()来检索principal, 然后将其与套接字会话关联,但是除了编写servlet过滤器并将原始请求包装到返回我的cookie中找到的principal之外,我没有找到任何轻松定制此行为的方法。

所以我的问题是:

  1. 当客户端连接时如何手动将principal设置给套接字会话(我有此信息感谢自定义cookie,并且我不使用Spring安全性)?
  2. 如果1不可行,如何在客户端连接时添加其他属性到套接字会话中?
  3. 如何从消息处理方法中访问套接字会话及其属性?
  4. 是否有一种方法访问浏览器在连接时发送的登录和密码。它们似乎完全被忽略了,并且不可访问。
2个回答

7
更新:使用Spring 4.1,可以为上面的#1设置握手时的用户。 根据Spring文档,您可以创建一个新类,该类扩展DefaultHandshakeHandler并覆盖determineUser方法。 此外,如果您有令牌,还可以创建安全过滤器来设置主体。 我自己实现了第二个,并在下面包含了一些示例代码。

#2和#3我认为仍不可能。 根据此处的文档,Spring故意忽略了这些。

DefaultHandshakeHandler子类的示例代码:

@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());

    }
}

安全过滤器的示例代码:

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();
    }
}

**注意:** 不要将您的过滤器声明为Bean。 如果这样做,它也会被捕获(至少使用Spring Boot)在通用过滤器中,因此它将在每个请求上触发。


4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接