Development note/Java

[Java] WebSocket에서 HttpSession을 가져오는 방법

v명월v 2019. 10. 29. 09:00

안녕하세요. 명월입니다.


이 글은 WebSocket에서 HttpSession을 가져오는 방법에 대한 글입니다.


예전에 제가 WebSocket에 관한 글을 쓴 적이 있습니다.

링크 - [Java] 웹 소켓 (WebSocket)

링크 - [Java] 웹 소켓 (WebSocket) - broadcast(session 다루기)


간혹 저에게 질문하시는 분들 중에서 WebSocket에서 httpSession 정보를 가져올 수 없냐고하는 질문이 간혹 있어서 정리했습니다.


먼저 WebSocket도 httpprotocol를 사용하기 때문에 WebSocket안에서 httpSession을 가져올 수 있습니다. 근데 이게 처음 접속할 때 handshake를 거치고 그 다음에는 동기식으로 바뀌기 때문에 이 handshake할 때 Map에 다가 WebSocketSession으로 키로 설정해서 HttpSession을 넣고, WebSocket안에서는 WebSocketSession키로 HttpSession를 가져와서 사용하면 됩니다.(ㅎㅎㅎ....)


먼저 HandShake하는 부분의 설정입니다.

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator;

public class HttpSessionConfigurator extends Configurator {
  public static final String Session = "Session";
  public static final String Context = "Context";

  // EndPointConfig에 HttpSession과 HttpContext를 넣는다. Request와 Response는 웹 요청, 응답시에만 필요한 데이터이기 때문에 필요없다.
  @Override
  public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
    // HttpRequest로부터 Session을 받는다.
    HttpSession session = (HttpSession) request.getHttpSession();
    // HttpSession으로 부터 Context도 받는다.
    ServletContext context = session.getServletContext();
    config.getUserProperties().put(HttpSessionConfigurator.Session, session);
    config.getUserProperties().put(HttpSessionConfigurator.Context, context);
  }
}
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

// handshake 설정하기 위한 클래스를 지정한다.
@ServerEndpoint(value = "/websocket", configurator = HttpSessionConfigurator.class)
public class websocket {
  private Map<Session, EndpointConfig> configs = Collections.synchronizedMap(new HashMap<>());
  // handshake가 끝나면 handleOpen이 호출된다.
  @OnOpen
  public void handleOpen(Session userSession, EndpointConfig config) {
    System.out.println("client is now connected...");
    // EndpointConfig의 클래스를 위 맵에 넣는다.
    if (!configs.containsKey(userSession)) {
      // userSession 클래스는 connection이 될 때마다 인스턴스 생성되는 값이기 때문에 키로서 사용할 수 있다.
      configs.put(userSession, config);
    }
  }
  // 클라이언트로 부터 메시지가 오면 handleMessage가 호출 된다.
  @OnMessage
  public String handleMessage(String message, Session userSession) {
    // 위 맵으로 부터 userSession을 키로 EndpointConfig값을 가져온다.
    if (configs.containsKey(userSession)) {
      EndpointConfig config = configs.get(userSession);
      // HttpSessionConfigurator에서 설정한 session값을 가져온다.
      HttpSession session = (HttpSession) config.getUserProperties().get(HttpSessionConfigurator.Session);
      // Session의 TestSession키로 데이터를 가져온다. (테스트용)
      return "Session - " + (String) session.getAttribute("TestSession");
    }
    return "error";
  }
  @OnClose
  public void handleClose(Session userSession) {
    System.out.println("client is now disconnected...");
    // 접속이 종료되면 map에서 EndpointConfig를 제거한다.
    if (configs.containsKey(userSession)) {
      configs.remove(userSession);
    }
  }
  @OnError
  public void handleError(Throwable e, Session userSession) {
    e.printStackTrace();
  }
}
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 테스트를 위한 클래스
@WebServlet("/SessionIn")
public class SessionIn extends HttpServlet {
  private static final long serialVersionUID = 1L;
  public SessionIn() {
    super();
  }
  // 파라미터 param에 값이 넘겨오면 TestSession이름으로 세션에 넣는다.
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    String param = request.getParameter("param");
    if (param == null || param.isEmpty()) {
      param = "hello world";
    }
    request.getSession().setAttribute("TestSession", param);
    response.getWriter().append("Session In OK ");
  }
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    doGet(request, response);
  }
}
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 테스트를 위한 클래스
@WebServlet("/SessionOut")
public class SessionOut extends HttpServlet {
  private static final long serialVersionUID = 1L;

  public SessionOut() {
    super();
  }
  // 요청되면 TestSession이름으로 세션을 제거한다.
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    request.getSession().setAttribute("TestSession", null);
    response.getWriter().append("Session Clear ");
  }

  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    doGet(request, response);
  }
}
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  <input onclick="sendMessage()" value="Send" type="button">
  <input onclick="disconnect()" value="Disconnect" type="button">
  <br />
  <textarea id="messageTextArea" rows="10" cols="50" readonly="readonly"></textarea>
  <script type="text/javascript">
    var webSocket = new WebSocket("ws://localhost:8080/WebSocketSessionShare/websocket");
    var messageTextArea = document.getElementById("messageTextArea");

    webSocket.onopen = function(message) {
      messageTextArea.value += "Server connect...\n";
    };

    webSocket.onclose = function(message) {
      messageTextArea.value += "Server Disconnect...\n";
    };

    webSocket.onerror = function(message) {
      messageTextArea.value += "error...\n";
    };
    // 서버로부터 데이터가 오면 Textarea에 표시합니다.
    webSocket.onmessage = function(message) {
      messageTextArea.value += "Recieve From Server => " + message.data+ "\n";
    };
    //Send 버튼을 누르면 세션을 가져옵니다.
    function sendMessage() {
      messageTextArea.value += "Get Http Session!!";
      webSocket.send("Get");
    }
    function disconnect() {
      webSocket.close();
    }
  </script>
</body>
</html>

기본적인 WebSocket의 값은 이전에 작성한 WebSocket예제와 거의 동일합니다.

거기에서 HttpSessionConfigurator클래스가 추가되어서 handshake부분에서 session을 가져오는 부분과 websocket 클래스에서 각 socketSession별로 httpSession을 map에 넣어 사용하는 부분이 추가되었습니다.

먼저 index.jsp로 접속을 합니다.

「./SessionIn?param=데이터」를 통해서 세션에 값을 넣습니다.

다시 index.jsp로 돌아와서 send버튼을 누르면 전에 session에 넣었던 값이 나올 것입니다.

이번에는 「./SessionOut」를 통해서 세션 값을 초기화합니다.

다시 index.jsp로 돌아와서 send버튼을 누르면 Session값이 초기화 되었기 때문에 null나오는 것을 확인할 수 있습니다.


첨부 파일 - WebSocketSessionShare.zip


참조 링크 - https://stackoverflow.com/questions/17936440/accessing-httpsession-from-httpservletrequest-in-a-web-socket-serverendpoint


여기까지 WebSocket에서 HttpSession을 가져오는 방법에 대한 설명이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.