로그아웃DefaultLogoutPageGeneratingFilterLogoutFilterLogoutHandlerLogoutSuccessHandler로그인, 로그아웃 기본 설정 코드
로그아웃
DefaultLogoutPageGeneratingFilter
- GET /logout 처리
- POST /logout을 요청할 수 있는 UI를 제공
- DefaultLoginPageGeneratingFilter를 사용하는 경우에 같이 제공됨
LogoutFilter
- POST /logout을 처리. processingURL을 변경하면 바꿀 수 있음
- 해당 필터는 configure에서 추가해주지 않아도 기본으로 등록되어 있음. 그래서 /logout 에 POST를 보내게 되면 로그아웃이 진행됨
public LogoutFilter(LogoutSuccessHandler logoutSuccessHandler, LogoutHandler... handlers) { this.handler = new CompositeLogoutHandler(handlers); Assert.notNull(logoutSuccessHandler, "logoutSuccessHandler cannot be null"); this.logoutSuccessHandler = logoutSuccessHandler; this.setFilterProcessesUrl("/logout"); }
LogoutHandler

- CsrfLogoutHandler : csrfTokenRepository 에서 csrf 토큰을 clear 함
- SecurityContextLogoutHandler : 세션과 SecurityContext 를 clear 함
invalidateHttpSession
clearAuthentication
- CookieClearingLogoutHandler : clear 대상이 된 쿠키들을 삭제함
- HeaderWriterLogoutHandlerRememberMeServices : remember-me 쿠키를 삭제함
- LogoutSuccessEventPublishingLogoutHandler : 로그아웃이 성공하면 이벤트를 발행함
LogoutSuccessHandler
- 메서드 시그니처
- void onLogoutSuccess( HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException;
- SimpleUrlLogoutSuccessHandler
로그인, 로그아웃 기본 설정 코드
@EnableWebSecurity(debug = true) @EnableGlobalMethodSecurity(prePostEnabled = true) /* 해당 어노테이션을 붙임으로써, Controller 에서 @PreAuthorize("hasAnyAuthority('ROLE_USER')") 같은 기능이 활성화됨 */ public class SecurityConfig extends WebSecurityConfigurerAdapter { private final CustomAuthDetails customAuthDetails; public SecurityConfig(CustomAuthDetails customAuthDetails) { this.customAuthDetails = customAuthDetails; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser( User.withDefaultPasswordEncoder() .username("user1") .password("1111") .roles("USER") ).withUser( User.withDefaultPasswordEncoder() .username("admin") .password("2222") .roles("ADMIN") ); // 메모리 상으로 user 정보 잠시 저장하는 것. 테스트할때 유용함 } // role의 계층구조를 정의하는 RoleHierarchy 클래스를 빈으로 등록. @Bean RoleHierarchy roleHierarchy(){ RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER"); return roleHierarchy; } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(request->{ request.antMatchers("/").permitAll() .antMatchers("/**").authenticated() ; }) .formLogin(login -> login.loginPage("/login") .permitAll() .defaultSuccessUrl("/", false) /* 로그인 성공시 /로 이동. 항상 /로 가는건 아니고 다른 페이지로 요청했을 시에는 그 페이지로 넘어감 */ .failureUrl("/login-error") // 로그인 실패 연결 페이지 설정 .authenticationDetailsSource(customAuthDetails)) /* Authentication interface의 details 정보를 나타내기 위한 커스텀 클래스를 정의해서 소스로 사용할 수 있음*/ .logout(logout -> logout.logoutSuccessUrl("/")) // logout 성공시 연결 url .exceptionHandling(exception -> exception.accessDeniedPage("/access-denied")) // exception 났을 때 처리하는 방법 설정 ; } } }
//authentiocationDetailsSource에서 사용하는 컴포넌트 @Component public class CustomAuthDetails implements AuthenticationDetailsSource<HttpServletRequest, RequestInfo> { @Override public RequestInfo buildDetails(HttpServletRequest context) { return RequestInfo.builder() .remoteIp(context.getRemoteAddr()) .sessionId(context.getSession().getId()) .loginTime(LocalDateTime.now()) .build(); } } @Data @AllArgsConstructor @NoArgsConstructor @Builder public class RequestInfo { private String remoteIp; private String sessionId; private LocalDateTime loginTime; }