4

Spring、hibernate、struts2 フレームワークを使用して、IIS7.5 の背後で Tomcat 6.29 を使用しています。特に 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 インセプター コード

インターセプター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

レガシープロジェクト(私のものではない)で2日前に同様のものをデバッグしました。

カスタムインターセプターのせいであることが判明しました。

スタックに表示されるカスタム インターセプターを確認してください。

        <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と、そのセッションで共有オブジェクトが設定されます。User1 がまだ終了していないときに User2 がメソッドに入ると、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 Interceptor 内からリクエストにアクセスする正しい方法は次のとおりです。

// 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 に答える