用户会话在 Tomcat 上搞混了

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

We are using Tomcat 6.29 behind IIS7.5, with spring, hibernate and struts2 framework. We are now beginning to notice that the server sessions are getting mixed up especially in Ajax request.

有关问题的更多详细信息

More details about the problem

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

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

Any pointers to the origin of the problem would help, the code runs fine with lesser number of users and no such instance is reported.

编辑

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) 用户通过提交表单登录,登录时我们执行以下操作,

1) Users are login in by submitting a form, on login we perform the following,

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

2) Os being used is windows 2008 RC2

EDIT2 INCEPTOR 代码

EDIT2 INCEPTOR CODE

拦截器 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;
}

推荐答案

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

I've debugged something similar two days ago on a legacy project (not mine).

原来是自定义拦截器的错.

It turned out it was custom Interceptors's fault.

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

Check the custom Interceptors i can see in your stack,

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

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

and ensure that their code is Thread Safe (avoid fields on interceptors instead of synchronize all your stuff, use only local variables).

例如,考虑代码:

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 覆盖 session 对象,并且 User1 将引用 User2 的 session 而不是它自己的.

This way, when User1 enters the method, it sets the shared session object with its session; if User2 enters the method while User1 has not yet finished, User2 will immediately override the session object with its session, and User1 will refer to User2 session instead of its own.

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

To make it thread safe it should be like follows:

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();
    }
}

<小时>

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

Your interceptors present some problems:

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

1) Request must not be accessed this way (as described here):

HttpServletRequest request = ServletActionContext.getRequest();

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

the right way to access the request from within an Struts2 Interceptor is:

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

2) 如果您不想立即返回 invocation.invoke();,请注意将其分配给 result 字符串将触发流程",并且 invoke() 之后的行将在 Action 执行之后执行,如此处所述:

2) If you don't want to return the invocation.invoke(); immediately, please note that assigning that to the result String will fire the "flow", and the lines after the invoke() will be executed AFTER the Action execution, as described here:

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;
}

相关文章