java - 为什么在没有同步的Catalina过滤器中使用非线程安全的 "session"?

标签 java multithreading session tomcat thread-safety

this post on stackoverflow , 确定 HttpSession 不是线程安全的。具体来说,

The Developer has the responsibility for thread-safe access to the attribute objects themselves. This will protect the attribute collection inside the HttpSession object from concurrent access, eliminating the opportunity for an application to cause that collection to become corrupted.

但是,查看 implementation of Catalina's CsrfPreventionFilter ,我看到没有应用同步:

HttpSession session = req.getSession(false);

@SuppressWarnings("unchecked")
LruCache<String> nonceCache = (session == null) ? null
        : (LruCache<String>) session.getAttribute(
                Constants.CSRF_NONCE_SESSION_ATTR_NAME);


...

if (nonceCache == null) {
    nonceCache = new LruCache<String>(nonceCacheSize);
    if (session == null) {
        session = req.getSession(true);
    }
    session.setAttribute(
            Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);
}

您能否解释一下这种访问是否安全,为什么?


更新:回应Andrew下面的回答:请检查以下代码。在 ThreadDemo 的函数 process() 中,虽然 req 是一个局部变量,但它不是线程安全的,因为它持有对共享对象的引用 session

import java.util.HashMap;
import java.util.Map;

public class Main {

    public static void main(String[] args) {
        Session session = new Session();
        int len = 10;

        ThreadDemo[] t = new ThreadDemo[len];

        for (int i = 0; i < len; i++) {
            t[i] = new ThreadDemo(session);
            t[i].start();
        }
    }
}

class ThreadDemo extends Thread {
    private final Session session;

    ThreadDemo(Session session) {
        this.session = session;
    }

    @Override
    public void run() {
        Request request = new HttpRequest(session);
        process(request);
    }

    private void process(Request request) {
        HttpRequest req = (HttpRequest) request;
        Session session = req.getSession();

        if (session.getAttribute("TEST") == null)
            session.putAttribute("TEST", new Object());
        else
            session.clearAttributes();

        System.out.println(session.getAttribute("TEST") == null);
    }
}

class Session {
    private final Map<String, Object> session;

    Session() {
        session = new HashMap<>();
    }

    Object getAttribute(String attr) {
        return session.get(attr);
    }

    void putAttribute(String attr, Object o) {
        session.put(attr, o);
    }

    void clearAttributes() {
        session.clear();
    }
}

class Request {
    private final Session session;

    Request(Session session) {
        this.session = session;
    }

    Session getSession() {
        return session;
    }
}

class HttpRequest extends Request {
    HttpRequest(Session session) {
        super(session);
    }
}

最佳答案

来自source

HttpSession session = req.getSession(false);  // line 196

req 是本地的,因此是线程安全的,如果收到并发请求,上面的 session(每个线程唯一)导致 null,那么假设并发请求代表不同的用户(或至少来自单个用户的不相关请求)是相当安全的。

稍后,建立一个新 session (对每个线程都是唯一的):

session = req.getSession(true);   // line 217

此时无法让另一个并发请求与上述非空 session 相关,因为客户端尚未收到响应,因此客户端还不知道其 session ID在后续请求期间发送。

因此为刚刚创建的 session 创建 session 属性是安全的:

session.setAttribute(Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);  // line 219

然后将带有 session ID 的响应发送回客户端。下一个请求包括 session ID 所以

session = req.getSession(true);   // line 217

不会为空。同样,nonceCache 不会为空,所以整个 block

if (nonceCache == null) {
    nonceCache = new LruCache<String>(nonceCacheSize);
    if (session == null) {
        session = req.getSession(true);
    }
    session.setAttribute(
        Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);
}

将在与现有 session 相关的后续请求中被跳过。

对缓存元素的访问在嵌套的 LruCache 类第 358 和 364 行中同步。

关于java - 为什么在没有同步的Catalina过滤器中使用非线程安全的 "session"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52670908/

相关文章:

java - 为什么在线程之间共享静态变量会降低性能?

PHP session ID 重复?

java - 从任何 java 应用程序调用自己的 eclipse 插件方法

java - 迭代实例化类集合的方法调用

java - 构建期间重复输入

Python 线程和 PySimpleGUI

c++ - opencv::flann:Index knnsearch 线程安全吗?

CodeIgniter session 类在Chrome中不起作用

session - Spring Framework 3 和 session 属性

java - 什么是 Jython,它是否有用?