我是靠谱客的博主 开朗小笼包,最近开发中收集的这篇文章主要介绍shiro中获取当前user出错,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

准确场景描述应该是:
1、在应用层使用“线程池等会缓存线程的组件”,比如Executors.newFixedThreadPool(n);在线程里进行
getUser();
2、可能A用户获取到B用户。
下面分析一下原理:
顺着shiro源码去找,获取当前用户方法SecurityUtils.getSubject();

/*ThreadContext线程上下文环境,主要靠InheritableThreadLocal保存线程变量;这里使用InheritableThreadLocal而不是普通的ThreadLocal,保证在servlet分配的每个线程,获取
到父线程的ThreadLocal变量,因为servlet分配线程也是用了线程池的*/
public static Subject getSubject() {
Subject subject = ThreadContext.getSubject();
if (subject == null) {
subject = (new Subject.Builder()).buildSubject();
//获取不到,直接bind,调用ThreadContext put方法
ThreadContext.bind(subject);
}
return subject;
}

首先聊一下ThreadLocal的作用,ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。

如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。

接下来继续看代码,进getSubject(),再进get();

public static Subject getSubject() {
return (Subject) get(SUBJECT_KEY);
}
public static Object get(Object key) {
if (log.isTraceEnabled()) {
String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
log.trace(msg);
}
Object value = getValue(key);
if ((value != null) && log.isTraceEnabled()) {
String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
log.trace(msg);
}
return value;
}
//resources是private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
private static Object getValue(Object key) {
return resources.get().get(key);
}
//getValue和put都是操作resources而已
public static void put(Object key, Object value) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
if (value == null) {
remove(key);
return;
}
resources.get().put(key, value);
if (log.isTraceEnabled()) {
String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" +
key + "] to thread " + "[" + Thread.currentThread().getName() + "]";
log.trace(msg);
}
}

造成这个问题的原因是什么呢?如何让任务之间使用缓存的线程不受影响呢?实际原因是,我们的线程在执行完毕的时候并没有清除ThreadLocal中的值,导致后面的任务重用现在的localMap。

解决办法由2个
1、在程序中使用多线程的情况下,子线程无法获取上层线程的shiro相关变量,当前用户需要在外层设置进去,比如我的程序里是:user作为子线程成员变量,在子线程中不再执行shiro的getUser()获取当前用户
disPatchCompletionService.submit(new DisPatchCallable(crmCase, assign, user, dispatchType));
2、如果我们能够,在使用完这个线程的时候清除所有的localMap,在submit新任务的时候在重新重父线程中copy所有的Entry。然后重新给当前线程的t.inhertableThreadLocal赋值。这样就能够解决在线程池中每一个新的任务都能够获得父线程中ThreadLocal中的值而不受其他任务的影响,因为在生命周期完成的时候会自动clear所有的数据。Alibaba的一个库解决了这个问题github:alibaba/transmittable-thread-local

最后

以上就是开朗小笼包为你收集整理的shiro中获取当前user出错的全部内容,希望文章能够帮你解决shiro中获取当前user出错所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(33)

评论列表共有 0 条评论

立即
投稿
返回
顶部