http-session.md 40.6 KB
Newer Older
茶陵後's avatar
茶陵後 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
# `HttpSession`积分

Spring Session 提供了与`HttpSession`的透明集成。这意味着开发人员可以使用 Spring Session 支持的实现来切换`HttpSession`实现。

## 为什么 Spring session 和`HttpSession`?

我们已经提到, Spring Session 提供了与`HttpSession`的透明集成,但是我们从中得到了什么好处呢?

* **群集会话**: Spring Session 使得在不绑定到特定于应用程序容器的解决方案的情况下支持[群集会话](#httpsession-redis)变得非常简单。

* **RESTful API**: Spring session 让在 header 中提供会话 ID 可以与[RESTful API](#httpsession-rest)一起工作

## `HttpSession`with redis

使用 Spring session with`HttpSession`是通过在使用`HttpSession`之前添加一个 Servlet 过滤器来实现的。你可以通过以下两种方式选择启用此功能:

* [基于 Java 的配置](#httpsession-redis-jc)

* [基于 XML 的配置](#httpsession-redis-xml)

### Redis 基于 Java 的配置

这一节描述了如何使用基于 Java 的配置使用 Redis 来支持`HttpSession`

|   |[HttpSession 示例](samples.html#samples)提供了如何使用 Java 配置集成 Spring Session 和`HttpSession`的工作示例,<br/>你可以在接下来的几节中阅读集成的基本步骤,但是,我们鼓励你在与自己的应用程序集成时遵循详细的 HttpSession 指南。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

####  Spring Java 配置

在添加了所需的依赖关系之后,我们就可以创建我们的 Spring 配置了。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将`HttpSession`实现替换为由 Spring Session 支持的实现。为此,添加以下 Spring 配置:

```
@EnableRedisHttpSession (1)
public class Config {

	@Bean
	public LettuceConnectionFactory connectionFactory() {
		return new LettuceConnectionFactory(); (2)
	}

}
```

|**1**|`@EnableRedisHttpSession`注释创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean,它实现了`Filter`<br/>过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。<br/>在此实例中, Spring Session 由 Redis 支持。|
|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|我们创建一个`RedisConnectionFactory`将 Spring Session 连接到 Redis 服务器。<br/>我们将连接配置为在默认端口(6379)上连接到 localhost。<br/>有关配置 Spring 数据 Redis 的更多信息,请参见[参考文献](https://docs.spring.io/spring-data/data-redis/docs/2.6.2/reference/html/)。|

#### Java Servlet 容器初始化

我们的[Spring Configuration](#httpsession-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean,它实现了`Filter``springSessionRepositoryFilter` Bean 负责用 Spring Session 支持的自定义实现替换`HttpSession`

为了让我们的`Filter`发挥其魔力, Spring 需要加载我们的`Config`类。最后,我们需要确保我们的 Servlet 容器(即 Tomcat)为每个请求使用我们的`springSessionRepositoryFilter`。幸运的是, Spring Session 提供了一个名为`AbstractHttpSessionApplicationInitializer`的实用程序类,以使这两个步骤都变得容易。以下是一个例子:

SRC/main/java/sample/initializer.java

```
public class Initializer extends AbstractHttpSessionApplicationInitializer { (1)

	public Initializer() {
		super(Config.class); (2)
	}

}
```

|   |我们类的名称(`Initializer`)并不重要。重要的是,我们扩展`AbstractHttpSessionApplicationInitializer`。|
|---|---------------------------------------------------------------------------------------------------------------------------------------|

|**1**|第一步是扩展`AbstractHttpSessionApplicationInitializer`<br/>这样做可以确保 Spring  Bean 以`springSessionRepositoryFilter`的名称在我们的 Servlet 容器中为每个请求注册。|
|-----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|`AbstractHttpSessionApplicationInitializer`还提供了一种机制,以确保 Spring 加载我们的`Config`。|

### 基于 XML 的 Redis 配置

本节介绍如何使用基于 XML 的配置使用 Redis 来支持`HttpSession`

|   |[HttpSession XML 示例](samples.html#samples)提供了如何使用 XML 配置集成 Spring Session 和`HttpSession`的工作示例,<br/>你可以在接下来的几个部分中阅读集成的基本步骤,但是,我们鼓励你在与自己的应用程序集成时遵循详细的 HttpSession XML 指南。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

####  Spring xml 配置

在添加了所需的依赖关系之后,我们就可以创建我们的 Spring 配置了。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将`HttpSession`实现替换为由 Spring Session 支持的实现。为此,添加以下 Spring 配置:

SRC/main/webapp/WEB-INF/ Spring/session.xml

```
(1)
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>

(2)
<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
```

|**1**|我们使用`<context:annotation-config/>``RedisHttpSessionConfiguration`的组合,因为 Spring Session 尚未提供 XML 名称空间支持(参见[gh-104](https://github.com/spring-projects/spring-session/issues/104))。<br/>这将创建一个名称为`springSessionRepositoryFilter`的 Spring  Bean 实现`Filter`<br/>过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。<br/>在此实例中, Spring Session 由 Redis 支持。|
|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|我们创建一个`RedisConnectionFactory`将 Spring Session 连接到 Redis 服务器。<br/>我们将连接配置为在默认端口(6379)<br/>上连接到 localhost。有关配置 Spring 数据 Redis 的更多信息,请参见[参考文献](https://docs.spring.io/spring-data/data-redis/docs/2.6.2/reference/html/)。|

#### xml Servlet 容器初始化

我们的[Spring Configuration](#httpsession-xml-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean,它实现了`Filter``springSessionRepositoryFilter` Bean 负责用 Spring Session 支持的自定义实现替换`HttpSession`

为了使我们的`Filter`发挥其魔力,我们需要指示 Spring 加载我们的`session.xml`配置。我们可以通过以下配置来实现这一点:

SRC/main/webapp/WEB-INF/web.xml

```
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		/WEB-INF/spring/session.xml
	</param-value>
</context-param>
<listener>
	<listener-class>
		org.springframework.web.context.ContextLoaderListener
	</listener-class>
</listener>
```

[`ContextLoaderListener`](https://DOCS. Spring.io/ Spring/DOCS/5.3.16/ Spring-framework-reference/core.html#context-create)读取 contextconfiglocation 并获取 session.xml 配置。

最后,我们需要确保我们的 Servlet 容器(即 Tomcat)为每个请求使用我们的`springSessionRepositoryFilter`。下面的代码片段为我们执行了最后一步:

SRC/main/webapp/WEB-INF/web.xml

```
<filter>
	<filter-name>springSessionRepositoryFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>springSessionRepositoryFilter</filter-name>
	<url-pattern>/*</url-pattern>
	<dispatcher>REQUEST</dispatcher>
	<dispatcher>ERROR</dispatcher>
</filter-mapping>
```

[`DelegatingFilterProxy`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/web/filter/delegatingfilterproxy.html)以`springSessionRepositoryFilter`的名称查找 Bean 并将其强制转换为`Filter`。对于每一个调用`DelegatingFilterProxy`的请求,都会调用`springSessionRepositoryFilter`

### HttpSession with Mongo

使用 Spring 与`HttpSession`会话是通过在使用`HttpSession`的任何之前添加一个 Servlet 过滤器来实现的。

本节介绍如何使用基于 Java 的配置使用 Mongo 返回`HttpSession`

|   |[HttpSession Mongo 示例](#samples)提供了一个工作示例,说明如何使用 Java 配置集成 Spring Session 和`HttpSession`<br/>你可以阅读下面的集成基本步骤,但鼓励你在与自己的应用程序集成时遵循详细的 HttpSession 指南。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

你所要做的就是添加以下 Spring 配置:

```
@EnableMongoHttpSession (1)
public class HttpSessionConfig {

	@Bean
	public JdkMongoSessionConverter jdkMongoSessionConverter() {
		return new JdkMongoSessionConverter(Duration.ofMinutes(30)); (2)
	}

}
```

|**1**|`@EnableMongoHttpSession`注释创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean,它实现了 filter。<br/>这个过滤器用 MongoDB 支持的 Bean 替换了默认的`HttpSession`。|
|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|将会话超时时间配置为 30 分钟。|

#### 会话序列化机制

为了能够在 MongoDB 中持久化会话对象,我们需要提供序列化/反序列化机制。

默认情况下, Spring Session  MongoDB 将使用`JdkMongoSessionConverter`

但是,只需在启动应用程序中添加以下代码,就可以切换到`JacksonMongoSessionConverter`:

```
@Bean
JacksonMongoSessionConverter mongoSessionConverter() {
    return new JacksonMongoSessionConverter();
}
```

##### JacksonMongosessionConverter

这种机制使用 Jackson 序列化到/来自 JSON 的会话对象。

通过创建以下内容 Bean:

```
@Bean
JacksonMongoSessionConverter mongoSessionConverter() {
    return new JacksonMongoSessionConverter();
}
```

…你可以从默认的(基于 JDK 的序列化)切换到使用 Jackson。

|   |如果你正在与 Spring Security 集成(通过将你的会话存储在 MongoDB 中),则该配置将<br/>注册适当的白名单组件,以便 Spring Security 能够正常工作。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

如果你想提供自定义的 Jackson 模块,你可以通过显式地注册如下所示的模块来实现这一点:

```
@Configuration
@EnableMongoHttpSession
static class Config extends BaseConfig {

	@Bean
	AbstractMongoSessionConverter mongoSessionConverter() {
		return new JacksonMongoSessionConverter(Collections.singletonList(new GeoModule()));
	}

}
```

##### JDKMongosessionConverter

`JdkMongoSessionConverter`使用标准的 Java 序列化来持久化以二进制形式映射到 MongoDB 的会话属性。但是,诸如 ID、访问时间等标准会话元素仍然被写成一个普通的 Mongo 对象,并且可以在不需要额外的努力的情况下进行读取和查询。如果没有明确的`JdkMongoSessionConverter` Bean 定义,则使用`AbstractMongoSessionConverter`

还有一个构造函数接受`Serializer``Deserializer`对象,允许你传递自定义的实现,这在你想要使用非默认的类加载器时尤其重要。

## `HttpSession`with JDBC

通过在使用`HttpSession`的任何内容之前添加一个 Servlet 过滤器,可以使用与`HttpSession`的 Spring Session 。你可以选择以下任何一种方式:

* [基于 Java 的配置](#httpsession-jdbc-jc)

* [基于 XML 的配置](#httpsession-jdbc-xml)

* [Spring Boot-based Configuration](#httpsession-jdbc-boot)

### 基于 Java 的 JDBC 配置

本节描述了在使用基于 Java 的配置时如何使用关系数据库来备份`HttpSession`

|   |[HttpSession JDBC 示例](samples.html#samples)提供了如何通过使用 Java 配置来集成 Spring Session 和`HttpSession`的工作示例,<br/>你可以在接下来的几个部分中阅读集成的基本步骤,但我们鼓励你在与自己的应用程序集成时遵循详细的 HttpSession JDBC 指南。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

####  Spring Java 配置

在添加了所需的依赖关系之后,我们就可以创建我们的 Spring 配置了。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器用 Spring Session 支持的实现替换`HttpSession`实现。为此,添加以下 Spring 配置:

```
@EnableJdbcHttpSession (1)
public class Config {

	@Bean
	public EmbeddedDatabase dataSource() {
		return new EmbeddedDatabaseBuilder() (2)
				.setType(EmbeddedDatabaseType.H2).addScript("org/springframework/session/jdbc/schema-h2.sql").build();
	}

	@Bean
	public PlatformTransactionManager transactionManager(DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource); (3)
	}

}
```

|**1**|`@EnableJdbcHttpSession`注释创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean。<br/>该 Bean 实现了`Filter`<br/>过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。<br/>在此实例中, Spring Session 是由关系数据库支持的。|
|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|我们创建了一个`dataSource`,它将 Spring Session 连接到 H2 数据库的嵌入式实例。<br/>我们通过使用 Spring Session 中包含的 SQL 脚本来配置 H2 数据库来创建数据库表。|
|**3**|我们创建一个`transactionManager`,它管理先前配置的`dataSource`的事务。|

有关如何配置与数据访问相关的问题的更多信息,请参见[Spring Framework Reference Documentation](https://docs.spring.io/spring/docs/5.3.16/spring-framework-reference/data-access.html)

#### Java Servlet 容器初始化

我们的[Spring Configuration](#httpsession-jdbc-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean,它实现了`Filter``springSessionRepositoryFilter` Bean 负责用 Spring Session 支持的自定义实现替换`HttpSession`

为了让我们的`Filter`发挥其魔力, Spring 需要加载我们的`Config`类。最后,我们需要确保我们的 Servlet 容器(即 Tomcat)为每个请求使用我们的`springSessionRepositoryFilter`。幸运的是, Spring Session 提供了一个名为`AbstractHttpSessionApplicationInitializer`的实用程序类,以使这两个步骤都变得容易。下面的示例展示了如何做到这一点:

SRC/main/java/sample/initializer.java

```
public class Initializer extends AbstractHttpSessionApplicationInitializer { (1)

	public Initializer() {
		super(Config.class); (2)
	}

}
```

|   |类的名称(初始化器)并不重要。<br/>重要的是我们扩展`AbstractHttpSessionApplicationInitializer`。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------|

|**1**|第一步是扩展`AbstractHttpSessionApplicationInitializer`<br/>这样做可以确保名为`springSessionRepositoryFilter`的 Spring  Bean 在我们的 Servlet 容器中为每个请求注册。|
|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|`AbstractHttpSessionApplicationInitializer`还提供了一种机制,以确保 Spring 加载我们的`Config`。|

#### 多个数据源

Spring Session 提供了`@SpringSessionDataSource`限定符,允许你显式地声明应该在`JdbcIndexedSessionRepository`中注入哪个`DataSource` Bean。这在应用程序上下文中存在多个`DataSource`bean 的场景中特别有用。

下面的示例展示了如何做到这一点:

config.java

```
@EnableJdbcHttpSession
public class Config {

	@Bean
	@SpringSessionDataSource (1)
	public EmbeddedDatabase firstDataSource() {
		return new EmbeddedDatabaseBuilder()
				.setType(EmbeddedDatabaseType.H2).addScript("org/springframework/session/jdbc/schema-h2.sql").build();
	}

	@Bean
	public HikariDataSource secondDataSource() {
		// ...
	}
}
```

|**1**|此限定符声明 Spring Session 将使用 FirstDataSource。|
|-----|-----------------------------------------------------------------------------|

### 基于 XML 的 JDBC 配置

本节描述了在使用基于 XML 的配置时如何使用关系数据库来备份`HttpSession`

|   |[HttpSession JDBC XML 示例](samples.html#samples)提供了如何通过使用 XML 配置来集成 Spring Session 和`HttpSession`的工作示例,<br/>你可以在接下来的几个部分中阅读集成的基本步骤,但是,我们鼓励你在与自己的应用程序集成时遵循详细的 HttpSession JDBC XML 指南。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

####  Spring xml 配置

在添加了所需的依赖关系之后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器用 Spring Session 支持的实现替换`HttpSession`实现。下面的清单显示了如何添加以下 Spring 配置:

SRC/main/webapp/WEB-INF/ Spring/session.xml

```
(1)
<context:annotation-config/>
<bean class="org.springframework.session.jdbc.config.annotation.web.http.JdbcHttpSessionConfiguration"/>

(2)
<jdbc:embedded-database id="dataSource" database-name="testdb" type="H2">
	<jdbc:script location="classpath:org/springframework/session/jdbc/schema-h2.sql"/>
</jdbc:embedded-database>

(3)
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<constructor-arg ref="dataSource"/>
</bean>
```

|**1**|我们使用`<context:annotation-config/>`的组合和`JdbcHttpSessionConfiguration`因为 Spring session 尚未提供 XML 名称空间支持(参见[gh-104](https://github.com/spring-projects/spring-session/issues/104))。<br/>这将创建一个名为`springSessionRepositoryFilter`的 Spring  Bean。<br/> Bean 实现`Filter`<br/>过滤器负责替换要由 Spring session 支持的`HttpSession`实现。<br/>在此实例中, Spring Session 由关系数据库支持。|
|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|我们创建一个`dataSource`,它将 Spring Session 连接到 H2 数据库的嵌入式实例。<br/>我们通过使用 Spring Session 中包含的 SQL 脚本配置 H2 数据库来创建数据库表。|
|**3**|我们创建一个`transactionManager`,它管理先前配置的`dataSource`的事务。|

有关如何配置与数据访问相关的问题的更多信息,请参见[Spring Framework Reference Documentation](https://docs.spring.io/spring/docs/5.3.16/spring-framework-reference/data-access.html)

#### xml Servlet 容器初始化

我们的[Spring Configuration](#httpsession-jdbc-xml-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean,它实现了`Filter``springSessionRepositoryFilter` Bean 负责用 Spring Session 支持的自定义实现替换`HttpSession`

为了使我们的`Filter`发挥其魔力,我们需要指示 Spring 加载我们的`session.xml`配置。我们的配置如下:

SRC/main/webapp/WEB-INF/web.xml

```
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		/WEB-INF/spring/session.xml
	</param-value>
</context-param>
<listener>
	<listener-class>
		org.springframework.web.context.ContextLoaderListener
	</listener-class>
</listener>
```

[`ContextLoaderListener`](https://DOCS. Spring.io/ Spring/DOCS/5.3.16/ Spring-framework-reference/core.html#context-create)读取`contextConfigLocation`并获取 session.xml 配置。

最后,我们需要确保我们的 Servlet 容器(即 Tomcat)为每个请求使用我们的`springSessionRepositoryFilter`。下面的代码片段为我们执行了最后一步:

SRC/main/webapp/WEB-INF/web.xml

```
<filter>
	<filter-name>springSessionRepositoryFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>springSessionRepositoryFilter</filter-name>
	<url-pattern>/*</url-pattern>
	<dispatcher>REQUEST</dispatcher>
	<dispatcher>ERROR</dispatcher>
</filter-mapping>
```

[`DelegatingFilterProxy`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/web/filter/delegatingfilterproxy.html)查找一个名为`springSessionRepositoryFilter`的 Bean 并将其强制转换为`Filter`。对于每个调用`DelegatingFilterProxy`的请求,都调用`springSessionRepositoryFilter`

### JDBC Spring 基于引导的配置

本节描述了在使用 Spring 引导时如何使用关系数据库来备份`HttpSession`

|   |[ HttpSession JDBC Spring Boot Sample](samples.html#samples)提供了如何使用 Spring boot 集成 Spring Session 和`HttpSession`的工作示例,<br/>你可以在接下来的几个部分中阅读集成的基本步骤,但是,我们鼓励你在与自己的应用程序集成时遵循详细的 HttpSession JDBC Spring 引导指南。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

####  Spring 引导配置

在添加了所需的依赖项之后,我们可以创建我们的 Spring 引导配置。多亏了一流的自动配置支持,在关系数据库支持下设置 Spring Session 就像向`application.properties`添加一个配置属性一样简单。下面的清单展示了如何做到这一点:

SRC/主/资源/应用程序.properties

```
spring.session.store-type=jdbc # Session store type.
```

如果在 Classpath 上存在单个 Spring Session 模块,则 Spring 引导自动使用该存储实现。如果有多个实现,则必须选择要用来存储会话的 StoreType,如上面所示。

Spring Boot 应用的配置相当于手动添加`@EnableJdbcHttpSession`注释。这将创建一个名为`springSessionRepositoryFilter`的 Spring  Bean。 Bean 实现`Filter`。过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。

你可以使用`application.properties`来进一步自定义。下面的清单展示了如何做到这一点:

SRC/主/资源/应用程序.properties

```
server.servlet.session.timeout= # Session timeout. If a duration suffix is not specified, seconds are used.
spring.session.jdbc.initialize-schema=embedded # Database schema initialization mode.
spring.session.jdbc.schema=classpath:org/springframework/session/jdbc/[email protected]@[email protected]@.sql # Path to the SQL file to use to initialize the database schema.
spring.session.jdbc.table-name=SPRING_SESSION # Name of the database table used to store sessions.
```

有关更多信息,请参见 Spring 引导文档的[Spring Session](https://docs.spring.io/spring-boot/docs/2.5.6/reference/htmlsingle/#boot-features-session)部分。

#### 配置`DataSource`

Spring 启动会自动创建`DataSource`,该会话将 Spring Session 连接到 H2 数据库的嵌入式实例。在生产环境中,你需要更新配置以指向关系数据库。例如,你可以在应用程序中包含以下内容:

SRC/主/资源/应用程序.properties

```
spring.datasource.url= # JDBC URL of the database.
spring.datasource.username= # Login username of the database.
spring.datasource.password= # Login password of the database.
```

有关更多信息,请参见 Spring 引导文档的[配置数据源](https://docs.spring.io/spring-boot/docs/2.5.6/reference/htmlsingle/#boot-features-configure-datasource)部分。

####  Servlet 容器初始化

我们的[Spring Boot Configuration](#httpsession-jdbc-boot-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean,它实现了`Filter``springSessionRepositoryFilter` Bean 负责用 Spring Session 支持的自定义实现替换`HttpSession`

为了让我们的`Filter`发挥其魔力, Spring 需要加载我们的`Config`类。最后,我们需要确保我们的 Servlet 容器(即 Tomcat)为每个请求使用我们的`springSessionRepositoryFilter`。幸运的是,Boot 为我们解决了这两个步骤。

## HttpSession with Hazelcast

使用 Spring 与`HttpSession`会话是通过在任何使用`HttpSession`的之前添加一个 Servlet 过滤器来实现的。

本节介绍如何使用基于 Java 的配置使用 HazelCast 来支持`HttpSession`

|   |[ Hazelcast Spring Sample](samples.html#samples)提供了如何通过使用 Java 配置来集成 Spring Session 和`HttpSession`的工作示例,<br/>你可以在接下来的几个部分中阅读集成的基本步骤,但是,我们鼓励你在与自己的应用程序集成时遵循详细的 Hazelcast Spring 指南。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

###  Spring 配置

在添加了所需的依赖关系之后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建 Servlet 过滤器,该过滤器用 Spring Session 支持的实现替换`HttpSession`实现。为此,添加以下 Spring 配置:

```
@EnableHazelcastHttpSession (1)
@Configuration
public class HazelcastHttpSessionConfig {

	@Bean
	public HazelcastInstance hazelcastInstance() {
		Config config = new Config();
		MapAttributeConfig attributeConfig = new MapAttributeConfig()
				.setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
				.setExtractor(PrincipalNameExtractor.class.getName());
		config.getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME) (2)
				.addMapAttributeConfig(attributeConfig).addMapIndexConfig(
						new MapIndexConfig(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
		SerializerConfig serializerConfig = new SerializerConfig();
		serializerConfig.setImplementation(new HazelcastSessionSerializer()).setTypeClass(MapSession.class);
		config.getSerializationConfig().addSerializerConfig(serializerConfig); (3)
		return Hazelcast.newHazelcastInstance(config); (4)
	}

}
```

|**1**|`@EnableHazelcastHttpSession`注释创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean,它实现了`Filter`<br/>过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。<br/>在此实例中, Spring Session 由 Hazelcast 支持。|
|-----|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|为了支持通过主名索引检索会话,需要注册一个适当的`ValueExtractor`<br/> Spring Session 为此提供了`PrincipalNameExtractor`。|
|**3**|为了有效地序列化`MapSession`对象,需要注册`HazelcastSessionSerializer`。如果未设置<br/>,HazelCast 将使用本机 Java 序列化来序列化会话。|
|**4**|我们创建一个`HazelcastInstance`将 Spring Session 连接到 Hazelcast。<br/>默认情况下,应用程序启动并连接到 Hazelcast 的嵌入式实例。<br/>有关配置 Hazelcast 的更多信息,请参见[参考文献](https://docs.hazelcast.org/docs/3.12.12/manual/html-single/index.html#hazelcast-configuration)。|

|   |如果`HazelcastSessionSerializer`是首选的,则需要在所有 Hazelcast 集群成员启动之前对其进行配置。<br/>在 Hazelcast 集群中,所有成员都应该对会话使用相同的序列化方法。此外,如果使用了 HazelCast 客户机/服务器拓扑<br/>,那么成员和客户机都必须使用相同的序列化方法。序列化器可以通过`ClientConfig``SerializerConfiguration`相同的成员来注册。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

###  Servlet 容器初始化

我们的[Spring Configuration](guides/java-security.html#security-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean,它实现了`Filter``springSessionRepositoryFilter` Bean 负责用 Spring Session 支持的自定义实现替换`HttpSession`

为了让我们的`Filter`发挥其魔力, Spring 需要加载我们的`SessionConfig`类。由于我们的应用程序已经通过使用`SecurityInitializer`类加载了 Spring 配置,因此我们可以将`SessionConfig`类添加到其中。下面的清单展示了如何做到这一点:

SRC/main/java/sample/securityinitializer.java

```
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {

	public SecurityInitializer() {
		super(SecurityConfig.class, SessionConfig.class);
	}

}
```

最后,我们需要确保我们的 Servlet 容器(即 Tomcat)为每个请求使用我们的`springSessionRepositoryFilter`。在 Spring security 的`springSecurityFilterChain`之前调用 Spring session 的`springSessionRepositoryFilter`是极其重要的。这样做可以确保 Spring 安全性使用的`HttpSession`得到 Spring Session 的支持。幸运的是, Spring Session 提供了一个名为`AbstractHttpSessionApplicationInitializer`的实用程序类,这使得这样做很容易。下面的示例展示了如何做到这一点:

SRC/main/java/sample/initializer.java

```
public class Initializer extends AbstractHttpSessionApplicationInitializer {

}
```

|   |我们类的名称(`Initializer`)并不重要。重要的是,我们扩展`AbstractHttpSessionApplicationInitializer`。|
|---|---------------------------------------------------------------------------------------------------------------------------------------|

通过扩展`AbstractHttpSessionApplicationInitializer`,我们确保在 Spring Security 的`springSecurityFilterChain`之前的每个请求都将名为`springSessionRepositoryFilter`的 Spring  Bean 注册到我们的 Servlet 容器中。

## 集成如何工作

幸运的是,`HttpSession``HttpServletRequest`(获取`HttpSession`的 API)都是接口。这意味着我们可以为这些 API 中的每一个提供我们自己的实现。

|   |本节描述 Spring Session 如何提供与`HttpSession`的透明集成。我们提供这些内容,这样你就可以了解幕后发生了什么。这个功能已经集成了,你不需要自己实现这个逻辑。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

首先,我们创建一个自定义的`HttpServletRequest`,它返回一个`HttpSession`的自定义实现。它看起来是这样的:

```
public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {

	public SessionRepositoryRequestWrapper(HttpServletRequest original) {
		super(original);
	}

	public HttpSession getSession() {
		return getSession(true);
	}

	public HttpSession getSession(boolean createNew) {
		// create an HttpSession implementation from Spring Session
	}

	// ... other methods delegate to the original HttpServletRequest ...
}
```

返回`HttpSession`的任何方法都将被重写。所有其他方法都是通过`HttpServletRequestWrapper`实现的,并委托给原始的`HttpServletRequest`实现。

我们使用一个名为`SessionRepositoryFilter`的 Servlet `Filter`替换`HttpServletRequest`实现。下面的伪代码展示了它是如何工作的:

```
public class SessionRepositoryFilter implements Filter {

	public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		SessionRepositoryRequestWrapper customRequest =
			new SessionRepositoryRequestWrapper(httpRequest);

		chain.doFilter(customRequest, response, chain);
	}

	// ...
}
```

通过将自定义`HttpServletRequest`实现传递到`FilterChain`中,我们确保在`Filter`之后调用的任何内容都使用自定义`HttpSession`实现。这突出了为什么将 Spring Session 的`SessionRepositoryFilter`放在与`HttpSession`交互的任何事物之前是很重要的。

## `HttpSession`和 RESTful API

Spring Session 可以通过让会话在头文件中提供而与 RESTful API 一起工作。

|   |[剩余样本](samples.html#samples)提供了一个工作示例,说明如何在 REST 应用程序中使用 Spring Session 来支持使用头部进行身份验证,<br/>你可以遵循下面几节中描述的集成的基本步骤,但是,我们鼓励你在与自己的应用程序集成时遵循详细的 REST 指南。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

###  Spring 配置

在添加了所需的依赖关系之后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将`HttpSession`实现替换为由 Spring Session 支持的实现。为此,添加以下 Spring 配置:

```
@Configuration
@EnableRedisHttpSession (1)
public class HttpSessionConfig {

	@Bean
	public LettuceConnectionFactory connectionFactory() {
		return new LettuceConnectionFactory(); (2)
	}

	@Bean
	public HttpSessionIdResolver httpSessionIdResolver() {
		return HeaderHttpSessionIdResolver.xAuthToken(); (3)
	}

}
```

|**1**|`@EnableRedisHttpSession`注释创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean,它实现了`Filter`<br/>过滤器负责替换要由 Spring Session 支持的`HttpSession`实现。<br/>在此实例中, Spring Session 由 Redis 支持。|
|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|我们创建一个`RedisConnectionFactory`将 Spring Session 连接到 Redis 服务器。<br/>我们将连接配置为在默认端口(6379)上连接到 localhost。<br/>有关配置 Spring 数据 Redis 的更多信息,请参见[参考文献](https://docs.spring.io/spring-data/data-redis/docs/2.6.2/reference/html/)。|
|**3**|我们定制了 Spring Session 的 HttpSession 集成,以使用 HTTP 头来传递当前会话信息,而不是 Cookie。|

###  Servlet 容器初始化

我们的[Spring Configuration](#rest-spring-configuration)创建了一个名为`springSessionRepositoryFilter`的 Spring  Bean,它实现了`Filter``springSessionRepositoryFilter` Bean 负责用 Spring Session 支持的自定义实现替换`HttpSession`

为了使我们的`Filter`发挥其魔力, Spring 需要加载我们的`Config`类。我们在我们的 Spring `MvcInitializer`中提供了配置,如下例所示:

SRC/main/java/sample/mvc/mvcinitializer.java

```
@Override
protected Class<?>[] getRootConfigClasses() {
	return new Class[] { SecurityConfig.class, HttpSessionConfig.class };
}
```

最后,我们需要确保我们的 Servlet 容器(即 Tomcat)为每个请求使用我们的`springSessionRepositoryFilter`。幸运的是, Spring Session 提供了一个名为`AbstractHttpSessionApplicationInitializer`的实用程序类,这使得这样做很容易。为此,使用默认构造函数扩展类,如下例所示:

SRC/main/java/sample/initializer.java

```
public class Initializer extends AbstractHttpSessionApplicationInitializer {

}
```

|   |我们类的名称(`Initializer`)并不重要。重要的是,我们扩展`AbstractHttpSessionApplicationInitializer`。|
|---|---------------------------------------------------------------------------------------------------------------------------------------|

## 使用`HttpSessionListener`

Spring 通过声明`SessionEventHttpSessionListenerAdapter``SessionDestroyedEvent``SessionCreatedEvent`转换为`HttpSessionEvent`,会话支持`HttpSessionListener`。要使用此支持,你需要:

* 确保你的`SessionRepository`实现支持并配置为发射`SessionDestroyedEvent``SessionCreatedEvent`

*`SessionEventHttpSessionListenerAdapter`配置为 Spring  Bean。

* 将每个`HttpSessionListener`注入`SessionEventHttpSessionListenerAdapter`

如果你使用[`HttpSession`with Redis](#HttpSession-Redis)中记录的配置支持,那么你所需要做的就是将每个`HttpSessionListener`注册为 Bean。例如,假设你希望支持 Spring Security 的并发控制,并且需要使用`HttpSessionEventPublisher`。在这种情况下,可以将`HttpSessionEventPublisher`添加为 Bean。在 Java 配置中,这可能如下所示:

```
@Configuration
@EnableRedisHttpSession
public class RedisHttpSessionConfig {

	@Bean
	public HttpSessionEventPublisher httpSessionEventPublisher() {
		return new HttpSessionEventPublisher();
	}

	// ...

}
```

在 XML 配置中,这可能如下所示:

```
<bean class="org.springframework.security.web.session.HttpSessionEventPublisher"/>
```