Guide de la session de printemps

Haut REST

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS

1. Vue d'ensemble

Spring Session a pour objectif simple de libérer la gestion de session des limitations de la session HTTP stockée sur le serveur.

La solution facilite le partage des données de session entre les services dans le cloud sans être lié à un seul conteneur (par exemple Tomcat). De plus, il prend en charge plusieurs sessions dans le même navigateur et l'envoi de sessions dans un en-tête.

Dans cet article, nous utiliserons Spring Session pour gérer les informations d'authentification dans une application Web. Bien que Spring Session puisse conserver les données à l'aide de JDBC, Gemfire ou MongoDB, nous utiliserons Redis .

Pour une introduction à Redis, consultez cet article.

2. Un projet simple

Commençons par créer un projet Spring Boot simple à utiliser comme base pour nos exemples de session plus tard:

 org.springframework.boot spring-boot-starter-parent 2.2.6.RELEASE     org.springframework.boot spring-boot-starter-security   org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-test test  

Notre application fonctionne avec Spring Boot et le pom parent fournit des versions pour chaque entrée. La dernière version de chaque dépendance peut être trouvée ici: spring-boot-starter-security, spring-boot-starter-web, spring-boot-starter-test.

Ajoutons également quelques propriétés de configuration pour notre serveur Redis dans application.properties :

spring.redis.host=localhost spring.redis.port=6379

3. Configuration de Spring Boot

Pour Spring Boot, il suffit d'ajouter les dépendances suivantes , et la configuration automatique se chargera du reste:

 org.springframework.boot spring-boot-starter-data-redis   org.springframework.session spring-session-data-redis 

Nous utilisons le pom parent de démarrage pour définir les versions ici, donc celles-ci sont garanties pour fonctionner avec nos autres dépendances. La dernière version de chaque dépendance peut être trouvée ici: spring-boot-starter-data-redis, spring-session.

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

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

4.1. Dépendances

Premièrement, si nous ajoutons spring-session à un projet Spring standard, nous devrons définir explicitement:

 org.springframework.session spring-session 1.2.2.RELEASE   org.springframework.data spring-data-redis 1.5.0.RELEASE 

Les dernières versions de ces modules se trouvent ici: spring-session, spring-data-redis.

4.2. Configuration de la session de printemps

Ajoutons maintenant une classe de configuration pour Spring Session :

@Configuration @EnableRedisHttpSession public class SessionConfig extends AbstractHttpSessionApplicationInitializer { @Bean public JedisConnectionFactory connectionFactory() { return new JedisConnectionFactory(); } }

@EnableRedisHttpSession et l'extension de AbstractHttpSessionApplicationInitializer vont créer et câbler un filtre devant toute notre infrastructure de sécurité pour rechercher les sessions actives et peupler le contexte de sécurité à partir des valeurs stockées dans Redis .

Complétons maintenant cette application avec un contrôleur et la configuration de sécurité.

5. Configuration de l'application

Accédez à notre fichier d'application principal et ajoutez un contrôleur:

@RestController public class SessionController { @RequestMapping("/") public String helloAdmin() { return "hello admin"; } }

Cela nous donnera un point final à tester.

Ensuite, ajoutez notre classe de configuration de sécurité:

@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("admin") .password(passwordEncoder().encode("password")) .roles("ADMIN"); } @Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic().and() .authorizeRequests() .antMatchers("/").hasRole("ADMIN") .anyRequest().authenticated(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }

Cela protège nos points de terminaison avec une authentification de base et configure un utilisateur avec lequel tester.

6. Test

Enfin, testons tout - nous définirons ici un test simple qui va nous permettre de faire 2 choses:

  • consommer l'application Web en direct
  • parler à Redis

Commençons par mettre les choses en place:

public class SessionControllerTest { private Jedis jedis; private TestRestTemplate testRestTemplate; private TestRestTemplate testRestTemplateWithAuth; private String testUrl = "//localhost:8080/"; @Before public void clearRedisData() { testRestTemplate = new TestRestTemplate(); testRestTemplateWithAuth = new TestRestTemplate("admin", "password", null); jedis = new Jedis("localhost", 6379); jedis.flushAll(); } }

Remarquez comment nous configurons ces deux clients - le client HTTP et celui de Redis. Bien sûr, à ce stade, le serveur (et Redis) doit être opérationnel - afin que nous puissions communiquer avec eux via ces tests.

Commençons par tester que Redis est vide:

@Test public void testRedisIsEmpty() { Set result = jedis.keys("*"); assertEquals(0, result.size()); }

Maintenant, testez que notre sécurité renvoie un 401 pour les demandes non authentifiées:

@Test public void testUnauthenticatedCantAccess() { ResponseEntity result = testRestTemplate.getForEntity(testUrl, String.class); assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode()); }

Ensuite, nous testons que Spring Session gère notre jeton d'authentification:

@Test public void testRedisControlsSession() { ResponseEntity result = testRestTemplateWithAuth.getForEntity(testUrl, String.class); assertEquals("hello admin", result.getBody()); //login worked Set redisResult = jedis.keys("*"); assertTrue(redisResult.size() > 0); //redis is populated with session data String sessionCookie = result.getHeaders().get("Set-Cookie").get(0).split(";")[0]; HttpHeaders headers = new HttpHeaders(); headers.add("Cookie", sessionCookie); HttpEntity httpEntity = new HttpEntity(headers); result = testRestTemplate.exchange(testUrl, HttpMethod.GET, httpEntity, String.class); assertEquals("hello admin", result.getBody()); //access with session works worked jedis.flushAll(); //clear all keys in redis result = testRestTemplate.exchange(testUrl, HttpMethod.GET, httpEntity, String.class); assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode()); //access denied after sessions are removed in redis }

First, our test confirms that our request was successful using the admin authentication credentials.

Then we extract the session value from the response headers and use it as our authentication in our second request. We validate that, and then clear all the data in Redis.

Finally, we make another request using the session cookie and confirm that we are logged out. This confirms that Spring Session is managing our sessions.

7. Conclusion

Spring Session is a powerful tool for managing HTTP sessions. With our session storage simplified to a configuration class and a few Maven dependencies, we can now wire up multiple applications to the same Redis instance and share authentication information.

Comme toujours, tous les exemples sont disponibles sur Github.

REST bas

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS