websockets spring websocket stomp spring-messaging spring-websocket

websockets - websocket spring boot angular 4



Cómo obtener/configurar los atributos de principal y sesión de Spring 4 stomp websocket methods (2)

Estoy haciendo experimentos con websockets de Spring 4 y stomp , y me cuesta entender cómo obtener / configurar el usuario actual y otros atributos de sesión en un método de manejo de mensajes anotado con @MessageMapping .

La documentación dice que los métodos de manejo de mensajes pueden tomar un Principal como argumento, y encontré que Spring recupera el principal llamando a getUserPrincipal() en la sesión de socket nativo y luego asociado con la sesión de socket, pero no he encontrado cualquier forma de personalizar fácilmente este comportamiento, aparte de escribir un filtro de servlet y envolver la solicitud original en un contenedor que devuelve el principal que se encuentra en mi cookie.

Entonces mis preguntas son:

  1. Cómo configurar manualmente el principal en la sesión de socket, cuando el cliente se conecta (tengo esta información gracias a una cookie personalizada, y no uso la seguridad de Spring).
  2. Si 1 no es posible, ¿cómo agregar atributos adicionales a la sesión de socket cuando el cliente se conecta?
  3. ¿Cómo acceder a la sesión de socket y sus atributos desde un método de manejo de mensajes?
  4. ¿Hay alguna forma de acceder al inicio de sesión y a la contraseña que envía el navegador en el momento de la conexión? Parece que son completamente ignorados por Spring y no son accesibles.


ACTUALIZACIÓN: con Spring 4.1 es posible configurar al usuario en el saludo de manos para # 1 desde arriba. Según la documentación de Spring , puede crear una nueva clase que amplíe DefaultHandshakeHandler y anule el método de determinar el usuario. Además, también puede crear un filtro de seguridad que establezca el principal también si tiene un token. Implementé el segundo yo mismo e incluí un código de muestra para ambos a continuación.

Para # 2 y # 3 no creo que todavía sea posible. Para # 4 Spring ignora intencionalmente estos por la documentación aquí .

CÓDIGO DE MUESTRA PARA DefaultCLINKHandshakeHandler 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()); } }

CÓDIGO DE MUESTRA PARA EL FILTRO DE SEGURIDAD:

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

** NOTA: ** NO declare su filtro como un frijol. Si lo hace, también se recogerá (al menos usando Spring Boot) en los filtros genéricos para que se active en cada solicitud.