4

我们在 IIS7.5 之后使用 Tomcat 6.29,带有 spring、hibernate 和 struts2 框架。我们现在开始注意到服务器会话变得混乱,尤其是在 Ajax 请求中。

有关问题的更多详细信息

  • 用户 1 请求 page1,用户 2 请求 page2。但是 user1 获得了 page2 服务,而 user2 获得了服务器 page1。
  • 会话 ID 也在变化,但在刷新页面时,会提供正确的页面。
  • 当用户数量很多时,问题似乎更频繁地发生。

任何指向问题根源的指针都会有所帮助,代码在用户数量较少的情况下运行良好,并且没有报告此类实例。

编辑

web.xml

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">

<display-name>bm</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/*Context.xml</param-value>
</context-param>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
  <param-name>encoding</param-name>
  <param-value>UTF-8</param-value>
</init-param>
<init-param>
  <param-name>forceEncoding</param-name>
  <param-value>true</param-value>
</init-param>
</filter>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
  <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class>
</listener>

struts.xml

<result-types>
        <result-type name="jasper" class="org.apache.struts2.views.jasperreports.JasperReportsResult"/>
        <result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult" />
    </result-types> 

    <interceptors>

        <interceptor name="sessionLoggin" class="com.inrev.bm.interceptor.IRLoggingInterceptor" />
        <interceptor name="appAccess" class="appAccessInterceptor" />

        <interceptor-stack name="newStack">
            <interceptor-ref name="exception"/>
            <interceptor-ref name="alias"/>
            <interceptor-ref name="servletConfig"/>
            <interceptor-ref name="i18n"/>
            <interceptor-ref name="prepare"/>
            <interceptor-ref name="chain"/>
            <interceptor-ref name="debugging"/>
            <interceptor-ref name="scopedModelDriven"/>
            <interceptor-ref name="modelDriven"/>
            <interceptor-ref name="fileUpload"/>
            <interceptor-ref name="checkbox"/>
            <interceptor-ref name="multiselect"/>
            <interceptor-ref name="staticParams"/>
            <interceptor-ref name="params">
                <param name="excludeParams"> dojo\..*,^struts\..*</param>
            </interceptor-ref>
            <interceptor-ref name="actionMappingParams"/>
            <interceptor-ref name="sessionLoggin"/>
            <interceptor-ref name="appAccess"/>
        </interceptor-stack>
    </interceptors>

 <default-interceptor-ref name="newStack"/>

其他信息

1)用户通过提交表单登录,登录时我们执行以下操作,

public class xxxAction extends ActionSupport implements SessionAware  
{
    public String execute()
{
      session.clear();
      if (session instanceof org.apache.struts2.dispatcher.SessionMap) 
      {
    try 
    {
       ((org.apache.struts2.dispatcher.SessionMap) session).invalidate();
    }
    catch (IllegalStateException e) {
      log.error("Session Invalidate Failed ", e);
        }

        //Authorization code happens here
        session.put("orgs", orgs);
    session.put("currentOrg", org);
    session.put("permission", adminDAO.getRolePermission(orgs.get(0).getRoleId()));
    session.put("simplyApp", simplyApp);
    session.put("user", user);

       return "login"
    }
}

2) 使用的操作系统是 windows 2008 RC2

EDIT2 INCEPTOR 代码

拦截器 1

public String intercept(ActionInvocation invocation) throws Exception 
{
    String result = null;

    String className = invocation.getAction().getClass().getName();
    Map session = invocation.getInvocationContext().getSession();

    IRUser user = (IRUser) session.get("user");
    IROrgname org = (IROrgname)session.get("currentOrg");
    IRAppDetails simplyApp = (IRAppDetails)session.get("simplyApp");
    String sessionId = (String)session.get("sessionId");
    boolean switchUser =session.get("switchUser")!=null ? (Boolean)session.get("switchUser") : false;

    if(className.indexOf("IRLoginAction")!=-1 || className.indexOf("IRContactUsAction")!=-1 
            || className.indexOf("IRIPNAction")!=-1 || className.indexOf("IRPaymentAction")!=-1 
            || className.indexOf("IRServiceAction")!=-1 || className.indexOf("IRAppBossAction") !=-1) 
    {
        result= invocation.invoke();
        session.put("PREV_CLASS_NAME", className);
    }
    else if(!(className.indexOf("IRLoginAction")!=-1) && (user !=null && org!=null))
    {
        if(!IRSessionManager.getInstance().compareSession(user.getUserId(), sessionId) && !switchUser)
        {
            session.clear();
            if (session instanceof org.apache.struts2.dispatcher.SessionMap) 
            {
                try 
                {
                    ((org.apache.struts2.dispatcher.SessionMap) session).invalidate();
                }
                catch (IllegalStateException e) 
                {
                    log.error("Session Invalidate Failed ", e);
                }
            }
            result = "sessionDuplicated";

        }
        else
        {
            result= invocation.invoke();
            session.put("PREV_CLASS_NAME", className);
        }
    }
    else if(className.indexOf("widgets") !=-1)
    {
        result= invocation.invoke();
    }
    else if(className.indexOf("ActionSupport") !=-1)
    {
        result= invocation.invoke();
    }
    else if (!(className.indexOf("IRLoginAction")!=-1) && (user ==null || org==null || simplyApp==null))
    {
        result = "sessionExpired";
    }

    return result;
}

拦截器 2

    public String intercept(ActionInvocation invocation) throws Exception 
{
    String result = null;

    HttpServletRequest request = ServletActionContext.getRequest();


    String className = invocation.getAction().getClass().getName();

    try
    {
        Map session = invocation.getInvocationContext().getSession();

        IRUser user = (IRUser) session.get("user");
        IROrgname org = (IROrgname)session.get("currentOrg");
        IRAppDetails application = (IRAppDetails)session.get("simplyApp");

        if(( user!= null && user.getAppType()!=0) &&  !(className.indexOf("IRLoginAction")!=-1))
        {
            if(hasAccess(user.getAppType(), className))
            {
                result= invocation.invoke();
            }
            else
            {
                result = "checkURL";
            }
        }
        else
        {
            result= invocation.invoke();
        }
    }
    catch (Exception e) 
    {
        e.printStackTrace();
    }

    return result;
}
4

1 回答 1

8

两天前,我在一个遗留项目(不是我的)上调试了类似的东西。

原来这是自定义拦截器的错。

检查我可以在您的堆栈中看到的自定义拦截器,

        <interceptor-ref name="sessionLoggin"/>
        <interceptor-ref name="appAccess"/>

并确保他们的代码是线程安全的(避免拦截器上的字段而不是同步所有东西,只使用局部变量)。

例如,考虑以下代码:

public abstract class ThreadUnsafeInterceptor extends AbstractInterceptor {

    private Map<String, Object> session; // <!-- Thread Unsafe

    public final String intercept(ActionInvocation invocation) throws Exception {
        session = invocation.getInvocationContext().getSession();       
        /* Do stuff */

        System.out.println(session.get("myObject"));        

        return invocation.invoke();
    }
}

这样,当 User1 进入方法时,它会设置共享session对象及其会话;如果 User2 在 User1 尚未完成时进入该方法,则 User2 将立即session用其会话覆盖该对象,并且 User1 将引用 User2 会话而不是它自己的会话。

为了使其线程安全,它应该如下所示:

public abstract class ThreadSafeInterceptor extends AbstractInterceptor {

    public final String intercept(ActionInvocation invocation) throws Exception {

        Map<String, Object> session; // <!-- Thread Safe

        session = invocation.getInvocationContext().getSession();

        /* Do stuff */

        System.out.println(session.get("myObject"));        

        return invocation.invoke();
    }
}

编辑:

您的拦截器存在一些问题:

1) 不得以这种方式访问​​请求(如此处所述

HttpServletRequest request = ServletActionContext.getRequest();

从 Struts2 拦截器中访问请求的正确方法是:

// Constants are from StrutsStatics interface
HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST); 

2)如果您不想invocation.invoke();立即返回,请注意将其分配给result字符串将触发“流”,并且在invoke()操作执行之后执行之后的行,如下所述

public String intercept(ActionInvocation invocation) throws Exception {

    String className = invocation.getAction().getClass().getName();
    long startTime = System.currentTimeMillis();
    System.out.println("Before calling action: " + className);

    String result = invocation.invoke();

    long endTime = System.currentTimeMillis();
    System.out.println("After calling action: " + className
            + " Time taken: " + (endTime - startTime) + " ms");

    return result;
}
于 2012-11-29T11:06:49.583 回答