Spring Session mit JDBC

1. Überblick

In diesem kurzen Lernprogramm erfahren Sie, wie Sie die Spring-Session-JDBC verwenden, um Sitzungsinformationen in einer Datenbank zu speichern.

Zu Demonstrationszwecken verwenden wir eine H2-Datenbank im Arbeitsspeicher.

2. Einstellmöglichkeiten

Der einfachste und schnellste Weg, unser Beispielprojekt zu erstellen, ist Spring Boot . Wir zeigen jedoch auch eine Nicht-Startmethode zum Einrichten von Dingen.

Daher müssen Sie nicht die Abschnitte 3 und 4 ausfüllen. Wählen Sie einfach eine davon aus, ob Sie Spring Boot zur Konfiguration von Spring Session verwenden.

3. Spring Boot-Konfiguration

Sehen wir uns zunächst die erforderliche Konfiguration für Spring Session JDBC an.

3.1. Abhängigkeiten von Maven

Zuerst müssen wir diese Abhängigkeiten zu unserem Projekt hinzufügen:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.197</version>
    <scope>runtime</scope>
</dependency>

Unsere Anwendung läuft mit Spring Boot , und der übergeordnete pom.xml stellt Versionen für jeden Eintrag bereit. Die neueste Version jeder Abhängigkeit finden Sie hier: https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.boot%22%20AND%20a%3A%22spring- boot-starter-web% 22[spring-boot-starter-web], https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.boot%22%20AND% 20a% 3A% 22spring-boot-starter-test% 22[Spring-Boot-Starter-Test] https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework . session% 22% 20AND% 20a% 3A% 22spring-session-jdbc% 22[spring-session-jdbc]und h2.

Überraschenderweise ist die einzige Konfigurationseigenschaft, die wir benötigen, um Spring Session zu aktivieren, die durch eine relationale Datenbank gesichert wird , in der application.properties :

spring.session.store-type=jdbc

4. Standard Spring Config (kein Spring Boot)

Werfen wir auch einen Blick auf die Integration und Konfiguration der Spring-Session ohne Spring Boot - einfach nur mit Spring.

4.1. Abhängigkeiten von Maven

Wenn wir einem Spring-Projekt spring-session-jdbc hinzufügen, müssen wir zuerst https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework hinzufügen .session% 22% 20AND% 20a% 3A% 22spring-session-jdbc% 22[spring-session-jdbc]und https://search.maven.org/classic/#search%7Cgav%7Cg%3A%22com .h2database% 22% 20AND% 20a% 3A% 22h2% 22[h2]an unsere pom.xml (die letzten beiden Abhängigkeiten aus dem Snippet im vorherigen Abschnitt).

4.2. Frühjahrssitzung Konfiguration

Nun fügen wir eine Konfigurationsklasse für Spring Session JDBC hinzu:

@Configuration
@EnableJdbcHttpSession
public class Config
  extends AbstractHttpSessionApplicationInitializer {

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

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

Wie wir sehen können, sind die Unterschiede minimal. Jetzt müssen wir unsere EmbeddedDatabase und __PlatformTransactionManager __beans explizit definieren - Spring Boot erledigt das für uns in der vorherigen Konfiguration.

Das Obige stellt sicher, dass das Spring Bean mit dem Namen springSessionRepositoryFilter für jede Anfrage in unserem Servlet Container registriert wird.

5. Eine einfache App

Nun geht es weiter mit einer einfachen REST-API, die die Sitzungspersistenz zeigt. __

5.1. Regler

Zuerst fügen wir eine Controller -Klasse hinzu, um Informationen in der HttpSession zu speichern und anzuzeigen:

@Controller
public class SpringSessionJdbcController {

    @GetMapping("/")
    public String index(Model model, HttpSession session) {
        List<String> favoriteColors = getFavColors(session);
        model.addAttribute("favoriteColors", favoriteColors);
        model.addAttribute("sessionId", session.getId());
        return "index";
    }

    @PostMapping("/saveColor")
    public String saveMessage
      (@RequestParam("color") String color,
      HttpServletRequest request) {

        List<String> favoriteColors
          = getFavColors(request.getSession());
        if (!StringUtils.isEmpty(color)) {
            favoriteColors.add(color);
            request.getSession().
              setAttribute("favoriteColors", favoriteColors);
        }
        return "redirect:/";
    }

    private List<String> getFavColors(HttpSession session) {
        List<String> favoriteColors = (List<String>) session
          .getAttribute("favoriteColors");

        if (favoriteColors == null) {
            favoriteColors = new ArrayList<>();
        }
        return favoriteColors;
    }
}

6. Testen Sie unsere Implementierung

Da wir nun eine API mit einer GET- und POST-Methode haben, schreiben wir Tests, um beide Methoden aufzurufen.

In jedem Fall sollten wir behaupten können, dass die Sitzungsinformationen in der Datenbank erhalten bleiben. Um dies zu überprüfen, wird die Sitzungsdatenbank direkt abgefragt.

Lass uns zuerst die Dinge einrichten:

@RunWith(SpringRunner.class)
@SpringBootTest(
  webEnvironment = SpringBootTest.WebEnvironment.RANDOM__PORT)
@FixMethodOrder(MethodSorters.NAME__ASCENDING)
public class SpringSessionJdbcApplicationTests {

    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate testRestTemplate;

    private List<String> getSessionIdsFromDatabase()
      throws SQLException {

        List<String> result = new ArrayList<>();
        ResultSet rs = getResultSet(
          "SELECT **  FROM SPRING__SESSION");

        while (rs.next()) {
            result.add(rs.getString("SESSION__ID"));
        }
        return result;
    }

    private List<byte[]> getSessionAttributeBytesFromDb()
      throws SQLException {

        List<byte[]> result = new ArrayList<>();
        ResultSet rs = getResultSet(
          "SELECT **  FROM SPRING__SESSION__ATTRIBUTES");

        while (rs.next()) {
            result.add(rs.getBytes("ATTRIBUTE__BYTES"));
        }
        return result;
    }

    private ResultSet getResultSet(String sql)
      throws SQLException {

        Connection conn = DriverManager
          .getConnection("jdbc:h2:mem:testdb", "sa", "");
        Statement stat = conn.createStatement();
        return stat.executeQuery(sql);
    }
}

Beachten Sie die Verwendung von @ FixMethodOrder (MethodSorters.NAME ASCENDING) , um die Reihenfolge der Testfallausführung zu steuern. __Lesen Sie mehr darüber hier .

Beginnen wir damit, dass die Sitzungstabellen in der Datenbank leer sind

@Test
public void whenH2DbIsQueried__thenSessionInfoIsEmpty()
  throws SQLException {

    assertEquals(
      0, getSessionIdsFromDatabase().size());
    assertEquals(
      0, getSessionAttributeBytesFromDatabase().size());
}

Als Nächstes testen wir den GET-Endpunkt:

@Test
public void whenH2DbIsQueried__thenOneSessionIsCreated()
  throws SQLException {

    assertThat(this.testRestTemplate.getForObject(
      "http://localhost:" + port + "/", String.class))
      .isNotEmpty();
    assertEquals(1, getSessionIdsFromDatabase().size());
}

Wenn die API zum ersten Mal aufgerufen wird, wird eine Sitzung erstellt und in der Datenbank gespeichert. Wie wir sehen, gibt es zu diesem Zeitpunkt nur eine Zeile in der Tabelle _SPRING SESSION ___

Zum Schluss testen wir den POST-Endpunkt, indem wir eine Lieblingsfarbe angeben:

@Test
public void whenH2DbIsQueried__thenSessionAttributeIsRetrieved()
  throws Exception {

    MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
    map.add("color", "red");
    this.testRestTemplate.postForObject(
      "http://localhost:" + port + "/saveColor", map, String.class);
    List<byte[]> queryResponse = getSessionAttributeBytesFromDatabase();

    assertEquals(1, queryResponse.size());
    ObjectInput in = new ObjectInputStream(
      new ByteArrayInputStream(queryResponse.get(0)));
    List<String> obj = (List<String>) in.readObject();
    assertEquals("red", obj.get(0));
}

Wie erwartet, behält die Tabelle _SPRING SESSION ATTRIBUTES die Lieblingsfarbe bei. Beachten Sie, dass wir den Inhalt von ATTRIBUTE BYTES _ zu einer Liste von String__ -Objekten deserialisieren müssen, da Spring Objektserialisierung durchführt, wenn Sitzungsattribute in der Datenbank beibehalten werden.

7. Wie funktioniert es?

Wenn Sie sich den Controller anschauen, gibt es keine Hinweise darauf, dass die Datenbank die Sitzungsinformationen beibehält. Die ganze Magie geschieht in einer Zeile, die wir in application.properties hinzugefügt haben.

Das heißt, wenn wir spring.session.store-type = jdbc, hinter den Kulissen angeben, wendet Spring Boot eine Konfiguration an, die dem manuellen Hinzufügen von @ EnableJdbcHttpSession annotation entspricht.

Dadurch wird ein Spring Bean mit dem Namen s _pringSessionRepositoryFilter erstellt, das a SessionRepositoryFilter_ implementiert.

Ein weiterer wichtiger Punkt ist, dass der Filter jeden HttpServletRequest abfängt und in einen SessionRepositoryRequestWrapper umschließt.

Außerdem wird die commitSession -Methode aufgerufen, um die Sitzungsinformationen beizubehalten.

8. In der H2-Datenbank gespeicherte Sitzungsinformationen

Durch Hinzufügen der folgenden Eigenschaften können Sie die Tabellen, in denen die Sitzungsinformationen gespeichert sind, über die URL http://localhost : 8080/h2-console/:

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

9. Schlussfolgerung

Spring Session ist ein leistungsfähiges Werkzeug zum Verwalten von HTTP-Sitzungen in einer verteilten Systemarchitektur. Spring übernimmt das schwere Heben für einfache Anwendungsfälle, indem ein vordefiniertes Schema mit minimaler Konfiguration bereitgestellt wird. Gleichzeitig bietet es die Flexibilität, über unser Design zu entscheiden, wie wir Sitzungsinformationen speichern möchten.

Für die Verwaltung der Authentifizierungsinformationen mithilfe von Spring Session können Sie sich schließlich auf diesen Artikel beziehen ( https://www.baeldung.com/spring-session [Guide to Spring Session).