2017-03-20 4 views
-1

私はカスタムコンテキストクラスを使ってアプリケーション全体に値を格納する、乱雑なStruts 1アプリケーションを開発しています。基本的には、セッションスコープ変数の格納にのみ使用されます。私はこのカスタムクラスが使用される理由は、httpセッションにアクセスできない他のクラスがセッション変数を取得して設定できるようにするためだと思います。サーブレットフィルタにThreadLocaleの値が混在する

とにかく、ほとんどの場合、これはうまくいきます。カスタムコンテキストは、アクションクラスとサービスクラス全体で問題なく変数を共有するために使用されます。しかし、私はちょうど、うまくいけば、HTTPフィルタ内のこのカスタムコンテキストを使用して動作しないことを発見した!これは、ランダムに別のセッションから値を引き出すように見えます。そして、セッションによって、私は実際にスレッドを意味します。なぜなら、このカスタムコンテキストはThreadLocaleを使ってそれが汚い作業を行うからです。

が、これは、フィルタ以外の他のすべてのクラスの内部だけで結構な動作しているようですが、ここでも見

package com.zero.alpha.common; 

import java.io.Serializable; 
import java.util.Hashtable; 
import java.util.Locale; 
import java.util.Map; 

public final class CustomContext implements Serializable { 
    private static final long serialVersionUID = 400312938676062620L; 
    private static ThreadLocal<CustomContext> local = new ThreadLocal() { 
     protected CustomContext initialValue() { 
      return new CustomContext("0", "0", Locale.getDefault()); 
     } 
    }; 
    private String dscId; 
    private String sessionId; 
    private Locale locale; 
    private Map<String, Serializable> generalArea; 

    public CustomContext(String dscId, String sessionId, Locale locale) { 
     this.dscId = dscId; 
     this.sessionId = sessionId; 

     if (locale != null) { 
      this.locale = locale; 
     } else { 
      this.locale = Locale.getDefault(); 
     } 
     this.generalArea = new Hashtable(); 
    } 

    public static CustomContext get() { 
     return ((CustomContext) local.get()); 
    } 

    public static void set(CustomContext context) { 
     local.set(context); 
    } 

    public String getDscId() { 
     return this.dscId; 
    } 

    public String getSessionId() { 
     return this.sessionId; 
    } 

    public Locale getLocale() { 
     return this.locale; 
    } 

    public Serializable getGeneralArea(String key) { 
     return ((Serializable) this.generalArea.get(key)); 
    } 

    public Serializable putGeneralArea(String key, Serializable value) { 
     return ((Serializable) this.generalArea.put(key, value)); 
    } 

    public void clearGeneralArea() { 
     this.generalArea.clear(); 
    } 

    public Serializable removeGeneralArea(String key) { 
     return ((Serializable) this.generalArea.remove(key)); 
    } 
} 

してください。フィルターが壊れているところを見せてください。

カスタムコンテキストはdoFilterメソッドでユーザをつかむために使用される
package com.zero.alpha.myapp.common.filter; 

import java.io.IOException; 

import javax.servlet.Filter; 
import javax.servlet.FilterChain; 
import javax.servlet.FilterConfig; 
import javax.servlet.ServletException; 
import javax.servlet.ServletRequest; 
import javax.servlet.ServletResponse; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import javax.servlet.http.HttpSession; 

import com.zero.alpha.common.CustomContext; 
import com.zero.alpha.myapp.utility.CommonConstants; 
import com.zero.alpha.myapp.utility.CommonHelpers; 
import com.zero.alpha.myapp.UserDomain; 


public class LoginFilter implements Filter { 

    public LoginFilter() { 
    } 


    public void init(FilterConfig config) throws ServletException {} 


    public void destroy() {} 

    public void doFilter(ServletRequest request, ServletResponse response, 
      FilterChain chain) throws IOException, ServletException { 

     HttpServletRequest req = (HttpServletRequest) request; 

     // Don't use the login filter during a login or logout request 
     if (req.getServletPath().equals("/login.do") 
      || req.getServletPath().equals("/login-submit.do") 
      || req.getServletPath().equals("/logout.do")) { 
      chain.doFilter(request, response); 

     } else { 
      doFilter(req, (HttpServletResponse) response, chain); 
     } 
    } 

    protected void doFilter(HttpServletRequest request, HttpServletResponse response, 
      FilterChain chain) throws IOException, ServletException { 

     HttpSession session = request.getSession(false); 

     // This is the problem right here. Sometimes this will grab the value of a different user currently logged in 
     UserDomain user = (UserDomain) CustomContext.get() 
      .getGeneralArea(CommonConstants.ContextKey.USER_SESSION); 

     if (session == null || user == null) { 
      // Unauthorized 
      response.sendRedirect(loginPage); 
     } else { 
      // Authorized 
      session.setAttribute("userInfo", CommonHelpers.getUserDisplay(user)); 
      chain.doFilter(request, response); 
     } 
    } 
} 

、それがランダムにログインしているユーザーから別のユーザーオブジェクトを取得します。明らかに良い状況ではありません!

これが発生する唯一の時間は、ログインしている別のユーザーのアクティビティの後です。私は終日そこに座って、ユーザーAのセッションをリフレッシュし続けても問題はありません。ただし、ユーザーBとして何らかのアクションを実行した後、ユーザーAのセッションを再度リフレッシュすると、通常はスワップされます。しかし、ユーザーAのセッションをもう一度更新すると、正常に戻ります。

これは、アプリケーションが実際にリモート開発用Tomcatサーバーにデプロイされたときに非常に頻繁に発生することに気付きました。ローカルで実行しても、遠隔地に配置した場合ほど頻繁には発生しません。これは遠隔からほぼ100%発生します。

私はフィルタ内のセッション変数を調べましたが、セッション自体に問題はないようです。私は間違ったユーザーがThreadLocale変数から引き出されても、セッションIDが正しいことを確認しました。

誰でもお手伝いできますか?ありがとう。

+1

ユーザbのアクションがカスタムコンテキストインスタンスにアクセスしていないことを確認してください。 – efekctive

+0

@efekctiveユーザBのアクションは、同じ正確な方法で、ユーザAと同じカスタムコンテキストを使用します。 – zero01alpha

答えて

2

あなたの戦略は、修復できないほどの欠陥があります。サーブレットエンジンがシングルスレッドの場合を除き、同じスレッドを使用して特定のセッション内のすべての要求を処理することはできません。さらに、指定されたセッションで1つの要求を処理する同じスレッドがそのセッションの次の要求を処理するためにも使用されている場合でも、それが所属する要求を処理しないと想定するのは安全ではありませんその間に別のセッション。スレッド/セッションバインディングの保証はありません。

とにかく、明らかに見逃しているようです。セッションスコープ変数を保存する場合は、をセッションに保存します。それがセッション属性のためのものです。 HttpSession.setAttribute()およびHttpSession.getAttribute()を参照してください。これらを使用して、コンテキストオブジェクトを設定および取得できます。

+0

ありがとうございます。フォローアップとして、なぜこれがフィルターメソッド内でのみ起こっているのか、あなたは何か考えていますか? – zero01alpha

+1

@ zero01alpha、私はあなたがあなたのフィルターメソッドで*見る*だけだと推測しています。おそらくそれは、各リクエストで最初にクラックしたフィルタが、他のクラスから問題を隠す何らかのアクションを実行するためです。スレッド/セッションバインディングはありませんが、各要求を処理する間、* request/sessionバインディングがあります。 –

+1

@ zero01alphaでは、[SessionListener](http://docs.oracle.com/javaee/6/api/javax/servlet/)でより便利に行うことができるフィルタで作業しているかどうかを検討することもできますhttp/HttpSessionListener.html)を使用してください。たとえば、コンテキストオブジェクトを最初に作成して新しいセッションにバインドすると便利です。そのため、フィルタで処理する必要があるかどうかを考慮する必要がありません。 –

関連する問題