java通过sessionID获取指定session,jetty通过sessionID获取指定session,Jetty的session源码分析

写在前面

session+cookie的机制相信很多小伙伴都明白,这里就不再赘述了。

我们都知道,浏览器请求的信息中会自动将jsessionid传给服务端。

传统方式通过HttpSession session = request.getSession(); 方式就可以获取到一个HttpSession,那么我们如何能通过sessionId来获取到指定用户的session呢?

jetty环境源码分析

本文的运行web环境是jetty环境,tomcat估计同理应该大差不差。

其实,HttpServletRequest、HttpServletResponse、HttpSession不管在jetty、tomcat还是其他web容器中都有一个基本的实现类,而我们通过request.getSession()方式获取session,就是调用了org.eclipse.jetty.server.Request类的getSession()方法:

// org.eclipse.jetty.server.Request#getSession()
/*
 * @see javax.servlet.http.HttpServletRequest#getSession()
 */
@Override
public HttpSession getSession()
{
    
    
    return getSession(true);
}

/*
 * @see javax.servlet.http.HttpServletRequest#getSession(boolean)
 */
@Override
public HttpSession getSession(boolean create)
{
    
    
    if (_session != null)
    {
    
    
        if (_sessionHandler != null && !_sessionHandler.isValid(_session))
            _session = null;
        else
            return _session;
    }

    if (!create)
        return null;

    if (getResponse().isCommitted())
        throw new IllegalStateException("Response is committed");

    if (_sessionHandler == null)
        throw new IllegalStateException("No SessionManager");

    _session = _sessionHandler.newHttpSession(this); // 创建session
    if (_session == null)
        throw new IllegalStateException("Create session failed");
    
    HttpCookie cookie = _sessionHandler.getSessionCookie(_session, getContextPath(), isSecure());
    if (cookie != null)
        _channel.getResponse().replaceCookie(cookie);

    return _session;
}

我们可以看到jetty的源码,获取session时进行了判断,如果获取的session为空,就默认新建一个session,创建session是通过_sessionHandler创建的,而_sessionHandler是什么东西呢?

private SessionHandler _sessionHandler;
// org.eclipse.jetty.server.session.SessionHandler#newHttpSession
public HttpSession newHttpSession(HttpServletRequest request)
{
    
    
    long created = System.currentTimeMillis();
    String id = _sessionIdManager.newSessionId(request, created); // 创建sessionid
    Session session = _sessionCache.newSession(request, id, created, (_dftMaxIdleSecs > 0 ? _dftMaxIdleSecs * 1000L : -1)); // 创建新的session
    session.setExtendedId(_sessionIdManager.getExtendedId(id, request));
    session.getSessionData().setLastNode(_sessionIdManager.getWorkerName());

    try
    {
    
    
        _sessionCache.add(id, session); // 将session放入缓存
        Request baseRequest = Request.getBaseRequest(request);
        baseRequest.setSession(session);
        baseRequest.enterSession(session);
        _sessionsCreatedStats.increment();

        if (request != null && request.isSecure())
            session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE);

        callSessionCreatedListeners(session);

        return session;
    }
    catch (Exception e)
    {
    
    
        LOG.warn(e);
        return null;
    }
}

通过以上源码我们可以看出,session创建完毕后,会将session放入_sessionCache,这个_sessionCache是什么呢?

SessionCache的基本默认实现是DefaultSessionCache,里面存储的session都是用ConcurrentHashMap存储的,key是sessionid,value是session对象:

protected ConcurrentHashMap<String, Session> _sessions = new ConcurrentHashMap<>();

我们继续回到SessionHandler,看着像是session专用的处理器,发现里面有这样一个方法,通过id获取session,正是我们想要的:

// org.eclipse.jetty.server.session.SessionHandler#getSession
public Session getSession(String id)
{
    
    
    try
    {
    
    
        Session session = _sessionCache.get(id);
        if (session != null)
        {
    
    
            //If the session we got back has expired
            if (session.isExpiredAt(System.currentTimeMillis()))
            {
    
    
                //Expire the session
                try
                {
    
    
                    session.invalidate();
                }
                catch (Exception e)
                {
    
    
                    LOG.warn("Invalidating session {} found to be expired when requested", id);
                    LOG.warn(e);
                }

                return null;
            }

            session.setExtendedId(_sessionIdManager.getExtendedId(id, null));
        }
        return session;
    }
    catch (UnreadableSessionDataException e)
    {
    
    
        LOG.warn("Error loading session {}", id);
        LOG.warn(e);
        try
        {
    
    
            //tell id mgr to remove session from all other contexts
            getSessionIdManager().invalidateAll(id);
        }
        catch (Exception x)
        {
    
    
            LOG.warn("Error cross-context invalidating unreadable session {}", id);
            LOG.warn(x);
        }
        return null;
    }
    catch (Exception other)
    {
    
    
        LOG.warn(other);
        return null;
    }
}

此时我们只需要能得到这个SessionHandler,就能通过id获取我们想要的session啦!

我发现org.eclipse.jetty.server.Request有个getSessionHandler方法,正好符合我们的预期:

public SessionHandler getSessionHandler()
{
    
    
    return _sessionHandler;
}

此时大功告成!

根据sessionID获取指定Session

根据上面的分析,我们可以获取我们想要的Session了:

String sessionId = "sessionid";
if(request instanceof org.eclipse.jetty.server.Request){
    
    
    // 根据sessionId获取指定Session
    Session session = ((org.eclipse.jetty.server.Request) request).getSessionHandler().getSession(sessionId);
}

此时我们可以为所欲为了~

写在后面

cookie技术基本算是一个 过时的技术了,很多框架都是通过token+spring session的方式做session共享和前后端登录态的验证。

而spring session更是对session做了进一步的封装,使用起来更加的方便:
spring-session的使用及其原理——分布式session解决方案

猜你喜欢

转载自blog.csdn.net/A_art_xiang/article/details/128706106