2016-03-08 76 views
0

我使用線程local來存儲用戶請求的特定功能(例如瀏覽器代理),它用於在JAVA 7上正常工作,但現在升級到JAVA 8之後在某些情況下,來自android瀏覽器的請求處理,就好像它來自iOS瀏覽器一樣,即使它已被正確檢測爲android瀏覽器,但後來在處理請求時被替換爲另一個線程本地值!我不確定在這裏有什麼缺失可以幫助我嗎?我的環境設置(之前/之後)升級是:在tomcat升級到JAVA之後,Threadlocal行爲異常8

  • tomcat 8之前和之後。
  • JAVA 7〜8
  • 春天從4.1.7升級到3.2.3,從4.2.5升級
  • 春季安全升級到4.03

我有一個安全過濾器,看起來像這樣的:

import java.io.IOException; 

import javax.servlet.FilterChain; 
import javax.servlet.ServletException; 
import javax.servlet.ServletRequest; 
import javax.servlet.ServletResponse; 
import javax.servlet.http.HttpServletRequest; 

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 
import org.springframework.security.core.context.SecurityContextHolder; 
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; 
import org.springframework.web.filter.GenericFilterBean; 

public class AuthenticationTokenProcessingFilter extends GenericFilterBean { 
private final IdentityService identityService; 

public AuthenticationTokenProcessingFilter(IdentityService userService) { 
    this.identityService = userService; 
} 

@Override 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
    SecurityManager.manager().clearManager(); 
    HttpServletRequest httpRequest = this.getAsHttpRequest(request); 
    String agent = httpRequest.getHeader("User-Agent"); 
    SecurityManager.manager().setAgent(agent); 

    ... 

    chain.doFilter(request, response); 
} 

} 

和安全管理是這樣的:

import com.appseleon.platform.web.shared.CrossAppConstants; 

public class SecurityManager { 
private static SecurityManager manager; 

private final ThreadLocal<String> agentContext = new ThreadLocal<String>(); 

private SecurityManager() { 
    manager = this; 
} 

public void clearManager() { 
    agentContext.set(null); 
} 

public static SecurityManager manager() { 
    return manager; 
} 


public String getAgent() { 
    String os = agentContext.get(); 
    if (os == null) { 
     os = CrossAppConstants.DEFAULT_OS; 
    } 
    return os; 
} 

public void setAgent(String agent) { 
    System.out.println("### os detected: " + agent); 
    agentContext.set(agent); 
} 

} 

設置代理後,最後在我的代碼各個領域我所說的安全管理器來獲得當前用戶代理:

SecurityManager.manager().getAgent() 

誰能幫我找出這個問題的原因,甚至是可選的更可靠的方法來實現這一點?

感謝提前:)

回答

0

對於初學者您SecurityManager是有缺陷的,你不應該得到一個實例,但簡單地直接獲取/設置使用staticThreadLocal值。目前,當事情在不同的類加載器中加載時,可能會遇到問題,即不檢測單例。

public abstract class SecurityManager { 

    private static final ThreadLocal<String> agentContext = new ThreadLocal<String>(); 

    private SecurityManager() { } 

    public static void clearManager() { 
     agentContext.set(null); 
    } 


    public static String getAgent() { 
     String os = agentContext.get(); 
     if (os == null) { 
      os = CrossAppConstants.DEFAULT_OS; 
     } 
     return os; 
    } 

    public static void setAgent(String agent) { 
     System.out.println("### os detected: " + agent); 
     agentContext.set(agent); 
    } 

} 

然後直接調用get/set方法就可以了。

在您的過濾器中,您應該將filterChain.doFilter包裝在finallytry/finally塊中始終清除線程本地。

try { 
    chain.doFilter(request, response); 
} finally { 
    SecurityManager.clearManager(); 
} 

同樣的不是擴展GenericFilterBean你可能要延長OncePerRequestFilter這可以確保(如果你有你的邏輯一些向前特別有用)此功能僅調用一次,它僅適用於HttpServletRequest類型的請求,節省你有些代碼。

public class AuthenticationTokenProcessingFilter extends OncePerRequestFilter { 
... 

    @Override 
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse ress, FilterChain chain) throws IOException, ServletException { 

     String agent = req.getHeader("User-Agent"); 
     SecurityManager.setAgent(agent); 

     ... 
     try { 
      chain.doFilter(request, response); 
     } finally { 
      SecurityManager.clearManager(); 
     } 
    } 
} 

這也是雙向彈簧安全工程,例如溫泉事務管理(與靜態方法和共享ThreadLocal)。

+0

非常感謝你,我會嘗試一下,並將結果發回! – user1221612

+0

我花了我的時間來正確地測試和監控它,這個解決方案工作正常:) 謝謝! – user1221612

相關問題