Session de printemps avec JDBC

1. Vue d’ensemble

Dans ce didacticiel rapide, nous allons apprendre à utiliser le JDBC de session Spring pour conserver les informations de session dans une base de données.

À des fins de démonstration, nous utiliserons une base de données H2 en mémoire.

2. Options de configuration

Le moyen le plus simple et le plus rapide de créer notre exemple de projet consiste à utiliser Spring Boot . Cependant, nous allons également montrer un moyen non-boot pour configurer les choses.

Par conséquent, vous n’avez pas besoin de remplir les deux sections 3 et 4. Choisissez-en une seule selon que nous utilisons ou non Spring Boot pour configurer Spring Session .

3. Configuration de démarrage du printemps

Tout d’abord, examinons la configuration requise pour JDBC Spring Session.

3.1. Dépendances Maven

Premièrement, nous devons ajouter ces dépendances à notre projet:

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

Notre application fonctionne avec Spring Boot , et le parent pom.xml fournit des versions pour chaque entrée. La dernière version de chaque dépendance peut être trouvée ici: https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.boot%22%20and%20a%3A%22spring- boot-starter-web% 22[printemps-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[printemps-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]et h2.

De manière surprenante, la seule propriété de configuration dont nous avons besoin pour activer Spring Session sauvegardée par une base de données relationnelle est dans application.properties :

spring.session.store-type=jdbc

4. Configuration standard du ressort (pas de démarrage à ressort)

Jetons également un coup d’œil à l’intégration et à la configuration de la session de printemps sans Spring Boot - uniquement avec Plain Spring.

4.1. Dépendances Maven

Premièrement, si nous ajoutons spring-session-jdbc à un projet Spring standard, nous devrons ajouter https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework . .session% 22% 20AND% 20a% 3A% 22spring-session-jdbc% 22[spring-session-jdbc]et https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22com .h2database% 22% 20et% 20a% 3A% 22h2% 22[h2]à notre pom.xml (les deux dernières dépendances de l’extrait de code de la section précédente).

4.2. Configuration de session printanière

Ajoutons maintenant une classe de configuration pour Spring Session JDBC :

@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);
    }
}

Comme on peut le constater, les différences sont minimes. Nous devons maintenant définir explicitement nos EmbeddedDatabase et __PlatformTransactionManager __beans - Spring Boot le fait pour nous dans la configuration précédente.

Ce qui précède garantit que le bean Spring portant le nom springSessionRepositoryFilter est enregistré auprès de notre Servlet Container pour chaque demande.

5. Une application simple

Passons maintenant à une simple API REST qui enregistre la démonstration de la persistance de session _. _

5.1. Manette

Premièrement, ajoutons une classe Controller pour stocker et afficher des informations dans la HttpSession :

@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. Test de notre implémentation

Maintenant que nous avons une API avec une méthode GET et une méthode POST, écrivons des tests pour appeler les deux méthodes.

Dans chaque cas, nous devrions pouvoir affirmer que les informations de session sont conservées dans la base de données. Pour vérifier cela, nous interrogerons directement la base de données de session.

Commençons par mettre les choses en place:

@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);
    }
}

Notez l’utilisation de @ FixMethodOrder (MethodSorters.NAME ASCENDING) pour contrôler l’ordre d’exécution du scénario de test . __En savoir plus à ce sujet here .

Commençons par affirmer que les tables de session sont vides dans la base de données:

@Test
public void whenH2DbIsQueried__thenSessionInfoIsEmpty()
  throws SQLException {

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

Ensuite, nous testons le noeud final GET:

@Test
public void whenH2DbIsQueried__thenOneSessionIsCreated()
  throws SQLException {

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

Lorsque l’API est appelée pour la première fois, une session est créée et conservée dans la base de données. Comme nous pouvons le constater, la table _SPRING SESSION ___ ne contient qu’une seule ligne à ce stade.

Enfin, nous testons le point de terminaison POST en fournissant une couleur préférée:

@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));
}

Comme prévu, _SPRING SESSION ATTRIBUTES table conserve la couleur préférée. Notez que nous devons désérialiser le contenu de ATTRIBUTE BYTES _ dans une liste d’objets String__ depuis que Spring effectue la sérialisation des objets lors de la persistance des attributs de session dans la base de données.

7. Comment ça marche?

En regardant le contrôleur, rien n’indique que la base de données conserve les informations de session. Toute la magie se passe dans une ligne que nous avons ajoutée dans application.properties .

C’est-à-dire que lorsque nous spécifions spring.session.store-type = jdbc, en coulisse, Spring Boot appliquera une configuration équivalente à l’ajout manuel de @ EnableJdbcHttpSession annotation .

Cela crée un bean Spring nommé s _pringSessionRepositoryFilter qui implémente a SessionRepositoryFilter_ .

Un autre point clé est que le filtre intercepte chaque HttpServletRequest et l’enveloppe dans un SessionRepositoryRequestWrapper .

Il appelle également la méthode commitSession pour conserver les informations de session.

8. Informations de session stockées dans la base de données H2

En ajoutant les propriétés ci-dessous, nous pourrions examiner les tables dans lesquelles les informations de session sont stockées à partir de l’URL - http://localhost : 8080/h2-console/:

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

9. Conclusion

Spring Session est un outil puissant pour gérer les sessions HTTP dans une architecture de système distribuée. Spring prend en charge les tâches lourdes pour les cas d’utilisation simples en fournissant un schéma prédéfini avec une configuration minimale. Dans le même temps, il offre la souplesse nécessaire pour concevoir notre concept de stockage des informations de session.

Enfin, pour gérer les informations d’authentification à l’aide de Spring Session, vous pouvez vous référer à cet article - Guide to Spring Session .

Comme toujours, vous pouvez trouver le code source over sur Github .