我正在使用Spring 4 websockets and stomp进行实验,我很难弄清楚如何在使用@MessageMapping
注释的消息处理方法中获取/设置当前用户和其他会话属性。
The documentation说消息处理方法可以将Principal作为参数,我发现Spring通过在本机套接字会话上调用getUserPrincipal()
来检索主体,然后与套接字关联session,但我没有找到任何方法来轻松自定义此行为,除了编写servlet过滤器并将原始请求包装回包装器中,返回我的cookie中找到的主体。
所以我的问题是:
答案 0 :(得分:5)
更新:使用Spring 4.1,可以从上面为#1的握手设置用户。每the Spring documentation您可以创建一个新类,它扩展DefaultHandshakeHandler并覆盖determineUser方法。此外,您还可以创建一个安全过滤器,如果您有令牌,也可以设置主体。我自己实现了第二个,我在下面提供了一些示例代码。
对于#2和#3,我认为它仍然不可能。对于#4 Spring,故意忽略这些the documentation here。
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());
}
}
安全过滤器的示例代码:
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),因此它将在每个请求中触发。
答案 1 :(得分:4)
暂时不可能(Spring 4.0)。 Spring上已经打开(并考虑过)一个问题:https://jira.springsource.org/browse/SPR-11228