Spring Session和MySQL在Spring Boot中共享会话


多节点应用程序中的会话管理提出了许多挑战。当体系结构包含负载平衡器时,客户端请求可能每次都路由到不同的服务器,并且HTTP会话可能会丢失。在本教程中,我们将引导您完成多节点Spring Boot应用程序中会话共享的配置。

先决条件: 目录 会话持久性 会话持久性是一种使用应用程序层信息(例如cookie)将客户端绑定到单个服务器的技术。在本教程中,我们将借助HAProxy(一个可靠的高性能TCP / HTTP负载平衡器)实现会话持久性。

hapoxy 首先,让我们创建一个具有Okta身份验证的Web应用程序,并使用Docker Compose运行具有HAProxy负载平衡的三个节点。

使用Spring Initializr的API创建一个Maven项目。

壳 1个 卷曲https://start.spring.io/starter.zip \ 2个 -d 依赖关系= web,okta \ 3 -d groupId = com.okta.developer \ 4 -d 的artifactId = web应用\ 5 -d 名称= “Web应用程序” \ 6 -d 描述= “演示Web应用程序” \ 7 -d packageName = com.okta.developer.webapp \ 8 -d javaVersion = 11 \ 9 -o web-app.zip

解压缩项目:

壳 1个 解压缩web-app.zip -d网络应用 2个 cd网络应用

运行Okta Maven插件注册一个新帐户:

壳 1个 ./mvnw com.okta:okta-maven-plugin:注册

如果您已经注册了Okta帐户,请使用login代替register。

然后,使用Okta配置您的Spring应用程序以进行身份​​验证:

壳 1个 ./mvnw com.okta:okta-maven-plugin:spring-boot

它将为您设置一个新的OIDC应用程序,并将Okta设置写入src/main/resources/application.properties文件中。

创建GreetingController于src/main/java/com/okta/developer/webapp/controller:

爪哇

1个 包 com。okta。开发商。webapp。控制器; 2个 3 导入 组织。slf4j。记录仪; 4 导入 组织。slf4j。LoggerFactory ; 5 导入 组织。弹簧框架。安全性。核心。注解。AuthenticationPrincipal ; 6 导入 组织。弹簧框架。安全性。oauth2。核心。oidc。用户。OidcUser ; 7 导入 组织。弹簧框架。刻板印象。控制器; 8 导入 组织。弹簧框架。网页。绑定。注解。GetMapping ; 9 导入 组织。弹簧框架。网页。绑定。注解。ResponseBody ; 10 导入 组织。弹簧框架。网页。情境。要求。RequestContextHolder ; 11 12 导入 java。净。InetAddress ; 13 14 @控制器 15 公共 类 GreetingController { 16 17 私有 静态 最终 Logger logger = LoggerFactory。getLogger(GreetingController。类); 18岁 19 @GetMapping(value = “ / greeting”) 20 @ResponseBody 21岁 public String getGreeting(@AuthenticationPrincipal OidcUser oidcUser){ 22 字符串 serverUsed = “ unknown”; 23 尝试{ 24 InetAddress 主机 = InetAddress。getLocalHost(); 25 serverUsed = host。getHostName(); 26 } catch(例外 e){ 27 记录器。错误(“无法获取主机名”,e); 28岁 } 29 字符串 sessionId = RequestContextHolder。currentRequestAttributes()。getSessionId(); 30 记录器。信息(“请求由“ + serverUsed响应”); 31 返回 “ Hello” + oidcUser。getFullName()+ “,您的服务器为” + serverUsed + “,其sessionId为” + sessionId ; 32 } 33 }

使用以下命令运行该应用程序:

壳 1个 ./mvnw spring-boot:运行

http://localhost:8080在隐身窗口中转到,您应该被重定向到Okta登录页面。

Okta 如果您登录,则当您重定向回您的Spring Boot应用程序时,将收到404错误。这是预期的,因为没有控制器映射到/端点。如果需要,可以通过将以下方法添加到来解决此问题WebApplication.java。

爪哇

1个 @RestController 2个 @SpringBootApplication 3 公共 类 WebApplication { 4 5 public static void main(String [] args){ 6 SpringApplication。运行(WebApplication的。类,ARGS); 7 } 8 9 @GetMapping(“ /”) 10 公共 字符串 你好(@AuthenticationPrincipal OidcUser 用户){ 11 返回 “你好,” + 用户。getFullName(); 12 } 13 }

现在,让我们配置三个Docker容器,每个应用程序节点一个,还有一个HAProxy容器。在项目根文件夹中,创建一个docker/docker-compose.yml包含以下内容的文件:

YAML

1个 版本:'3.1' 2个 服务项目: 3 webapp1 : 4 环境: 5

- OKTA_OAUTH2_ISSUER = $ { OKTA_OAUTH2_ISSUER }

6

- OKTA_OAUTH2_CLIENT_ID = $ { OKTA_OAUTH2_CLIENT_ID }

7

- OKTA_OAUTH2_CLIENT_SECRET = $ { OKTA_OAUTH2_CLIENT_SECRET }

8 图片: webapp 9 主机名: webapp1 10 端口: 11

- 8081:8080

12 webapp2 : 13 环境: 14

- OKTA_OAUTH2_ISSUER = $ { OKTA_OAUTH2_ISSUER }

15

- OKTA_OAUTH2_CLIENT_ID = $ { OKTA_OAUTH2_CLIENT_ID }

16

- OKTA_OAUTH2_CLIENT_SECRET = $ { OKTA_OAUTH2_CLIENT_SECRET }

17 图片: webapp 18岁 主机名: webapp2 19 端口: 20

- 8082:8080

21岁 webapp3 : 22 环境: 23

- OKTA_OAUTH2_ISSUER = $ { OKTA_OAUTH2_ISSUER }

24

- OKTA_OAUTH2_CLIENT_ID = $ { OKTA_OAUTH2_CLIENT_ID }

25

- OKTA_OAUTH2_CLIENT_SECRET = $ { OKTA_OAUTH2_CLIENT_SECRET }

26 图片: webapp 27 主机名: webapp3 28岁 端口: 29

- 8083:8080

30 haproxy : 31 建立: 32 上下文:。 33 dockerfile : Dockerfile-haproxy 34 图片: my-haproxy 35 端口: 36

- 80:80

37 depends_on : 38

- “ webapp1”

39

- “ webapp2”

40

- “ webapp3”

创建一个docker/.env具有以下内容的文件:

纯文本 1个 OKTA_OAUTH2_ISSUER = {发行人} 2个 OKTA_OAUTH2_CLIENT_ID = { clientId } 3 OKTA_OAUTH2_CLIENT_SECRET = { clientSecret }

您可以在发行人,的clientId,并clientSecret中src/main/resources/application.properties,运行1563 Maven插件后。\粘贴值后,请删除发行人URL中的。另外,请确保删除值周围的花括号。

Dockerfile为HAProxy容器创建一个,docker/Dockerfile-haproxy然后添加以下内容:

Docker文件 1个 来自haproxy:2.2 2个 复制haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

在以下位置为HAProxy实例创建配置文件docker/haproxy.cfg:

纯文本

1个 全球的 2个 调试 3 守护程序 4 麦克斯康 2000 5 6 默认值 7 模式 http 8 超时 连接 5000毫秒 9 超时 客户端 50000毫秒 10 超时 服务器 50000毫秒 11 12 前端 HTTP -中 13 装订 *:80 14 default_backend 服务器 15 16 后端 服务器 17 平衡 轮循 18岁 Cookie SERVERUSED 插入 间接 nocache 19 选项 httpchk / 20 选项 重新分配 21岁 默认-服务器 检查 22 服务器 webapp1 webapp1:8080 cookie webapp1 23 服务器 webapp2 webapp2:8080 cookie webapp2 24 服务器 webapp3 webapp3:8080 cookie webapp3

我不会深入探讨如何配置HAProxy,但是请注意,在本backend servers节中,有以下选项:

balance roundrobin 将循环设置为负载平衡策略。 cookie SERVERUSED将Cookie SERVERUSED添加到响应中,指示服务器响应该请求。客户端请求将坚持到该服务器。 option redispatch 如果当前服务器失败,则将请求重新分派到其他服务器。 编辑pom.xml,将Jib Maven插件添加到该部分以创建webappDocker映像。

XML格式 1个

<插件> 2个 < groupId > com.google.cloud.tools 3 < artifactId > jib-maven-plugin 4 <版本> 2.5.2 5 <配置> 6 <至> 7 < image > webapp 8 9 10

构建webapp容器映像:

壳 1个 ./mvnw编译jib:dockerbuild

使用docker-compose启动所有服务:

壳 1个 光盘泊坞窗 2个 码头工人组成

注意:如果URISyntaxException在启动时启动,请在\中的发行者中将其删除application.properties。

在日志中看到以下行之后,HAProxy将准备就绪:

纯文本 1个 haproxy_1 | [警告] 253 /一十三万零一百四十○(6):服务器 的服务器/ webapp2的 是 UP,原因:7层 校验 通过,代码:302,检查 持续时间:5毫秒。1台 活动 服务器和 0台 备用 服务器 处于联机状态。0 会话 重新排队,0 总 的 队列。 2个 haproxy_1 | [警告] 253 / 130141(6):服务器 的服务器/ webapp3 是 UP,原因:7层 校验 通过,代码:302,检查 持续时间:4毫秒。2台 活动 服务器和 0台 备用 服务器 处于联机状态。0 会话 重新排队,0 总 的 队列。 3 haproxy_1 | [警告] 253 / 130143(6):服务器 的服务器/ webapp1 是 UP,原因:7层 校验 通过,代码:302,检查 持续时间:7毫秒。3台 活动 服务器和 0台 备用 服务器 处于联机状态。0 会话 重新排队,0 总 的 队列。

在登录到您的应用程序之前,您需要进入Okta开发人员控制台,并为添加一个Login重定向URI http://localhost/login/oauth2/code/okta。否则,下一步将出现400错误。在其中时,为添加一个注销重定向URI http://localhost。

在浏览器中,转到http://localhost/greeting。登录后,检查请求cookie SERVERUSED。一个示例值是:

纯文本 1个 Cookie:SERVERUSED = webapp3 ; JSESSIONID = 5 AF5669EA145CC86BBB08CE09FF6E505

使用以下Docker命令关闭当前节点:

壳 1个 码头工人停止docker_webapp3_1

刷新浏览器,然后等待几秒钟。检查SERVERUSEDcookie以验证HAProxy是否已将请求重新分派到另一个节点,并且sessionId已更改,这意味着旧会话丢失了。

您可以使用停止服务CTRL+C。

与Spring Session分享Session 将会话存储在单个节点中可能会影响可伸缩性。扩大规模时,活动会话将保留在原始节点中,流量不会在节点之间平均分配。同样,当一个节点发生故障时,该节点中的会话也会丢失。通过会话共享,用户会话位于所有服务器节点都可以访问的共享数据存储中。

接下来,为了使用redispatchHAProxy中的选项进行透明的故障转移,让我们使用Spring Session在节点之间添加会话共享。在本教程中,我将向您展示如何使用MySQL存储会话。

首先,将以下依赖项添加到中pom.xml:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.2.0</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>

重命名src/main/resources/application.properties为application.yml,将okta.*属性更改为YAML语法,并添加以下键/值对:

okta:
  oauth2:
    issuer: {issuer}
    client-secret: {client-secret}
    client-id: {client-id}

spring:
  session:
    jdbc:
      initialize-schema: always
  datasource:
    url: jdbc:mysql://localhost:3306/webapp
    username: root
    password: example
    driverClassName: com.mysql.cj.jdbc.Driver
    hikari:
      initializationFailTimeout: 0

logging:
  level:
    org.springframework: INFO
    com.zaxxer.hikari: DEBUG

在此示例中,您将HikariCP用于数据库连接池。该选项initializationFailTimeout设置为0,这意味着如果无法获得连接,则该池仍将启动。

您还指示Spring Session始终使用option创建架构spring.session.jdbc.initialize-schema=always

application.yml您刚创建的文件包含MySQL会话存储的默认数据源属性。由于测试运行时MySQL数据库尚未建立,因此请设置内存中的H2数据库,以使应用程序测试不会失败。

创建一个src/test/resources/application-test.yml具有以下内容的文件:

spring:
  datasource:
    url: jdbc:h2:mem:testdb
    username: sa
    password: passord
    driverClassName: org.h2.Driver

修改WebApplicationTests.java类以添加@ActiveProfiles注释:

package com.okta.developer.webapp;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
@SpringBootTest
@ActiveProfiles("test")
class WebApplicationTests {

    @Test
    void contextLoads() {
    }
}

进行修改docker/docker-compose.yml以添加数据库容器和管理应用程序以检查会话表。最终配置应如下所示:

version: '3.1'
services:
  webapp1:
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/webapp
      - OKTA_OAUTH2_ISSUER=${OKTA_OAUTH2_ISSUER
      - OKTA_OAUTH2_CLIENT_ID=${OKTA_OAUTH2_CLIENT_ID}
      - OKTA_OAUTH2_CLIENT_SECRET=${OKTA_OAUTH2_CLIENT_SECRET}
    image: webapp
    hostname: webapp1
    ports:
      - 8081:8080
    depends_on:
      - "db"
  webapp2:
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/webapp
      - OKTA_OAUTH2_ISSUER=${OKTA_OAUTH2_ISSUER}
      - OKTA_OAUTH2_CLIENT_ID=${OKTA_OAUTH2_CLIENT_ID}
      - OKTA_OAUTH2_CLIENT_SECRET=${OKTA_OAUTH2_CLIENT_SECRET}
    image: webapp
    hostname: webapp2
    ports:
      - 8082:8080
    depends_on:
      - "db"
  webapp3:
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/webapp
      - OKTA_OAUTH2_ISSUER=${OKTA_OAUTH2_ISSUER}
      - OKTA_OAUTH2_CLIENT_ID=${OKTA_OAUTH2_CLIENT_ID}
      - OKTA_OAUTH2_CLIENT_SECRET=${OKTA_OAUTH2_CLIENT_SECRET}
    image: webapp
    hostname: webapp3
    ports:
      - 8083:8080
    depends_on:
      - "db"

  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: example
      MYSQL_DATABASE: webapp
    ports:
      - 3306:3306

  adminer:
    image: adminer

    restart: always
    ports:
      - 8090:8080

  haproxy:
    build:
      context: .
      dockerfile: Dockerfile-haproxy
    image: my-haproxy
    ports:
      - 80:80
    depends_on:
      - "webapp1"
      - "webapp2"
      - "webapp3"

webapp使用以下命令删除先前的容器和先前的Docker映像:

docker-compose down
docker rmi webapp

在项目的根文件夹中,使用Maven重建webapp docker映像:

./mvnw compile jib:dockerBuild

再次(docker-compose updocker目录中)启动所有服务,并重复重新调度测试(然后使用来http://localhost/greeting关闭活动节点docker stop docker_webapp#_1)。在数据库启动之前,您可能会看到很多连接错误。

现在,更改节点后,会话应该相同。多么酷啊?!

您可以在的管理界面中查看会话数据http://localhost:8090。使用登录,root然后输入MYSQL_ROOT_PASSWORD您在中设置的值docker-compose.yml。

de57d31.png

进一步了解Spring Session和OAuth 2.0 我希望您喜欢本教程,并且可以了解会话共享技术在多节点应用程序中的优势。


原文链接:http://codingdict.com