用户会话在tomcat上混淆了

时间:2012-11-29 06:04:28

标签: java struts2 thread-safety interceptor struts2-interceptors

我们在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)正在使用的Os是windows 2008 RC2

EDIT2 收件人代码

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

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

1 个答案:

答案 0 :(得分:8)

两天前我在遗留项目(而不是我的)上调试了类似的内容。

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

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

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

并确保其代码为Thread Safe(避免拦截器上的字段而不是同步所有内容,仅使用局部变量)。

例如,考虑代码:

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)不得以这种方式访问​​请求(如here所述):

HttpServletRequest request = ServletActionContext.getRequest();

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

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

2)如果您不想立即返回invocation.invoke();,请注意将其分配给result字符串将触发“流”,以及{{1}之后的行将在Action执行后执行as described here

invoke()