1. 소개

Spring Security는 Spring 기반 애플리케이션을 보호하기 위한 표준입니다. 로그인 및 로그아웃을 포함하여 사용자 인증을 관리하는 여러 기능이 있습니다. 

이 예제에서는 Spring Security를 ​​사용한 수동 로그아웃 에 중점을 둘 것 입니다.

독자가 이미 표준 Spring Security 로그아웃 프로세스를 이해하고 있다고 가정합니다. 

2. 기본 로그아웃

사용자가 로그아웃을 시도 하면  현재 세션 상태에 여러 가지 결과가 있습니다 . 다음 두 단계로 세션을 파괴해야 합니다.

  1. HTTP 세션 정보를 무효화합니다.
  2. 인증 정보가 포함되어 있으므로 SecurityContext 를 지 웁니다 .

이 두 가지 작업은 SecurityContextLogoutHandler에 의해 수행됩니다.

실제로 작동하는지 봅시다.

@Configuration
public class DefaultLogoutConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
          .logout(logout -> logout
            .logoutUrl("/basic/basiclogout")
            .addLogoutHandler(new SecurityContextLogoutHandler())
          );
        return http.build();
    }
}

SecurityContextLogoutHandler 는  기본적으로 Spring Security에 의해 추가됩니다. 명확성을 위해 여기에 표시합니다.

3. 쿠키 삭제 로그아웃

종종 로그아웃 시 사용자 쿠키의 일부 또는 전체를 지워야 합니다.

이를 위해 모든 쿠키를 반복하고 로그아웃 시 만료되는 자체 LogoutHandler 를 만들 수 있습니다 .

@Configuration
public class AllCookieClearingLogoutConfiguration {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
          .logout(logout -> logout
            .logoutUrl("/cookies/cookielogout")
            .addLogoutHandler((request, response, auth) -> {
                for (Cookie cookie : request.getCookies()) {
                    String cookieName = cookie.getName();
                    Cookie cookieToDelete = new Cookie(cookieName, null);
                    cookieToDelete.setMaxAge(0);
                    response.addCookie(cookieToDelete);
                }
            })
          );
        return http.build();
    }
}

실제로 Spring Security는 쿠키 제거를 위해 즉시 사용할 수 있는 로그아웃 핸들러인 CookieClearingLogoutHandler 를 제공합니다.

4. Clear-Site-Data 헤더 로그아웃

마찬가지로 특별한 HTTP 응답 헤더를 사용하여 동일한 결과를 얻을 수 있습니다. 이것이 Clear-Site-Data  헤더 가 작동하는 곳입니다.

기본적으로 Clear-Data-Site 헤더는 요청한 웹사이트와 관련된 검색 데이터(쿠키, 저장소, 캐시)를 지웁니다.

@Configuration
public class ClearSiteDataHeaderLogoutConfiguration {

    private static final ClearSiteDataHeaderWriter.Directive[] SOURCE = 
      {CACHE, COOKIES, STORAGE, EXECUTION_CONTEXTS};

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
          .logout(logout -> logout
            .logoutUrl("/csd/csdlogout")
            .addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(SOURCE)))
          );
        return http.build();
    }
}

그러나 한 가지 유형의 스토리지만 지울 때 스토리지 정리로 인해 애플리케이션 상태가 손상될 수 있습니다. 따라서 Incomplete Clearing 으로 인해 요청이 안전한 경우에만 헤더가 적용됩니다.

5. 요청 시 로그아웃

마찬가지로 HttpServletRequest.logout() 메서드를 사용하여 사용자를 로그아웃할 수 있습니다.

 먼저 요청에 대해 수동으로 logout() 을 호출하는 데 필요한 구성을 추가해 보겠습니다 .

@Configuration
public static class LogoutOnRequestConfiguration {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.antMatcher("/request/**")
            .authorizeRequests(authz -> authz.anyRequest()
                .permitAll())
            .logout(logout -> logout.logoutUrl("/request/logout")
                .addLogoutHandler((request, response, auth) -> {
                    try {
                        request.logout();
                    } catch (ServletException e) {
                        logger.error(e.getMessage());
                    }
                }));
        return http.build();
    }
}

마지막으로 모든 것이 예상대로 작동하는지 확인하는 테스트 케이스를 만들어 보겠습니다.

@Test
public void givenLoggedUserWhenUserLogoutOnRequestThenSessionCleared() throws Exception {

    this.mockMvc.perform(post("/request/logout").secure(true)
        .with(csrf()))
        .andExpect(status().is3xxRedirection())
        .andExpect(unauthenticated())
        .andReturn();
}

6. 결론

요약하면, Spring Security에는 인증 시나리오를 처리하기 위한 많은 내장 기능이 있습니다. 프로그래밍 방식으로 이러한 기능을 사용하는 방법을 마스터하는 것은 항상 편리합니다.

항상 그렇듯이 이 예제의 코드는  GitHub에서 사용할 수 있습니다 .

Security footer banner