Перейти к основному содержимому

Путеводитель по весенней сессии

· 5 мин. чтения

Задача: Наибольшая подстрока без повторений

Для заданной строки s, найдите длину наибольшей подстроки без повторяющихся символов. Подстрока — это непрерывная непустая последовательность символов внутри строки...

ANDROMEDA 42

1. Обзор

Spring Session имеет простую цель освободить управление сеансом от ограничений сеанса HTTP, хранящегося на сервере.

Решение упрощает обмен данными сеанса между службами в облаке без привязки к одному контейнеру (например, Tomcat). Кроме того, он поддерживает несколько сеансов в одном браузере и отправку сеансов в заголовке.

В этой статье мы будем использовать Spring Session для управления данными аутентификации в веб-приложении. Хотя Spring Session может сохранять данные с помощью JDBC, Gemfire или MongoDB, мы будем использовать Redis .

Для ознакомления с Redis ознакомьтесь с этой статьей.

2. Простой проект

Давайте сначала создадим простой проект Spring Boot , чтобы использовать его в качестве основы для наших примеров сеансов позже:

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.1</version>
<relativePath/>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<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>
</dependencies>

Наше приложение работает с Spring Boot , а родительский pom предоставляет версии для каждой записи. Последнюю версию каждой зависимости можно найти здесь: spring-boot-starter-security , spring-boot-starter-web , spring-boot-starter-test .

Давайте также добавим некоторые свойства конфигурации для нашего сервера Redis в application.properties :

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

3. Конфигурация весенней загрузки

Для Spring Boot достаточно добавить следующие зависимости , а об остальном позаботится автоконфигурация:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>

Мы используем загрузочный родительский pom для установки версий здесь, поэтому они гарантированно будут работать с другими нашими зависимостями. Последнюю версию каждой зависимости можно найти здесь: spring-boot-starter-data-redis , spring-session .

4. Стандартная конфигурация Spring (без загрузки)

Давайте также взглянем на интеграцию и настройку spring-session без Spring Boot — просто с помощью простого Spring.

4.1. Зависимости

Во- первых, если мы добавляем spring-session в стандартный проект Spring, нам нужно явно определить:

<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.5.0.RELEASE</version>
</dependency>

Последние версии этих модулей можно найти здесь: spring-session , spring-data-redis .

4.2. Конфигурация весеннего сеанса

Теперь давайте добавим класс конфигурации для Spring Session :

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

@EnableRedisHttpSession и расширение AbstractHttpSessionApplicationInitializer создадут и подключит фильтр перед всей нашей инфраструктурой безопасности для поиска активных сеансов и заполнения контекста безопасности значениями, хранящимися в Redis .

Давайте теперь завершим это приложение контроллером и конфигурацией безопасности.

5. Конфигурация приложения

Перейдите к нашему основному файлу приложения и добавьте контроллер:

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

Это даст нам конечную точку для тестирования.

Затем добавьте наш класс конфигурации безопасности:

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

Это защищает наши конечные точки с помощью базовой аутентификации и настраивает пользователя для тестирования.

6. Тест

Наконец, давайте все проверим — здесь мы определим простой тест, который позволит нам сделать 2 вещи:

  • использовать живое веб-приложение
  • поговорить с Редисом

Давайте сначала настроим вещи:

public class SessionControllerTest {

private Jedis jedis;
private TestRestTemplate testRestTemplate;
private TestRestTemplate testRestTemplateWithAuth;
private String testUrl = "http://localhost:8080/";

@Before
public void clearRedisData() {
testRestTemplate = new TestRestTemplate();
testRestTemplateWithAuth = new TestRestTemplate("admin", "password", null);

jedis = new Jedis("localhost", 6379);
jedis.flushAll();
}
}

Обратите внимание, как мы настраиваем оба этих клиента — клиент HTTP и клиент Redis. Конечно, на данный момент сервер (и Redis) должны быть запущены и работать, чтобы мы могли общаться с ними через эти тесты.

Давайте начнем с проверки того, что Redis пуст:

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

Теперь проверьте, что наша система безопасности возвращает 401 для неаутентифицированных запросов:

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

Затем мы проверяем, что Spring Session управляет нашим токеном аутентификации:

@Test
public void testRedisControlsSession() {
ResponseEntity<String> result = testRestTemplateWithAuth.getForEntity(testUrl, String.class);
assertEquals("hello admin", result.getBody()); //login worked

Set<String> 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<String> 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
}

Во-первых, наш тест подтверждает, что наш запрос был успешным с использованием учетных данных для аутентификации администратора.

Затем мы извлекаем значение сеанса из заголовков ответа и используем его в качестве аутентификации во втором запросе. Мы проверяем это, а затем очищаем все данные в Redis .

Наконец, мы делаем еще один запрос, используя файл cookie сеанса, и подтверждаем, что вышли из системы. Это подтверждает, что Spring Session управляет нашими сеансами.

7. Заключение

Spring Session — мощный инструмент для управления HTTP-сессиями. С нашим хранилищем сеансов, упрощенным до класса конфигурации и нескольких зависимостей Maven, мы теперь можем подключить несколько приложений к одному и тому же экземпляру Redis и обмениваться информацией для аутентификации.

Как всегда, все примеры доступны на Github .