Apache Shiro getSubject在REST API的@Singleton EJB bean中

时间:2014-08-20 13:54:52

标签: java rest thread-safety ejb shiro

我正在使用Java EE 6和Shiro for REST后端开发一个Web应用程序,这可以通过基于Angular的前端访问。

在shiro中禁用了会话创建(shiro.ini中的noSessionCreation),我根据他/她提供登录凭据后收到的令牌,对每个请求进行身份验证。

在自定义验证过滤器中,我正在调用

SecurityUtils.getSubject().login(bearerToken);

调用自定义身份验证领域并验证凭据。

我有一个Dao,我想使用存储在Shiro Subject的主要信息中的用户ID。

final UserInfo userInfo = (UserInfo) SecurityUtils.getSubject().getPrincipal();
final Long currentUserId = userInfo.getId();

Dao本身是一个@Singleton EJB

@Singleton
public class TaskDao {
    ...
    public List<Task> filterActiveTasks() {
    }
}

在这种情况下,我想过滤属于访问该服务的用户的活动任务。

同时我的问题和疑虑:

这个线程安全吗?在并发使用时,受试者可能会混淆不清吗?

如果是这样,怎么可以避免?

修改

我关注的不仅仅是activeTasks,还适用于我想使用SecurityUtils.getSubject()静态方法的任何@Singleton注释类。

public List<SomeData> getSomeDataRelatedToCurrentlyLoggedInUser() {
    // Since the following is a static call in a singleton, how can I be sure
    // that when 10 users are calling this at the same time, each call will get
    // the appropriate UserInfo that was "logged-in" at the AuthenticatingFilter level
    final UserInfo userInfo = (UserInfo) SecurityUtils.getSubject().getPrincipal();
    Long currentUserId = userInfo.getId();

    return em.createNamedQuery("select s from SomeData s where s.userId = :userId").
        setParameter("userId", currentUserId).
        getResultList();

}

登录部分在自定义AuthenticatingFilter中完成,为每个用户创建-AFAIK.

所以问题仍然存在:对SecurityUtils.getSubject()的调用是否会返回正确的用户,还是可以将它们混合起来?如何在执行登录机制时检索用户绑定的单例中的threadcontext?

1 个答案:

答案 0 :(得分:0)

您可以在单例ejb中简单地引入ThreadLocal变量。

这样,您只需将与当前线程/用户相关的内容放入变量中。例如:

@Singleton
public class TaskDao {

    private static ThreadLocal<Set<ActiveTasks>> tasksThreadLocal = new ThreadLocal<>(){
        @Override
        protected Set<ActiveTasks> initialValue() {
            return new HashSet<>();
        }
    };

    filterActiveTasks() {
         Set<ActiveTasks> tasks = tasksThreadLocal.get();
         //work with the Set
    }
}

这样,集合对每个线程都是本地的,不会出现多线程问题。

对于SecurityUtils.getSubject(),此方法在内部使用相同的机制,因此它始终只返回当前线程上的用户,从而返回登录到当前请求的用户。它使用ThreadContext类,它使用ThreadLocal存储当前用户/登录会话。

如果你想以某种方式访问​​该对象(在大多数情况下,你没有)它有一堆静态公共方法,你可以直接调用设置/获取像主题或安全管理器的东西当前主题。