如何实现session共享

本文介绍在同一Tomcat服务器的不同项目间实现Session共享的方法,包括配置上下文、管理Session生命周期、利用Cookie传递Session信息等步骤,适用于需要跨项目保持登录状态的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 同一tomcat不同项目如何实现session共享

需求:
现A、B两个项目,运行于同一tomcat下,要求在A项目下登录后,B项目中同样获取登录权限,支持同一用户重复登录。

分析:
即要实现A、B项目的session共享。
如何共享?

  • A项目session创建后、B项目则不创建新的session;
  • B项目能实时获取A项目的session;
  • 需要有一块共用空间保存A项目的session。

思路:

  • A项目中进行session管理,即所有的登录操作均在A中进行;
  • 使用cookie传递session信息给客户端,避免创建多个session;
  • 维护共用空间中的session的生命周期。

技术支持:

  • 每一个web应用程序都有唯一一个ServletContext实例对象,被该web应用下面的每一个servlet共享。tomcat支持不同项目的ServletContext实例共享,如此就可以让ServletContext对象充当储存session的公共空间,而不需要序列化或数据库存储,从而节约资源;

  • 服务器一般采用session机制实现登录,当通过getSession()获取session后服务端会将此session的id作为JSESSIONID值保存在cookie中返回给客户端,客户端通过JSESSIONID来与服务器的session匹配来找到唯一的用户。sessioon保存于服务端,cookie保存于客户端。当用户于A项目登录后我们需要创建一个全局的cookie记录A项目的session id,以便在访问B项目时获取全局cookie的session id来找到A项目中对应的session,从而完成session共享。

实现:
1.不同项目ServletContext共享

修改tomcat的service.xml文件

<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
    <Context docBase="BudgetSystem" path="/BudgetSystem" reloadable="true" source="org.eclipse.jst.jee.server:BudgetSystem"/></Host>
    <Context docBase="mate" path="/mate" reloadable="true" crossContext="true" source="org.eclipse.jst.jee.server:mate"/>
</Host>

此处为了简便

  • BudgetSystem充当A项目
  • mate充当B项目

其中项目mate配置了crossContext=”true”属性,即代表可在此项目中可调用另一个WEB应用的ServletContext对象

2.管理A项目session的生命周期

web.xml配置session监听器

<listener>
    <listener-class>com.alone.interceptor.listener.SessionListener</listener-class>
</listener>

复制一份session存进内存中,手动来管理session的生命周期

/**
 * session监听器
 * 
 * @author alone
 *
 */
public class SessionListener implements HttpSessionListener {

    private static final Logger logger = Logger.getLogger(SessionListener.class);
    private static Map<String, HttpSession> sessionMap = new HashMap<String, HttpSession>();

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        //将session在本地复制一份保存进map
        HttpSession session = event.getSession();
        sessionMap.put(session.getId(), session);
        ServletContext context = session.getServletContext();
        context.setAttribute("sessionMap", sessionMap);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        HttpSession session = event.getSession();
        //map中清除session
        sessionMap.remove(session.getId());
    }
}

3.A项目中进行登录,并保存信息进session

在项目A中获取session,并存入一些测试属性,当并将此session id以键值对形式存入cookie中,创建一个作用域全局的cookie(1.此处使用其他key均可,使用JSESSIONID是为了访问B项目后在访问A项目getSession时不需要重新创建session,节省资源,2.设置全局是为了跨项目也能访问到)

/**
 * 同一tomcat不同项目session共享
 */
public void shareSessionA() {
    HttpSession session = getSession();
    System.out.println("A项目登录完成!" + session.getAttribute("test"));
    session.setAttribute("test", "test data");
    Cookie cookie = new Cookie("JSESSIONID", getSession().getId());
    cookie.setPath("/");
    cookie.setHttpOnly(true);
    getResponse().addCookie(cookie);

    renderText("A项目使用完毕");
}

4.重写项目B中获取session的方法,使其不自动创建,而是使用A项目的session

获取A项目中的session集合,通过全局的cookie中的session id即可找到A中对应的session

@Override
public HttpSession getSession() {
    HttpServletRequest request = getRequest();
    Cookie[] cookies = request.getCookies();
    if (cookies == null) {
        return null;
    }
    ServletContext context = request.getServletContext();
    ServletContext budgetContext = context.getContext("/BudgetSystem");
    Map<String, HttpSession> sessionMap = (Map<String, HttpSession>) budgetContext.getAttribute("sessionMap");
    for (Cookie c : cookies) {
        if (c.getName().equals("JSESSIONID")) {
            return sessionMap.get(c.getValue());
        }
    }
    return null;
}

5.项目B中获取session,操作其中属性

/**
 * 同一tomcat不同项目session共享
 */
public void shareSessionB() {
    HttpSession session = getSession();
    if (session == null) {
        System.out.println("项目B检测到未登录");
    } else {
        System.out.println("项目B检测到登录");
        System.out.println("session中值为:" + session.getAttribute("test"));
        session.setAttribute("test", "数据被修改");
    }
    renderText("B项目使用完毕");
}

6.查看结果

A调用完成后客户端会存在2个cookie,一个自动创建,一个人创建

这里写图片描述

访问B项目时即可携带全局的cookie

这里写图片描述

查看打印结果:

调用A项目

A项目登录完成!sessionId: 167C07D9EBBF8E3AA8C3DA193FB53A83,sessionAttr: null

调用B项目

项目B检测到登录
session中值为:test data

再调用A项目

A项目登录完成!sessionId: 167C07D9EBBF8E3AA8C3DA193FB53A83,sessionAttr: 数据被修改

由上可以看出,调用A,A创建了session,并存入属性值。B中获取到A的session,并获取属性值,修改属性值;再次调用A,A取得上一次的session,并获取修改后的属性。不同项目实现了共享session。

1.依此类推,假如有多个项目,可将session的管理均存放与一个项目中,其他项目只是变向的去获取此项目中的session,便于维护与管理;

2.此种情况下,可以简化操作,避免了session的序列化,使用更简便;

3.此方案遵从servlet的运行机制,极大的依赖session与cookie对应关系,所以cookie中存储的信息就尤为重要,为防止JSESSIONID泄露而绕过登录,需要采取一些安全措施,最好是使用https进行加密。

  • 不同服务器如何实现session共享

而当跨服务器环境时,则需要对session进行序列化存储,可以保存到数据库或其他的地方,进行统一管理,原理与此类似。

  • 长时间登录方案

1.最简便的就是修改session的生命周期,虽然会浪费一些资源,但处理大多数情况是足够了;

2.优化方案就将session token化,将口令存储于cookie中,根据口令获取用户登录权限。需要注意的是token的一些加密及防窃取设置。

进阶,集群下的session共享:http://blog.youkuaiyun.com/qinmengdecluntan/article/details/77532883

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值