1. 개요

이 빠른 사용방법(예제)에서는 Spring 세션 JDBC를 사용하여 세션 정보를 데이터베이스에 유지하는 방법을 배웁니다.

데모 목적으로 메모리 내 H2 데이터베이스를 사용할 것입니다.

2. 구성 옵션

샘플 프로젝트를 만드는 가장 쉽고 빠른 방법은 Spring Boot 를 사용하는 것 입니다. 그러나 우리는 설정하는 비 부팅 방법도 보여줄 것입니다.

따라서 섹션 3과 4를 모두 완료 할 필요는 없습니다 . Spring 세션  을 구성하기 위해 Spring Boot 를  사용하는지 여부에 따라 하나만 선택하면  됩니다.

3. 스프링 부트 설정

먼저 Spring Session JDBC에 필요한 구성을 살펴 보겠습니다.

3.1. Maven 의존성

먼저 프로젝트에 이러한 의존성을 추가해야합니다.

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

우리의 애플리케이션은 Spring Boot로 실행되며  상위 pom.xml 은 각 항목에 대한 버전을 제공합니다. 각 의존성의 최신 버전은  spring-boot-starter-webspring-boot-starter-test,  spring-session-jdbc 및 h2에서 찾을 수 있습니다.

놀랍게도 관계형 데이터베이스 에서 지원하는 Spring Session을 활성화하는 데 필요한 유일한 구성 속성 application.properties입니다 .

spring.session.store-type=jdbc

4. 표준 스프링 구성 (스프링 부트 없음)

또한 Spring Boot없이 Spring 세션을 통합하고 구성하는 방법을 살펴 보겠습니다.

4.1. Maven 의존성

우리가 추가하는 경우, 최초로 스프링 세션 JDBC를 표준 Spring 프로젝트에, 우리는 추가해야합니다 스프링 세션 JDBC  와  H2를  우리에 pom.xml 파일 (이전 섹션에서 스 니펫에서 마지막 두 의존성을).

4.2. Spring 세션 구성

이제 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);
    }
}

보시다시피 차이는 미미합니다. 이제 EmbeddedDatabase  및  PlatformTransactionManager  빈을 명시 적으로 정의해야합니다. Spring Boot는 이전 구성에서이를 수행합니다.

위의 내용은 springSessionRepositoryFilter 라는 이름의 Spring bean이 모든 요청에 ​​대해 Servlet Container 등록 되도록 보장합니다  .

5. 간단한 앱

계속해서 세션 지속성을 보여주는 간단한 REST API를 살펴 보겠습니다

5.1. 제어 장치

먼저 HttpSession에 정보를 저장하고 표시 하는 Controller 클래스를 추가해 보겠습니다 .

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

이제 GET 및 POST 메서드가있는 API가 있으므로 두 메서드를 모두 호출하는 테스트를 작성해 보겠습니다.

각각의 경우 세션 정보가 데이터베이스에 유지된다는 것을 주장 할 수 있어야합니다. 이를 확인하기 위해 세션 데이터베이스를 직접 쿼리합니다.

먼저 설정해 보겠습니다.

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

사용 참고 @FixMethodOrder (MethodSorters.NAME_ASCENDING) 테스트 케이스 실행 순서를 제어 여기에서 자세한 내용을 읽어보십시오 .

데이터베이스에서 세션 테이블이 비어 있다고 가정하여 시작해 보겠습니다.

@Test
public void whenH2DbIsQueried_thenSessionInfoIsEmpty() 
  throws SQLException {
 
    assertEquals(
      0, getSessionIdsFromDatabase().size());
    assertEquals(
      0, getSessionAttributeBytesFromDatabase().size());
}

다음으로 GET 엔드 ​​포인트를 테스트합니다.

@Test
public void whenH2DbIsQueried_thenOneSessionIsCreated() 
  throws SQLException {
 
    assertThat(this.testRestTemplate.getForObject(
      "http://localhost:" + port + "/", String.class))
      .isNotEmpty();
    assertEquals(1, getSessionIdsFromDatabase().size());
}

API가 처음 호출되면 세션이 생성되고 데이터베이스에 유지됩니다. 보시 다시피이 시점에서 SPRING_SESSION  테이블에는 행이 하나만  있습니다.

마지막으로 선호하는 색상을 제공하여 POST 끝점을 테스트합니다.

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

예상대로 SPRING_SESSION_ATTRIBUTES  테이블은 좋아하는 색상을 유지합니다. Spring은 데이터베이스에서 세션 속성을 유지할 때 객체 직렬화를 수행하기 때문에 ATTRIBUTE_BYTES  의 내용을 String 객체 List 으로 역 직렬화  해야합니다.

7. 어떻게 작동합니까?

컨트롤러를 보면 데이터베이스가 세션 정보를 유지하고 있다는 표시가 없습니다. 모든 마법은 우리가 application.properties에 추가 한 한 줄에서 일어나고 있습니다.

즉, spring.session.store-type = jdbc를 지정 하면  배후에서 Spring Boot는 @EnableJdbcHttpSession 어노테이션 을 수동으로 추가하는 것과 동일한 구성을 적용합니다 .

이것은 SessionRepositoryFilter 를 구현 하는 s pringSessionRepositoryFilter 라는 이름의 Spring Bean을 생성합니다 .

또 다른 요점은 필터가 모든 HttpServletRequest를 가로 채고  이를 SessionRepositoryRequestWrapper 로 래핑한다는 것  입니다.

또한 commitSession  메서드를 호출  하여 세션 정보를 유지합니다.

8. H2 데이터베이스에 저장된 세션 정보

아래 속성을 추가하여 http : // localhost : 8080 / h2-console / URL에서 세션 정보가 저장된 테이블을 살펴볼 수 있습니다   .

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
Spring 세션 jdbc Spring 세션 jdbc

9. 결론

Spring Session은 분산 시스템 아키텍처에서 HTTP 세션을 관리하기위한 강력한 도구입니다. Spring은 최소한의 구성으로 미리 정의 된 스키마를 제공하여 간단한 사용 사례에 대한 무거운 작업을 처리합니다. 동시에 세션 정보를 저장하는 방법에 대한 디자인을 제안 할 수있는 유연성을 제공합니다.

마지막으로 Spring Session을 사용하여 인증 정보를 관리하려면이 기사 – Guide to Spring Session을 참조하십시오 .

항상 그렇듯이 Github 에서 소스 코드 찾을 수 있습니다 .