Руководство по весенней сессии

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

1. обзор

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

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

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

Чтобы познакомиться сRedis, ознакомьтесь со статьейthis.

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

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


    org.springframework.boot
    spring-boot-starter-parent
    1.4.0.RELEASE
    



    
        org.springframework.boot
        spring-boot-starter-security
    
    
        org.springframework.boot
        spring-boot-starter-web
    
    
        org.springframework.boot
        spring-boot-starter-test
        test
    

Наше приложение работает с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

Во-первых, давайте продемонстрируем настройкуSpring Session с загрузкой.

Примечание: вам не нужно заполнять разделы 3 и 4. Просто выберите один в зависимости от того, используете ли выSpring Boot для настройкиSpring Session.

3.1. зависимости

Добавьте эти зависимости в наш проект:


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


    org.springframework.session
    spring-session

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

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

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

@Configuration
@EnableRedisHttpSession
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
}

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

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

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

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


    org.springframework.session
    spring-session
    1.2.2.RELEASE


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

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

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

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

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

Как видите, различия минимальны - нам просто нужно явно определить наш bean-компонентJedisConnectionFactory - Boot сделает это за нас.

В обоих типах@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("password").roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
          .httpBasic().and()
          .authorizeRequests()
          .antMatchers("/").hasRole("ADMIN")
          .anyRequest().authenticated();
    }
}

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

6. Test

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

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

  • поговорить с Redis

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

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 result = jedis.keys("*");
    assertEquals(0, result.size());
}

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

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

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

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

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

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

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

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

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

Как всегда, вы можете найти исходный кодover on Github.