升级到JAVA 8后tomcat错误行为的Threadlocal
我使用thread local来存储用户请求特定的功能(例如浏览器代理),它在JAVA 7上正常工作,但是现在在升级到JAVA 8之后,在某些情况下,我看到来自android浏览器的请求被当作来自iOS浏览器一样处理,尽管它被正确地检测为android浏览器,但后来在处理请求时,它被另一个线程本地值替换了!我不确定我在这里遗漏了什么有人能帮我吗?我的环境设置(升级之前/之后)为:升级到JAVA 8后tomcat错误行为的Threadlocal,java,spring,tomcat,thread-local,Java,Spring,Tomcat,Thread Local,我使用thread local来存储用户请求特定的功能(例如浏览器代理),它在JAVA 7上正常工作,但是现在在升级到JAVA 8之后,在某些情况下,我看到来自android浏览器的请求被当作来自iOS浏览器一样处理,尽管它被正确地检测为android浏览器,但后来在处理请求时,它被另一个线程本地值替换了!我不确定我在这里遗漏了什么有人能帮我吗?我的环境设置(升级之前/之后)为: Tomcat8前后 JAVA从7升级到8 Spring从4.1.7升级到4.2.5 Spring security
- Tomcat8前后
- JAVA从7升级到8
- Spring从4.1.7升级到4.2.5
- Spring security从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);
}
}
安全管理器如下所示:
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
存在缺陷。您不应该获取实例,而只需使用static
直接获取/设置ThreadLocal
上的值。当前,当在不同的类加载器中加载东西时,即没有检测到单例时,您可能会遇到问题
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);
}
}
另外,与扩展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();
}
}
}
这也是Spring安全性的工作方式,例如Spring事务管理(使用静态方法和shared
ThreadLocal
) 首先,您的SecurityManager
存在缺陷。您不应该获取实例,而应该使用static
直接获取/设置ThreadLocal
上的值。当前,当在不同的类加载器中加载东西时,即没有检测到单例时,您可能会遇到问题
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);
}
}
另外,与扩展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();
}
}
}
这也是Spring安全性的工作方式,例如Spring事务管理(使用静态方法和shared
ThreadLocal
) 非常感谢你,我将尝试它,并张贴结果回来!我花了我的时间来测试和监控它,这个解决方案工作得很好:)谢谢!非常感谢你,我将尝试它,并张贴结果回来!我花了我的时间来测试和监控它,这个解决方案工作得很好:)谢谢!