Java OpenResty Spring Spring Boot MySQL Redis MongoDB PostgreSQL Linux Android Nginx 面试 小程序 Arthas JVM AQS juc Kubernetes Docker 诊断工具


Java WebSocket 获取 HttpSession

Java WebSocket Session 评论 1 大约 2696 字

原理

WebSocket在建立时握手阶段是建立在HTTP协议之上,握手成功升级为TCP长链接。在握手阶段获取HttpSession,设置到WebSocketSession中即可。

前提

HTTP服务端口必须和WebSocket端口为同一个(比如:8080),不能一个使用代理端口,一个直接使用服务端口。

代码

配置类:从握手阶段获取到HttpSession,如果不为null,则放入WebSocketSession中的用户配置Map中。

public class WebSocketServerConfigurator extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        if (httpSession != null) {
            Map<String, Object> userProperties = sec.getUserProperties();
            userProperties.put(HttpSession.class.getName(), httpSession);
        }
    }
}

设置自定义的Configurator

@ServerEndpoint(value = "/test/ws/{username}", configurator = WebSocketServerConfigurator.class)
public class WebSocketServer {

    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session) {
        HttpSession httpSession = (HttpSession) session.getUserProperties().get(HttpSession.class.getName());
        System.out.println("onOpen#" + httpSession);

        // 最大超时时间 60 秒
        session.setMaxIdleTimeout(TimeUnit.MINUTES.toMillis(5));
        session.setMaxBinaryMessageBufferSize(8192 * 1024); // 8KB
        session.setMaxTextMessageBufferSize(8192 * 1024); // 字符数
        session.getAsyncRemote().sendText("123");
    }

    // ... 省略了其他事件
}

注意

如果一开始没有请求没有初始化HttpSession(如刚进入网页就连接WebSocket),则WebSocket握手阶段HandshakeRequest获取到的HttpSessionnull,此时再在别的Servlet中设置HttpSessionWebSocket中始终获取不到HttpSession

解决办法

方法一

连接WebSocket操作放在初始化了HttpSession之后。比如登录后写入相关信息到HttpSession

方法二

增加ServertRequestListener,在每次请求初始化时都去获取一次HttpSession(如果请求没有关联的HttpSession,则创建一个新的),这样此次用户浏览网页时都是关联的同一个Session

@WebListener
public class MyServletListener implements ServletRequestListener {

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
        HttpSession session = request.getSession();
        System.out.println("requestInitialized session = " + session);
    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("requestDestroyed sre = " + sre);
    }

}

可以看到getSession表示:返回请求关联的 session, 若请求没有 session 则创建一个。

public interface HttpServletRequest extends ServletRequest {
    /**
     * Returns the current session associated with this request, or if the
     * request does not have a session, creates one.
     *
     * @return the <code>HttpSession</code> associated with this request
     * @see #getSession(boolean)
     */
    public HttpSession getSession();
}
阅读 3948 · 发布于 2021-04-21

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
  • 111 1楼
    回复一下:如果websoket配置没有问题,但是获取到httpsession还是为空.如果是前后端分离,那么需要修改,在跨域请求中,设置了axios.defaults.withCredentials = true(携带cookie发起请求,因为session id最后是写在cookie返回给前端的),且服务器必须在响应中设置Access-Control-Allow-Credentials为true。
    这样多个controler还有websocket就能拿到唯一session(针对的是同一个用户)
    Safari | Mac OSX 2023-12-26
随便看看 换一批