问题 多个用户登录bi系统,假设用户1有报表1的权限,用户2有报表2的权限。但是发现有时用户1此时无法访问报表1,提示信息是无权限。。。看下用户信息的设置和获取的相关代码:
保存用户信息的实体类,使用了TreadLocal保证线程安全:
public class LocalUser {
private ThreadLocal<User> localCache = new ThreadLocal<User>();
private ThreadLocal<String> localBiUser = new ThreadLocal<String>();
private static LocalUser localUser = new LocalUser();
public static LocalUser getInstance() {
return localUser;
}
public User getUser() {
return localCache.get();
}
public void setUser(User user) {
localCache.set(user);
}
public String getBiUser() {
return localBiUser.get();
}
public void setBiUser(String biUserInfo) {
localBiUser.set(biUserInfo);
}
}
用户信息的设置
public class LocalSecurityFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 处理
if ("/bi.html".equals(hsr.getServletPath())) {
String servletPath = hsr.getServletPath();
String biUserInfo = getCookieValue("bi_usertoken", (HttpServletRequest)servletRequest);
LocalUser.getInstance().setBiUser(new String(DESEncryptTools.decrypt(biUserInfo)));
LOG.info("设置用户信息:"+new String(DESEncryptTools.decrypt(biUserInfo))+ " 线程: " +Thread.currentThread().getName());
}
// 处理
}
}
用户信息的获取:
userid3 = LocalUser.getInstance().getUser().getUserId();
userName3 = LocalUser.getInstance().getUser().getUsername();
log.info("获取用户信息:"+ " 线程: " +Thread.currentThread().getName()+" "+userName3) ;
在设置用户和获取用户的地方分别加日志,然后日志内容如下:
[INFO]-[2019-08-02 14:16:10]-[LocalSecurityFilter.doFilter():96]-[http-nio-8083-exec-7] 设置用户信息:mazhen 线程: http-nio-8083-exec-7
[INFO]-[2019-08-02 14:16:12]-[BoardRoleService.getBoardData():86]-[http-nio-8083-exec-5] 获取用户信息: 线程: http-nio-8083-exec-5 null
[INFO]-[2019-08-02 14:16:13]-[BoardRoleService.getBoardData():86]-[http-nio-8083-exec-7] 获取用户信息: 线程: http-nio-8083-exec-7 马振
[INFO]-[2019-08-02 14:38:33]-[LocalSecurityFilter.doFilter():96]-[http-nio-8083-exec-7] 设置用户信息:mazhen 线程: http-nio-8083-exec-7
[INFO]-[2019-08-02 14:38:34]-[BoardRoleService.getBoardData():86]-[http-nio-8083-exec-4] 获取用户信息: 线程: http-nio-8083-exec-4 null
日志能说明两点:
1 可以看出 线程exec-7 用户信息都是马振,所以一定是使用了线程池,而且线程在重复利用
2 一次页面的面板展示会使用多线程进行多次请求,而不是一个线程处理所有.
一个页面的展示,会有如下请求:
上面用户信息保存的代码:将用户信息保存到threadLocal中,这个的确是线程安全的,但是因为访问一个页面会同时发出多个http请求,而处理多个http请求tomcat会从线程池中获取多个线程来并发处理。
所以上面用户信息的保存仅仅是保存到处理 满足"/bi.html".equals(hsr.getServletPath() 要求的线程中,别的线程并没有当前用户的信息,别的线程的用户信息会为空或者是别的用户的信息。
所以上面保存用户信息的方式完全就是错误的,不是典型的多并发导致的问题,而是对web请求认识不清,以为某个用户会使用同一个线程。
上面的解决方案是,每个请求进来通过过滤器的时候都要设置用户信息,将用户信息设置为当前的请求对应的用户