Sessão de primavera com JDBC
*1. Visão geral *
Neste tutorial rápido, aprenderemos como usar o JDBC da sessão Spring para manter as informações da sessão em um banco de dados.
Para fins de demonstração, usaremos um banco de dados H2 na memória.
===* 2. Opções de configuração *
A maneira mais fácil e rápida de criar nosso projeto de amostra é usando Spring Boot. No entanto, também mostraremos uma maneira sem inicialização de configurar as coisas.
Portanto, você não precisa concluir as seções 3 e 4. Basta escolher um, dependendo de estarmos usando o Spring Boot para configurar o Spring Session.
===* 3. Configuração de inicialização por mola *
Primeiro, vamos analisar a configuração necessária para o Spring Session JDBC.
====* 3.1 Dependências do Maven *
Primeiro, precisamos adicionar essas dependências ao nosso projeto:
<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>
Nosso aplicativo é executado com Spring Boot, e o pai pom.xml fornece versões para cada entrada. A versão mais recente de cada dependência pode ser encontrada aqui: https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.boot%22%20AND%20a%3A%22spring- boot-starter-web% 22 [https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.boot%22%20AND% 20a% 3A% 22spring-boot-starter-test% 22 [teste de inicialização de inicialização da primavera] https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework. sessão% 22% 20AND% 20a% 3A% 22spring-session-jdbc% 22 [spring-session-jdbc] e h2.
Surpreendentemente,* a única propriedade de configuração que precisamos para ativar o Spring Session suportado por um banco de dados relacional está no _ application.properties *: _
spring.session.store-type=jdbc
*4. Configuração de mola padrão (sem inicialização por mola) *
Vamos também dar uma olhada na sessão de integração e configuração do Spring sem o Spring Boot - apenas com o Spring simples.
====* 4.1 Dependências do Maven *
Primeiro, se estamos adicionando spring-session-jdbc a um projeto padrão do Spring, precisamos adicionar https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework .session% 22% 20AND% 20a% 3A% 22spring-session-jdbc% 22 [spring-session-jdbc] e https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22com .h2database% 22% 20AND% 20a% 3A% 22h2% 22 [h2] para o nosso pom.xml (últimas duas dependências do snippet na seção anterior).
====* 4.2 Configuração da sessão da primavera *
Agora vamos adicionar uma classe de configuração para 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);
}
}
Como podemos ver, as diferenças são mínimas. Agora temos que definir nossos EmbeddedDatabase e PlatformTransactionManager beans explicitamente - o Spring Boot faz isso por nós na configuração anterior.
O item acima garante que o Spring bean com o nome springSessionRepositoryFilter seja registrado com nosso Servlet Container para cada solicitação.
===* 5. Um aplicativo simples *
Continuando, vejamos uma API REST simples que salva demonstra a persistência da sessão.
====* 5.1. Controlador *
Primeiro, vamos adicionar uma classe Controller para armazenar e exibir informações na 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. Testando nossa implementação *
Agora que temos uma API com os métodos GET e POST, vamos escrever testes para chamar os dois métodos.
Em cada caso, devemos poder afirmar que as informações da sessão são mantidas no banco de dados. Para verificar isso, consultaremos o banco de dados da sessão diretamente.
Vamos primeiro configurar as coisas:
@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);
}
}
Observe o uso de _ @ FixMethodOrder (MethodSorters.NAME_ASCENDING) _ para controlar a ordem de execução do caso de teste. Leia mais sobre o assunto https://www..com/junit-5-test-order [aqui].
Vamos começar afirmando que as tabelas de sessão estão vazias no banco de dados:
@Test
public void whenH2DbIsQueried_thenSessionInfoIsEmpty()
throws SQLException {
assertEquals(
0, getSessionIdsFromDatabase().size());
assertEquals(
0, getSessionAttributeBytesFromDatabase().size());
}
Em seguida, testamos o ponto de extremidade GET:
@Test
public void whenH2DbIsQueried_thenOneSessionIsCreated()
throws SQLException {
assertThat(this.testRestTemplate.getForObject(
"http://localhost:" + port + "/", String.class))
.isNotEmpty();
assertEquals(1, getSessionIdsFromDatabase().size());
}
Quando a API é chamada pela primeira vez, uma sessão é criada e persistida no banco de dados. Como podemos ver, há apenas uma linha na tabela SPRING_SESSION neste momento.
Por fim, testamos o terminal POST fornecendo uma cor favorita:
@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));
}
Como esperado, a tabela SPRING_SESSION_ATTRIBUTES persiste a cor favorita. Observe que precisamos desserializar o conteúdo de ATTRIBUTE_BYTES para uma lista de objetos String, pois o Spring faz a serialização de objetos ao persistir os atributos da sessão no banco de dados.
===* 7. Como funciona? *
Olhando para o controlador, não há indicação de que o banco de dados persista nas informações da sessão. Toda a mágica está acontecendo em uma linha que adicionamos no application.properties.
Ou seja,* quando especificarmos spring.session.store-type = jdbc, _ nos bastidores, o Spring Boot aplicará uma configuração equivalente a adicionar manualmente a anotação _ @ EnableJdbcHttpSession *.
Isso cria um Spring Bean chamado springSessionRepositoryFilter que implementa a SessionRepositoryFilter.
Outro ponto importante é que o filtro intercepta todos os HttpServletRequest e os agrupa em um SessionRepositoryRequestWrapper.
Ele também chama o método commitSession para manter as informações da sessão.
*8. Informações da sessão armazenadas no banco de dados H2 *
Adicionando as propriedades abaixo, podemos dar uma olhada nas tabelas em que as informações da sessão são armazenadas a partir da URL - http://localhost: 8080/h2-console/:
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
https://www..com/wp-content/uploads/2018/07/bael-1911_1.png [imagem: https://www..com/wp-content/uploads/2018/07/bael-1911_1_s. png [spring session jdbc]] https://www..com/wp-content/uploads/2018/07/bael-1911_2.png [imagem: spring session jdbc]
9. Conclusão
O Spring Session é uma ferramenta poderosa para gerenciar sessões HTTP em uma arquitetura de sistema distribuída. A Spring cuida do trabalho pesado para casos de uso simples, fornecendo um esquema predefinido com configuração mínima. Ao mesmo tempo, oferece a flexibilidade de apresentar nosso design sobre como queremos armazenar informações da sessão.
Por fim, para gerenciar informações de autenticação usando o Spring Session, você pode consultar este artigo - https://www..com/spring-session [Guia para o Spring Session].
Como sempre, você pode encontrar o código fonte over no Github.