2016-03-08 6 views
0

Я использую thread local для хранения конкретных пользовательских запросов (например, агента браузера), которые он отлично работал на JAVA 7, но теперь после обновления до JAVA 8 В некоторых случаях я вижу запросы, поступающие из браузера android, обрабатываются так, как будто он поступает из браузера iOS, хотя он был обнаружен корректно в качестве браузера для Android, но позже при обработке запроса он был заменен другим потоком локального значения! я не уверен, что здесь не хватает, кто-нибудь может мне помочь? (до/после) обновление:Threadlocal on the tomcat misbehavior после обновления до JAVA 8

  • tomcat 8 до и после.
  • JAVA повышен с 7 до 8.
  • Спринг повышен с 4.1.7 до 4.2.5
  • Спринг безопасности повышен с 3.2.3 до 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 для получения текущего агента пользователя:

SecurityManager.manager().getAgent() 

Может кто-нибудь помочь мне выяснить причину этой проблемы, или даже альтернативу более надежный способ добиться этого?

Заранее спасибо :)

ответ

0

Для начала вашего SecurityManager несовершенен вы не должны получить экземпляр, а просто напрямую получить/установить значение на ThreadLocal с помощью static. В настоящее время вы можете столкнуться с проблемами, когда вещи загружаются в другой загрузчик классов, т. Е. Не обнаруживают синглтон.

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 в блоке try/finally в 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

Смежные вопросы