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.4.0</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에서 찾을 수 있습니다.

application.properties에 Redis 서버에 대한 몇 가지 구성 속성을 추가해 보겠습니다 .

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 Boot없이 Spring 세션통합하고 구성하는 방법을 살펴 보겠습니다 .

4.1. 의존성

먼저 표준 Spring 프로젝트에 spring-session추가하는 경우 명시 적으로 정의해야합니다.

<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 세션 구성

이제 Spring Session에 대한 구성 클래스를 추가해 보겠습니다 .

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

@EnableRedisHttpSessionAbstractHttpSessionApplicationInitializer 의 확장은 모든 Security 인프라 앞에 필터를 만들고 연결하여 활성 세션을 찾고 Redis에 저장된 값에서 Security 컨텍스트를 채 웁니다 .

이제 컨트롤러와 Security 구성을 사용하여이 애플리케이션을 완료 해 보겠습니다.

5. 애플리케이션 구성

메인 애플리케이션 파일로 이동하여 컨트롤러를 추가합니다.

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

이를 통해 테스트 할 엔드 포인트가 제공됩니다.

다음으로 Security 구성 클래스를 추가합니다.

@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. 테스트

마지막으로 모든 것을 테스트 해 보겠습니다. 여기에서 두 가지 작업을 수행 할 수있는 간단한 테스트를 정의하겠습니다.

  • 라이브 웹 애플리케이션 사용
  • 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<String> result = jedis.keys("*");
    assertEquals(0, result.size());
}

이제 Security이 인증되지 않은 요청에 대해 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의 모든 데이터를 지 웁니다 .

마지막으로 세션 쿠키를 사용하여 또 다른 요청을하고 로그 아웃되었는지 확인합니다. 이것은 Spring Session 이 우리 세션을 관리하고 있음을 확인 합니다.

7. 결론

Spring Session 은 HTTP 세션을 관리하기위한 강력한 도구입니다. 세션 저장소를 구성 클래스로 단순화하고 Maven 의존성을 몇 개 사용하여 이제 여러 애플리케이션을 동일한 Redis 인스턴스에 연결하고 인증 정보를 공유 할 수 있습니다.

항상 그렇듯이 모든 예제는 Github에서 사용할 수 있습니다 .