logout 필터 프로세스를 살펴보고 이해해보자.
스프링 시큐리티는 formLogin
기능을 활성화 하는 경우 기본적으로 로그인/로그아웃 페이지를 제공한다.
로그아웃 페이지는 DefaultLougoutPageGeneratingFilter
를 통해 생성되고 "GET /logout" URL로 접근 가능하다.
로그인 페이지는 DefautlLoginPageGeneratingFilter
를 통해 생성되고 "GET /login" URL로 접근 가능하다.
로그아웃 요청은 기본적으로 "POST /logout" URL로만 가능하지만, CSRF 기능을 비활성화 하는 경우 or RequestMatcher
를 사용할 경우 GET, PUT, DELETE
모두 가능하다.
로그아웃 필터(LogoutFilter
)를 거치지 않고 스프링 MVC에서 커스텀으로 구현 가능하다
로그인 페이지를 커스텀하여 생성하는 경우 로그아웃 기능도 커스텀으로 구현해야 한다.
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.logout(
httpSecurityLogoutConfigurer -> httpSecurityLogoutConfigurer
//로그아웃 요청이 수행될 URL을 지정한다(기본값은 "/logout")
.logoutUrl("/logoutProc")
//로그아웃 요청이 수행될 URL을 지정한다. httpMethod를 지정하지 않으면 모든 HTTP 메소드 요청으로 로그아웃 수행이 가능하다.
//logoutUrl api보다 우선순위가 높다
.logoutRequestMatcher(
new AntPathRequestMatcher("/logoutProc", "POST")
)
//로그아웃 완료된 후 redirect될 URL을 지정한다(기본값은 "/login?logout")
.logoutSuccessUrl("/logoutSuccess")
//로그아웃이 성공했을 떄 수행할 handler를 설정한다.
//이 handler를 지정하면, logoutSuccessUrl api 설정이 무시된다.
.logoutSuccessHandler((request, response, authentication) -> {
response.sendRedirect("/logoutSuccess");
})
//로그아웃 성공 시 제거될 쿠키의 이름을 지정할 수 있다.
.deleteCookies("JSESSIONID", "CUSTOM_COOKIE")
//HttpSession을 무효화한다. 기본값은 true
.invalidateHttpSession(true)
//로그아웃 시 SecurityContextLogoutHandler가 Authentication을 삭제한다. 기본값은 true
.clearAuthentication(true)
//기존 로그아웃 핸들러들 뒤에 새로운 `LogoutHandler`를 추가한다.
.addLogoutHandler(((request, response, authentication) -> {
}))
//logoutUrl(), RequestMatcher() URL에 대한 모든 사용자의 접근을 허용한다ㅣ.
.permitAll()
);
return http.build();
}
사용자가 로그아웃 요청을 한다.
RequestMatcher
를 통해 LogoutFilter
가 동작해야 하는지 판단한다.
RequestMatcher
에 매칭되면, LogoutHandler
가 동작한다.
LogoutHandler
들이 존재한다.LogoutHandler
를 추가할 수 있다.(대체 X)로그아웃이 성공하면 LogoutSuccessHandler
가 동작한다. 우리가 정의한 LogoutSuccessHandler
로 대체할 수 있다.
처음 요청은 우선 FilterChainProxy
가 받게 된다.
FilterChainProxy
에서 SecurityFilterChain
이 가지고 있는 Filter
들을 순회하며 요청을 처리한다.
LogoutFilter
순서에 도달하면 현재 요청이 이 필터를 타야하는지 먼저 검증한다.
RequestMatcher
와 일치하면 로그아웃 필터의 로직을 실행하게 된다.
SecurityContextHolder
에서 SecurityContext
를 꺼내고 Authentication
객체를 꺼낸다.
그리고 handler.logout
을 실행하게 되는데 this.handler
는 CompositeLogoutHandler
로 logoutHandler들의 목록을 가지고 있다.
여기서 두 번째 핸들러가 우리가 추가한 LogoutHandler
다.
이제 각각의 로그아웃 핸들러들을 순회하며 logout을 호출하는데, 우리가 설정한 handler를 실행할 차례가 오면 해당 handler의 logout을 호출하게 된다.
커스텀하게 구현한 로그아웃 필터 내용은 기본적으로 스프링 시큐리티가 들고있는 로그아웃 핸들러 목록 중 SecurityContextLogoutHandler
가 하는 내용을 간단하게 작성한 것이다.