jsp 中跨选项卡的会话管理

session management across tabs in jsp

技术-- Struts2 & JSP

  1. 访问URL--http://localhost:8080/session_acroos_tab/login.action

  2. 在 Welcome.jsp 中,用户正在 'First Text Field' 中输入一些值并单击 'First button'。

  3. 单击此按钮(第一个按钮)时,将调用 LoginAction.java 的 firstButtonClick 函数。
  4. 现在控制权将再次转移到 Welcome.jsp。在此 jsp 上显示用户在 'First Text Field' 中输入的值,
  5. 用户应在 'Second Text Field' 中输入一些值并单击 'Second button'。
  6. 单击此按钮(第二个按钮)时,将调用 LoginAction.java 的 secondButtonClick 函数,控件将再次转到 Welcome.jsp 页面。

在执行第 5 步之前,如果用户打开新选项卡并从第 1 步执行到第 3 步,然后再次返回到 tab-1 并执行剩余的步骤,那么所有会话数据都会变得混乱。我知道,所有打开的新选项卡都应共享同一个会话,但解决方案是什么。我想独立使用这两个选项卡。

Welcome.jsp

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<head> 
<title>Welcome</title>
<script type="text/javascript">
function clickSecondButton(form)
{
    form.action="secondButtonClickForm2";
    form.submit(); 
}
</script>
</head>
<s:form action="firstButtonClickForm2" method="post">
    <s:if test="%{#session.HIDE_FIRST_BUTTON!=null}">
        <ul>
            <li>FIRST TEXT FIELD VALUE - <s:property value="%{#session.FIRST_TEXT_FIELD}"/></li>
        </ul>
        <s:textfield name="secondtextfield" key="label.secondtextfield" size="20"></s:textfield>
        <s:submit key="label.secondButton" align="center" onclick="clickSecondButton(this.form)"></s:submit>
    </s:if>
    <s:else>
        <s:textfield name="firsttextfield" key="label.fisttextfield" size="20"></s:textfield>
        <s:submit key="label.firstButton" align="center"></s:submit>
    </s:else>
</s:form>

LoginAction.java

package net.patel.struts2;
import java.util.Map;
import com.opensymphony.xwork2.ActionContext;

public class LoginAction {
  private String username;
  private String password;
  private String firsttextfield;
  private String secondtextfield;    

  public String execute() {
    Map session = (Map) ActionContext.getContext().getSession();
    session.clear();
    if (this.username==null)
        return "success";

    if (this.username.equals("admin") 
            && this.password.equals("admin123")) {
        return "success";
    } else {
        return "error";
    }
  }

  public String firstButtonClick() {
  System.out.println("{firstButtonClick} "+firsttextfield);
  Map session = (Map) ActionContext.getContext().getSession();
  session.put("HIDE_FIRST_BUTTON", "Y");
  //session.put("FIRST_TEXT_FIELD", firsttextfield);
  return "success";
  }

  public String secondButtonClick() {
  Map session = (Map) ActionContext.getContext().getSession();
  System.out.println("{secondButtonClick} firsttextfield-"+session.get("FIRST_TEXT_FIELD")+" II TF-"+secondtextfield);

  return "success";
  }

  public String getUsername() {
      return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public String getFirsttextfield() {
    return firsttextfield;
  }

  public void setFirsttextfield(String firsttextfield) {
    this.firsttextfield = firsttextfield;
  }

  public String getSecondtextfield() {
    return secondtextfield;
  }

  public void setSecondtextfield(String secondtextfield) {
    this.secondtextfield = secondtextfield;
  }

}

struts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <constant name="struts.enable.DynamicMethodInvocation" value="false" />
    <constant name="struts.devMode" value="true" />
    <constant name="struts.custom.i18n.resources" value="ApplicationResources"></constant>
    <package name="default" extends="struts-default" namespace="/">
    <action name="login" class="net.viralpatel.struts2.LoginAction">
        <result name="success">Welcome.jsp</result>
        <result name="error">Login.jsp</result>
    </action>    
    <action name="*Form2" class="net.viralpatel.struts2.LoginAction" method="{1}">
        <result name="success">Welcome.jsp</result>
        <result name="error">Login.jsp</result>
    </action>    
    </package>
  </struts>

ApplicationResources.properties

 label.username= Username
 label.password= Password
 label.login= Login 
 label.fisttextfield=First Text Field
 label.firstButton=First Button
 label.secondtextfield=Second Text Field
 label.secondButton=Second Button

尽量说得更清楚,我对这个问题缺乏想法,即使它听起来很经典:我花了几个小时尝试解决这个问题,但得到了 nowhere.Your 帮助很多赞赏。

您可以使用 HTML5 SessionStorage (window.sessionStorage)。您将生成一个随机 ID 并保存在每个浏览器选项卡的会话存储中。然后每个浏览器选项卡都有自己的Id。

"Data stored using sessionStorage do not persist across browser tabs, even if two tabs both contain webpages from the same domain origin. In other words, data inside sessionStorage is confined to not just the domain and directory of the invoking page, but the browser tab in which the page is contained in. Contrast that to session cookies, which do persist data from tab to tab."

你永远需要一个令牌。它可以生成和维护 server-side (Java) 和 client-side (HTML5 sessionStorage)。

然后它可以用于将某个page/tab固定到一组特定的会话属性,通过参数化它们。

服务器端方式的一个示例是在由所有其他方式扩展的 BaseAction 中生成令牌。最好在拦截器中执行此操作,但这取决于您。

在这种情况下,因为它不是安全令牌(例如,对抗 XSRF 的令牌,需要不可预测),您可以使用当前时间作为令牌。

例如服务器端令牌:

public abstract class BaseAction extends ActionSupport 
                                  implements SessionAware {

    private Map<String,Object>  session;       
    private String token;

    public void setSession(Map<String,Object> session) {
        this.session = session;
    }
    public void setToken(String token) {
        // Retrieve the Token from the page, if any
        this.token = token;
    }
    public String getToken() {
        if (token==null){
            // Generates a new Token, only if it has not been passed from the JSP
            token = System.currentTimeMillis();
        }       
        return token;
    }
    public void setTokenSessionAttribute(String k, Object v){
        session.putSessionAttribute(getToken() + "_" + k,v);
    }
    public Object getTokenSessionAttribute(String k){
        return session.getSessionAttribute(getToken() + "_" + k);
    }


    public String execute(){
        return SUCCESS;
    }

    public String firstButtonClick() {
        setTokenSessionAttribute("FIRST_TEXT_FIELD", firstTextField);
        return SUCCESS;
    }

    public String secondButtonClick() {
        log.debug("Saved value was: " + getTokenSessionAttribute("FIRST_TEXT_FIELD"))
        return SUCCESS;
    }
    private String firstTextField; // with setter

}

在您的 JSP 中:

<s:form ... >
    <!-- stuff -->
    <s:hidden name="token" />
    <!-- stuff -->
    <s:if test="%{#session[token+'HIDE_FIRST_BUTTON']!=null}">
        <!-- stuff -->
    <!-- stuff -->
</s:form>

这是众多方法中的一种,例如,您可以在返回正确的参数化会话属性的操作中创建一个方法:

public Object getTabSessionObj(String k){
    return session.get(token + "_" + k);
}
<s:if test="%{getTabSessionObj('HIDE_FIRST_BUTTON')!=null}">