在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/