【问题标题】:Apache Shiro getSubject in REST API's @Singleton EJB beanREST API 的 @Singleton EJB bean 中的 Apache Shiro getSubject
【发布时间】:2014-10-13 22:07:48
【问题描述】:

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

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

在我调用的自定义身份验证过滤器中

SecurityUtils.getSubject().login(bearerToken);

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

我有一个 Dao,我想使用存储在 Shiro 主题主体信息中的用户 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() 的调用会返回正确的用户还是可以将它们混合在一起?如何在执行登录机制时检索用户绑定到的单例中的线程上下文?

【问题讨论】:

    标签: java rest thread-safety ejb shiro


    【解决方案1】:

    你可以简单地在你的单例 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 来存储当前用户/登录会话。

    如果您想以某种方式访问​​该对象(在大多数情况下,您不需要),它有一堆静态公共方法,您可以直接调用来设置/获取主题或当前线程的安全管理器等内容.

    【讨论】:

    • 感谢您的回答!我猜我的问题不清楚,我更新了它。请检查一下,因为该解决方案似乎与您刚刚提出的类似。
    • 在答案中添加了关于您的附加问题的信息,它回答了吗?
    • 所以基本上我应该保持原样?
    猜你喜欢
    • 2013-05-24
    • 2017-09-04
    • 2017-04-13
    • 1970-01-01
    • 1970-01-01
    • 2012-08-03
    • 2020-07-02
    • 1970-01-01
    • 2014-04-24
    相关资源
    最近更新 更多