Note of Java Servlet session

Salad Lam - Jan 31 - - Dev Community

Notice

I wrote this article and was originally published on Qiita on 18 March 2023.


API

A Java Servlet (Servlet) application uses API methods below to access session related function.

package javax.servlet.http;

public interface HttpServletRequest extends ServletRequest {
    public HttpSession getSession(boolean create);
    public HttpSession getSession();
    public String changeSessionId();
    public String getRequestedSessionId();
    public boolean isRequestedSessionIdValid();
    public boolean isRequestedSessionIdFromCookie();
    public boolean isRequestedSessionIdFromURL();
    @Deprecated
    public boolean isRequestedSessionIdFromUrl();
    ...
}
Enter fullscreen mode Exit fullscreen mode
  • These methods are implemented by Servlet container (e.g. Tomcat)
  • Session object has interface javax.servlet.http.HttpSession, and normally its lifecycle is maintainanced by Servlet container. In Tomcat it is org.apache.catalina.session.StandardSession, wrapped by org.apache.catalina.session.StandardSessionFacade

Association between HTTP connection and session object

HTTP is stateless protocol, when Servlet application needs to store something into session object, it calls following methods to retrieve it.

javax.servlet.http.HttpServletRequest.getSession();
// or
javax.servlet.http.HttpServletRequest.getSession(true);
Enter fullscreen mode Exit fullscreen mode

Servlet container instantiates session object, creates session id and associates it with HTTP connection by writing session cookie information (default identifier is JSESSIONID).

Set-Cookie: JSESSIONID=14AE061861E1F5979F28A3D5D742A850; Path=/; HttpOnly
Enter fullscreen mode Exit fullscreen mode

The implementation of Tomcat 9 is org.apache.catalina.connector.Request#doGetSession. Writing
session cookie is performed by following code

        // Creating a new session cookie based on that session
        if (session != null && trackModesIncludesCookie) {
            Cookie cookie = ApplicationSessionCookieConfig.createSessionCookie(
                    context, session.getIdInternal(), isSecure());

            response.addSessionCookieInternal(cookie);
        }
Enter fullscreen mode Exit fullscreen mode

org.apache.catalina.connector.Response#addSessionCookieInternal is called

    /**
     * Special method for adding a session cookie as we should be overriding
     * any previous.
     *
     * @param cookie The new session cookie to add the response
     */
    public void addSessionCookieInternal(final Cookie cookie) {
        if (isCommitted()) {
            return;
        }

        String name = cookie.getName();
        final String headername = "Set-Cookie";
        final String startsWith = name + "=";
        String header = generateCookieString(cookie);
        boolean set = false;
        MimeHeaders headers = getCoyoteResponse().getMimeHeaders();
        int n = headers.size();
        for (int i = 0; i < n; i++) {
            if (headers.getName(i).toString().equals(headername)) {
                if (headers.getValue(i).toString().startsWith(startsWith)) {
                    headers.getValue(i).setString(header);
                    set = true;
                }
            }
        }
        if (!set) {
            addHeader(headername, header);
        }
    }
Enter fullscreen mode Exit fullscreen mode

In next HTTP request, the session id is provided in cookie header

GET /login HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:8080/
Connection: keep-alive
Cookie: JSESSIONID=14AE061861E1F5979F28A3D5D742A850
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Enter fullscreen mode Exit fullscreen mode

The application gets saved data by retrieving session object, calls

javax.servlet.http.HttpServletRequest.getSession();
// or
javax.servlet.http.HttpServletRequest.getSession(false);
Enter fullscreen mode Exit fullscreen mode

In Tomcat 9 retrieves session object is also processed by org.apache.catalina.connector.Response#addSessionCookieInternal. Following code is for loading session object

        // Return the requested session if it exists and is valid
        Manager manager = context.getManager();
        if (manager == null) {
            return null;      // Sessions are not supported
        }
        if (requestedSessionId != null) {
            try {
                session = manager.findSession(requestedSessionId);
            } catch (IOException e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("request.session.failed", requestedSessionId, e.getMessage()), e);
                } else {
                    log.info(sm.getString("request.session.failed", requestedSessionId, e.getMessage()));
                }
                session = null;
            }
            if ((session != null) && !session.isValid()) {
                session = null;
            }
            if (session != null) {
                session.access();
                return session;
            }
        }
Enter fullscreen mode Exit fullscreen mode

Other API calls

// change session id, but session object created in Servlet container will not be destroyed
javax.servlet.http.HttpServletRequest.changeSessionId();

// Unassociate current session object
javax.servlet.http.HttpSession.invalidate();
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .