Jetty 会话处理 - 持久化、缓存和集群
Jetty 会话处理 - 持久化、缓存和集群
2018年9月2日 • 作者:David Åse阅读时间:10-20分钟
本教程的源代码可以在 GitHub上找到。请 fork/clone 并在阅读时查看。
您将学到什么
在本教程中,我们将了解会话。我们将了解它们的用途,以及使用 Jetty 进行会话持久化、缓存和集群的不同方法。
什么是会话?
当用户访问网站(或打开 Web 应用)时,服务器通常会Session为用户创建一个对象。用户通常Session通过带有值的 Cookie与其自身关联sessionId。该Session对象可用于存储有关当前会话(例如,用户是否已登录)的信息。
Jetty 9.4 中的会话管理架构发生了重大变化,本教程旨在帮助您快速上手。 如果您需要了解所有详细信息,可以查看 Jetty 网站上的文档。
持久会话
默认情况下,Jetty 会将所有会话信息存储在HashMap内存 (RAM) 中的 中。Jetty 服务器重启后,所有会话信息都会被清除。例如,如果您在本地主机上进行更改,或者将新版本的应用部署到云服务提供商,就可能需要重启服务器。
请注意:为了使 Jetty 能够成功持久化您的会话,会话属性中的所有对象都必须实现该Serializable接口。这通常就像添加implements Serializable到您的类中一样简单。
持久化到文件系统
持久化 a 最简单的方法Session是将其作为文件存储Session在文件系统中。这可以使用 a 来实现FileSessionDataStore。
这种方法非常适合开发环境,因为它易于设置且没有依赖项。您需要创建一个SessionHandler带有 的文件夹SessionCache,并附加一个FileSessionDataStore:
public static SessionHandler fileSessionHandler() {
SessionHandler sessionHandler = new SessionHandler();
SessionCache sessionCache = new DefaultSessionCache(sessionHandler);
sessionCache.setSessionDataStore(fileSessionDataStore());
sessionHandler.setSessionCache(sessionCache);
sessionHandler.setHttpOnly(true);
// make additional changes to your SessionHandler here
return sessionHandler;
}
private static FileSessionDataStore fileSessionDataStore() {
FileSessionDataStore fileSessionDataStore = new FileSessionDataStore();
File baseDir = new File(System.getProperty(“java.io.tmpdir”));
File storeDir = new File(baseDir, “javalin-session-store”);
storeDir.mkdir();
fileSessionDataStore.setStoreDir(storeDir);
return fileSessionDataStore;
}
这种方法在远程服务器上也能奏效,但有些云服务提供商会在你重新部署服务时擦除所有文件,所以要小心。文件 IO 速度也可能很慢,具体取决于你的硬件。如果你想让会话更持久、更快速,可以使用数据库。
持久化到数据库
从编程角度来看,持久化到数据库和持久化到文件系统并没有太大区别。您需要使用 创建一个SessionHandler,SessionCache但您需要使用特定于数据库的数据存储区,而不是使用FileSessionDataStore。以下是使用 JDBC 的示例:
public static SessionHandler sqlSessionHandler(String driver, String url) {
SessionHandler sessionHandler = new SessionHandler();
SessionCache sessionCache = new DefaultSessionCache(sessionHandler);
sessionCache.setSessionDataStore(
jdbcDataStoreFactory(driver, url).getSessionDataStore(sessionHandler)
);
sessionHandler.setSessionCache(sessionCache);
sessionHandler.setHttpOnly(true);
// make additional changes to your SessionHandler here
return sessionHandler;
}
private static JDBCSessionDataStoreFactory jdbcDataStoreFactory(String driver, String url) {
DatabaseAdaptor databaseAdaptor = new DatabaseAdaptor();
databaseAdaptor.setDriverInfo(driver, url);
// databaseAdaptor.setDatasource(myDataSource); // you can set data source here (for connection pooling, etc)
JDBCSessionDataStoreFactory jdbcSessionDataStoreFactory = new JDBCSessionDataStoreFactory();
jdbcSessionDataStoreFactory.setDatabaseAdaptor(databaseAdaptor);
return jdbcSessionDataStoreFactory;
}
如果您想使用 MongoDB,您只需切换数据存储:
private static MongoSessionDataStoreFactory mongoDataStoreFactory(String url, String dbName, String collectionName) {
MongoSessionDataStoreFactory mongoSessionDataStoreFactory = new MongoSessionDataStoreFactory();
mongoSessionDataStoreFactory.setConnectionString(url);
mongoSessionDataStoreFactory.setDbName(dbName);
mongoSessionDataStoreFactory.setCollectionName(collectionName);
return mongoSessionDataStoreFactory;
}
Jetty 支持 JDBC、MongoDB、Inifinspan、Hazelcast 和 Google Cloud DataStore。JDBC 包含在核心 jetty-server 依赖项中,而 MongoDB 和其他依赖项则需要额外的依赖项。
如果您使用的是 SQL 数据库,Jetty 将创建一个jettysessions表。如果您使用的是 MongoDB,它将创建一个文档。它们都包含相同的数据:
{
“id”: {
“$oid”: “5b858d527d3c0f8722173292”
},
“id”: “node0j4ii2zxu01i91g5f8odup78c30”,
“accessed”: 1535479586876,
“context”: {
“00_0_0:”: {
“metadata“: {
“lastNode”: “node0”,
“lastSaved”: 1535479586879,
“version”: 78
},
“signed-in-user”: “tipsy” // custom data
}
},
“created”: 1535479122617,
“expiry”: 0,
“lastAccessed”: 1535479585053,
“maxIdle”: -1,
“valid”: true
}
出于性能和安全原因,建议使用 Jetty 独有的凭据创建一个单独的数据库(在数据库实例上)。
会话缓存和集群
由于数据库和文件系统操作相对较慢,将数据缓存在内存中可以提高应用程序的性能。在前面的两个示例中,我们都将 附加SessionCache到。Jetty中包含SessionHandler的两个实现,分别是和。SessionCacheDefaultSessionCacheNullSessionCache
默认会话缓存
我们DefaultSessionCache在之前的示例中使用了 ,它会将会话缓存在内存中。如果您的应用只有一个实例在运行,这很好用;但如果有两个实例位于负载均衡器之后,则可能会出现问题。Jetty 建议您始终将会话粘性与 结合使用DefaultSessionCache,但即使使用会话粘性,您也可能会遇到不一致的情况。如果一个实例宕机或过载,流量将被路由到另一个实例,而该实例的缓存中不会有相同的会话。这时 就派上用场了NullSessionCache。
空会话缓存
NullSessionCache实际上根本不进行任何缓存。每次需要 a 时,都会Session从 中获取SessionDataStore。这意味着所有实例共享同一个数据源,不会出现任何不一致的情况。Jetty 建议在不使用粘性会话的情况下使用此方法进行集群,但即使启用了粘性会话,这也是更安全的选择。
不使用缓存会降低性能,但如果您在同一网络上运行专用数据库,并且会话较少,则每个请求的响应时间应该约为 10 毫秒。使用外部托管的 MongoDB(例如mlab),响应时间大约为 40 毫秒。
SameSite 会话 cookie 设置
默认情况下,Jetty 使用较为宽松的 Cookie 安全设置。为了强化并缓解跨站请求伪造 ( CSRF ) 攻击,设置 Cookie 标志会很有帮助。如果Cookie 还直接或间接用于身份验证,SameSite=strict则尤其建议这样做。JSESSIONID
static SessionHandler customSessionHandler() {
final SessionHandler sessionHandler = new SessionHandler();
sessionHandler.setHttpOnly(true);
sessionHandler.setSecureRequestOnly(true);
sessionHandler.setSameSite(HttpCookie.SameSite.STRICT);
return sessionHandler;
}
概括
Jetty 中的会话处理需要SessionHandler、SessionCache和SessionDataStore
AFileSessionDataStore非常适合开发环境
DefaultSessionCache如果您只运行应用程序的一个实例,则效果很好
适用NullSessionCache于在负载均衡器后面运行的多个实例
Javalin 中的用法
由于您目前使用javalin.io,因此应该提及如何在您的 Javalin 应用中使用这些知识。由于 Javalin 依赖 Jetty 进行会话处理,因此您只需传递SessionHandler:
var app = Javalin.create(config -> {
config.jetty.modifyServletContextHandler(handler -> handler.setSessionHandler(fileSessionHandler()));
}).start(7070);
正如我们之前看到的,SessionHandler有一个,SessionCache而 又有一个SessionDataStore,因此无需进一步配置。所有会话配置都通过 Jetty 类进行。
注意:如果未设置和cookie 设置,某些浏览器(例如参见此处)最终将默认进一步将 cookie 限制为第一方访问。SameSitesecure
使用会话
会话是为已连接的客户端保持可信状态的绝佳方式。如果您使用会话数据库,则每个正在运行的实例都可以检索会话存储中存储的值。
写入值
app.get(“/write”, ctx -> {
// values written to the session will be available on all your instances if you use a session db
ctx.sessionAttribute(“my-key”, “My value”);
});
读取值
app.get(“/read”, ctx -> {
// values on the session will be available on all your instances if you use a session db
String myValue = ctx.sessionAttribute(“my-key”);
});
使会话无效
app.get(“/invalidate”, ctx -> {
// if you want to invalidate a session, jetty will clean everything up for you
ctx.req().getSession().invalidate();
});
更改会话 ID
app.get(“/change-id”, ctx -> {
// it could be wise to change the session id on login, to protect against session fixation attacks
ctx.req().changeSessionId();
});
最后编辑:Jeebiz 更新时间:2025-05-04 00:55