【问题标题】:troubles with HttpSession with 2 different requests带有 2 个不同请求的 HttpSession 问题
【发布时间】:2018-11-04 01:47:39
【问题描述】:

我正在开发基于 Java 的后端,但在管理用户会话时遇到了麻烦。

我想在会话中为每个用户存储一些个人信息,因此我实现了一个用于登录目的的 Servlet,如果登录成功,它会创建一个会话:

    @WebServlet("/LoginUserWithPassword")
@MultipartConfig(fileSizeThreshold = 1024 * 1024 * 2, // 2MB
maxFileSize = 1024 * 1024 * 10, // 10MB
maxRequestSize = 1024 * 1024 * 50) // 50MB
public class LoginUserWithPassword extends HttpServlet {
    private static final long serialVersionUID = 1L;
    static Logger log = Logger.getLogger(LoginUserWithPassword.class);


    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");


....
        HttpSession session = request.getSession();
        session.getServletContext().getContext("/{applicationContextRoot}").setAttribute("nom",usr.nom);
        session.getServletContext().getContext("/{applicationContextRoot}").setAttribute("prenom", usr.prenom);
        session.getServletContext().getContext("/{applicationContextRoot}").setAttribute("login", usr.email);
        session.getServletContext().getContext("/{applicationContextRoot}").setAttribute("id", String.valueOf(usr.id_user));
        session.getServletContext().getContext("/{applicationContextRoot}").setAttribute("id_right",String.valueOf(ur.id_right));
        session.getServletContext().getContext("/{applicationContextRoot}").setAttribute("right",url.right);
        session.getServletContext().getContext("/{applicationContextRoot}").setAttribute("session",session.getId());

然后,在客户端(JSP/Javascript),我正在检索会话信息。

考虑一个 user1,他正在使用以下 Javascript 代码在浏览器 (Chrome) 上成功登录:

Glogin = '<%= (String) (request.getSession().getServletContext().getContext("/{applicationContextRoot}").getAttribute("login")) %>';
        Gsession = '<%= (String) (request.getSession().getServletContext().getContext("/{applicationContextRoot}").getAttribute("session")) %>';

        console.log("login from session: "+Glogin);
        console.log("Session ID: "+Gsession);

我可以看到以下控制台日志:

login from session: admin3@toto.com
Session ID: 7D6638EA7167580F4C1BD4D51FAD3C9C

然后我在使用另一个浏览器 (FF) 的同一台计算机上执行 user2 的第二次登录,我在控制台日志中有以下内容:

login from session: admin@toto.com
Session ID: 376C57F6ACB08CD3B66AB8406DB72984

在那个阶段一切都很完美,我可以在每个会话中检索我各自的属性,但是如果我刷新用户 1 的浏览器,我会检索用户 2 的会话 ID ....并丢失我的用户 1 会话上下文。

你知道我为什么会出现这种行为吗?可能是我实现会话管理的方式不正确?

【问题讨论】:

    标签: java session servlets


    【解决方案1】:

    您没有正确使用会话。在任何地方将session.getServletContext().getContext("/{applicationContextRoot}") 替换为session。 servlet 上下文是全局的(共享的)。

    【讨论】:

    • 是的,我知道,但这是我发现的唯一解决方案来处理两个独立战争文件之间的会话共享(在同一个 tomcat 实例上),您还有其他解决方案吗?
    • 确实如此。见stackoverflow.com/questions/9436736/…。将 / 添加到您的 web.xml 或使用同一答案中提到的其他方法之一。
    • 这正是我在 context.xml 中所做的,它使我可以在战争之间共享会话,但我必须使用 getServletContext 而不是直接在我的 JSP 页面上检索我的会话。 ..唯一的缺点是会话是共享的,并且不是每个用户存储一个会话
    • 对。我认为你会遇到这个问题。部署并不是真正独立的。例如,如果它们共享对象并且您重新部署一个应用程序,则可能会出现内存泄漏,因为旧类仍然从活动对象中引用。另外,如果它们依赖于同一个 JVM 内的通信,您将无法通过将一些战争文件移动到另一台服务器来进行扩展。我会推荐单个部署单元或迁移到完整的分布式会话实现,例如带有 Redis 后端的 Spring Session。无论如何,祝你好运!
    • 我认为你是对的,我肯定会遇到其他麻烦,所以我会回到更“标准”的实现......
    【解决方案2】:

    不要把它放在ServletContext中,它会覆盖之前的值。而是将其放在当前会话中,如下所示:

    request.getSession().setAttribute("nom",usr.nom);
    

    并从会话中检索 JSP 上的这些值,例如

    (String)session.getAttribute("nom");
    

    [编辑]

    目前你正在关注像

    这样的架构

    Browser(CLient) ---> 在 War 1 上调用 JSP -----> 在 War 2 上调用 Servlet

    您可以在浏览器(客户端)之间维护会话---->在战争 1 上调用 JSP

    当您验证您的用户时,用户的详细信息来自战争 2,用户的详细信息可以捕获为管道符号分隔在字符串中,字符串可以保存在 ServletContext 中(注意仅在战争 2 中执行此代码)

        Map<String. String> allUsers = new HashMap<>();
    // add key as user id and value is comma separated in string
        ServletContext servletContext =request.getSession().getServletContext().getContext("contextPath")
        servletContext.setAttribute("users",allUsers );
    

    您的 servlet 应将用户 ID 返回给 JSP 以作为响应

    war 2 ----> 在 war 1 中将用户 ID 返回给 JSP

    现在您应该将此用户 ID 保存在您在 Browser (CLient) -----> JSP on war 1 之间维护的会话中。

    当你想在servlet上下文中使用你在war 2中存储的值时,你可以在war 1的JSP中使用下面的代码

    ServletContext servletContext =request.getSession().getServletContext().getContext("contextPath")
    servletContext.getAttribute("user"); 
    

    注意:你不能在两个war文件之间共享会话,但是你可以在它们之间共享对象。

    希望这会对你有所帮助。

    【讨论】:

    • 是的,但我将前端和后端部署在两个独立的 war 文件中(在同一个 tomcat 实例上),我发现管理两个 war 文件之间共享的会话的唯一解决方案是使用 ServletContext。 ...如果我只使用会话,我将无法共享它。还有其他解决方案吗?
    • 为什么你在两个不同的war文件中有前端(JSP)和后端(Servlet)。由于 JSP 在编译后被转换为 servlet,因此您应该在同一个 war 文件中包含 JSP 和 Servlet。 你的JSP 表单提交怎么可能调用驻留在不同war 文件中的servlet。您是否在表单提交中使用完整 URL
    • 我有不同的前端(JSP+JS),具有不同的外观和功能,依赖于相同的后端......但这是真的,我在会话共享方面遇到了麻烦...... .
    • 我已经用编辑标签提供了答案,希望对你有帮助
    • 这可能是一个很好的解决方案,我会实施并通知您。
    猜你喜欢
    • 2013-08-17
    • 1970-01-01
    • 1970-01-01
    • 2014-04-15
    • 2011-02-25
    • 1970-01-01
    • 2013-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多