如何通过HandlerInterceptorAdapter连接到WebSocket

时间:2015-07-01 13:38:23

标签: spring spring-mvc spring-websocket sockjs spring-messaging

我试图用简单的代理,SockJS和STOMP来实现Spring Websocket。我的应用程序在自定义拦截器(称为SecurityInterceptor)中处理身份验证,该拦截器扩展了HandlerInterceptorAdapter类。 我想要在建立连接时通过我的拦截器类由SOMP客户端发出的HTTP请求,以便我验证用户是经过身份验证的用户。但是这些初始HTTP请求不会通过我的自定义拦截器。请问有人在这个问题上给我启发吗?以下是我正在做的事情

主要网络配置类:

    @Configuration
    @EnableWebMvc
    @ComponentScan( { "..." } )
    public class RestWebSvcMainConfig extends WebMvcConfigurerAdapter {

        public void addInterceptors( final InterceptorRegistry oRegistry ) {

            try {

                oRegistry.addInterceptor( securityInterceptor() ).addPathPatterns( "/dms/secured/**" );
            }
            catch ( Exception ex ) {

                throw new RuntimeException( ex );
            }
        }

        @Bean
        public SecurityInterceptor securityInterceptor() throws Exception {
            SecurityInterceptor oSecurityInterceptor = new SecurityInterceptor( authenticationProvider() );
            return oSecurityInterceptor;
        }

    }

WebSocket配置类:

@Configuration
@ComponentScan({"..."})
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/dms/secured/hello").withSockJS();
    }

}

控制器类:

@Controller
public class GreetingController {
    @MessageMapping("/dms/secured/hello")
    @SendToUser(value="/topic/greetings", broadcast = false)
    public Greeting greeting(HelloMessage message) throws Exception {
        Thread.sleep(3000); // simulated delay
        return new Greeting("Hello, " + message.getName() + "!");
    }

}

web.xml中的Dispatcher servlet映射:

<!-- Map all requests to the dispatcher servlet -->
 <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
 </servlet-mapping>

STOMP客户:

function connect() {
  var socket = new SockJS('/GlobalAPI/dms/secured/hello');
  stompClient = Stomp.over(socket);  
  stompClient.connect({}, function(frame) {
     setConnected(true);
     console.log('Connected: ' + frame);
     stompClient.subscribe('/user/topic/greetings', function(greeting){
        showGreeting(JSON.parse(greeting.body).content);
     });
  });
}

GlobalAPI是上下文根。

http://localhost:8080/GlobalAPI/dms/secured/hello/info的初始请求不会通过拦截器。当我发出任何其他HTTP请求,如http://localhost:8080/GlobalAPI/dms/secured/documents时,请求很好地通过拦截器。 我的代码出了什么问题?

1 个答案:

答案 0 :(得分:0)

我建议你实现自己的HandshakeInterceptor实现。我复制了Spring框架的一个实现的所有代码:

package mx.config.ws;

import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

public class HttpSessionHandshakeInterceptor_personalised implements HandshakeInterceptor {


    /**
     * The name of the attribute under which the HTTP session id is exposed when
     * {@link #setCopyHttpSessionId(boolean) copyHttpSessionId} is "true".
     */
    public static final String HTTP_SESSION_ID_ATTR_NAME = "HTTP.SESSION.ID";


    private final Collection<String> attributeNames;

    private boolean copyAllAttributes;

    private boolean copyHttpSessionId = true;

    private boolean createSession;


    /**
     * Default constructor for copying all HTTP session attributes and the HTTP
     * session id.
     * @see #setCopyAllAttributes
     * @see #setCopyHttpSessionId
     */
    public HttpSessionHandshakeInterceptor_personalised() {
        this.attributeNames = Collections.emptyList();
        this.copyAllAttributes = true;
    }

    /**
     * Constructor for copying specific HTTP session attributes and the HTTP
     * session id.
     * @param attributeNames session attributes to copy
     * @see #setCopyAllAttributes
     * @see #setCopyHttpSessionId
     */
    public HttpSessionHandshakeInterceptor_personalised(Collection<String> attributeNames) {
        this.attributeNames = Collections.unmodifiableCollection(attributeNames);
        this.copyAllAttributes = false;
    }


    /**
     * Return the configured attribute names to copy (read-only).
     */
    public Collection<String> getAttributeNames() {
        return this.attributeNames;
    }

    /**
     * Whether to copy all attributes from the HTTP session. If set to "true",
     * any explicitly configured attribute names are ignored.
     * <p>By default this is set to either "true" or "false" depending on which
     * constructor was used (default or with attribute names respectively).
     * @param copyAllAttributes whether to copy all attributes
     */
    public void setCopyAllAttributes(boolean copyAllAttributes) {
        this.copyAllAttributes = copyAllAttributes;
    }

    /**
     * Whether to copy all HTTP session attributes.
     */
    public boolean isCopyAllAttributes() {
        return this.copyAllAttributes;
    }

    /**
     * Whether the HTTP session id should be copied to the handshake attributes
     * under the key {@link #HTTP_SESSION_ID_ATTR_NAME}.
     * <p>By default this is "true".
     * @param copyHttpSessionId whether to copy the HTTP session id.
     */
    public void setCopyHttpSessionId(boolean copyHttpSessionId) {
        this.copyHttpSessionId = copyHttpSessionId;
    }

    /**
     * Whether to copy the HTTP session id to the handshake attributes.
     */
    public boolean isCopyHttpSessionId() {
        return this.copyHttpSessionId;
    }

    /**
     * Whether to allow the HTTP session to be created while accessing it.
     * <p>By default set to {@code false}.
     * @see javax.servlet.http.HttpServletRequest#getSession(boolean)
     */
    public void setCreateSession(boolean createSession) {
        this.createSession = createSession;
    }

    /**
     * Whether the HTTP session is allowed to be created.
     */
    public boolean isCreateSession() {
        return this.createSession;
    }


    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
            WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {


        // Set ip attribute to WebSocket session
        attributes.put("ip", request.getRemoteAddress());

        // ============================================= CODIGO PERSONAL
        ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
        HttpServletRequest httpServletRequest = servletRequest.getServletRequest();
//        httpServletRequest.getCookies();
//        httpServletRequest.getParameter("inquiryId");
//        httpServletRequest.getRemoteUser();



        System.out.println("SessionHandshakeInterceptor::beforeHandshake()    httpServletRequest.getRemoteUser()(): "+httpServletRequest.getRemoteUser());

        //  Salida:     "/127.0.0.1:8080"
        System.out.println("SessionHandshakeInterceptor::beforeHandshake()    request.getLocalAddress(): "+request.getLocalAddress());
        //  Salida:     "http://localhost:8080/ServLocalBancarias-0.0.1-SNAPSHOT/chat/923/uezxomjg/websocket"
        System.out.println("SessionHandshakeInterceptor::beforeHandshake()    request.getURI(): "+request.getURI());
        //  Salida:     "/127.0.0.1:59197"
        System.out.println("SessionHandshakeInterceptor::beforeHandshake()    request.getRemoteAddress(): "+request.getRemoteAddress());
        //  Salida: null
        System.out.println("SessionHandshakeInterceptor::beforeHandshake()    request.getPrincipal(): "+request.getPrincipal());
        /*
         *   {
               Origin=[http://localhost:3000],
               Cookie=[__utma=111872281.293045146.1470257629.1470257649.1470362764.31;
               __utmc=111872281;
               __utmz=111872281.1470257629.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)],
               Sec-WebSocket-Key=[XGDdYK2TDz6djy852SzBNg==],
               User-Agent=[Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36],
               Connection=[Upgrade],
               Sec-WebSocket-Version=[13],
               Host=[localhost:8080],
               Accept-Encoding=[gzip, deflate, sdch],
               DNT=[1],
               Pragma=[no-cache],
               Upgrade=[websocket],
               Sec-WebSocket-Extensions=[permessage-deflate;
               client_max_window_bits],
               Cache-Control=[no-cache],
               Accept-Language=[en-US,en;q=0.8]
             }

         */
        System.out.println("SessionHandshakeInterceptor::beforeHandshake()    request.getHeaders(): "+request.getHeaders());

        /*
         * Salida:
         */
        attributes.forEach((key,value)->{System.out.println("SessionHandshakeInterceptor::beforeHandshake()   attributes Key: "+key+" Value: "+value);});








        // ============================================== CODIGO ORIGINAL DE Spring INI
        HttpSession session = getSession(request);
        if (session != null) {

            // ===================================== INI

             System.out.println("SessionHandshakeInterceptor::beforeHandshake()    session: "+session);


            // ===================================== END


            if (isCopyHttpSessionId()) {
                attributes.put(HTTP_SESSION_ID_ATTR_NAME, session.getId());
            }
            Enumeration<String> names = session.getAttributeNames();
            while (names.hasMoreElements()) {
                String name = names.nextElement();
                if (isCopyAllAttributes() || getAttributeNames().contains(name)) {
                    attributes.put(name, session.getAttribute(name));
                }
            }
        }
        // ============================================== CODIGO ORIGINAL DE Spring END



        return true;
    }

    private HttpSession getSession(ServerHttpRequest request) {
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
            return serverRequest.getServletRequest().getSession(isCreateSession());
        }
        return null;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
            WebSocketHandler wsHandler, Exception ex) {

         System.out.println("SessionHandshakeInterceptor::afterHandshake()");

    }


}

接下来,您需要创建一个bean:

@EnableScheduling
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Bean
    public HttpSessionHandshakeInterceptor_personalised handShakeInterceptor() {
        return new HttpSessionHandshakeInterceptor_personalised();
    }

Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //registry.addEndpoint("/ws").withSockJS();

        // Enpoint para el chat de pruebas.
        registry.addEndpoint("/chat")
//          .addInterceptors(new IpHandshakeInterceptor())
//          .addInterceptors(new SessionHandshakeInterceptor())// FUNCIONA TAMBIEN, IGUAL QUE EL OTRO
//          .addInterceptors(new HttpSessionHandshakeInterceptor())
            .addInterceptors(handShakeInterceptor()) // Este el mejor y unico necesario.
//          .setHandshakeHandler(handshakeHandler)
            .setAllowedOrigins("*")     // ELIMINAR. USADO PARA PERMITIR CONECTARSE DESDE OTRO SERVIDOR TIPO LOCALHOST
            .withSockJS()
            ;       


    }
    ... 
}

这项工作适合我。