[Spring Security] Logout 처리

식빵·2022년 8월 6일
1
post-thumbnail

이 시리즈에 나오는 모든 내용은 인프런 인터넷 강의 - [ 스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security ] - 에서 기반된 것입니다. 그리고 여기서 인용되는 PPT 이미지 또한 모두 해당 강의에서 가져왔음을 알립니다.




🥝 Spring Security Logout


Logout 동작방식

스프링 시큐리티의 로그아웃 기능의 동작방식은 아래와 같다.

  1. 사용자가 로그아웃 요청
  2. Spring Security가 로그아웃에 필요한 작업 수행
    • 세션 무효화
    • 인증토큰 삭제
    • 인증토큰을 갖고 있던 SecurityContext 또한 삭제
    • 쿠키정보 삭제
    • 로그인 페이지로 리다이렉트

위 과정뿐만 아니라 개발자가 원한다면 커스텀하게 로그아웃 작업을 추가할 수도 있다.
아래에 그 방법이 나온다.


🥥 Logout API

복습차원에서 이전에 했던 로그인 작업도 같이 작성한다.
하지만 이후로는 다 생략할 것이다.

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests(request -> request.anyRequest().authenticated());
        http
            .formLogin()
            .usernameParameter("userId")
            .passwordParameter("passwd")
            .successHandler((request, response, authentication) -> {
                response.sendRedirect("/");
            })
            .failureHandler((request, response, exception) -> {
                response.sendRedirect("/login");
                // 참고로 스프링 시큐리티가 제공하는 로그인 페이지 주소는
                // "/login"이다.
            })
            .permitAll();
    
    
    	// 여기서부터 로그아웃 API 내용~!
        http.logout()
            .logoutUrl("/logout")   // 로그아웃 처리 URL (= form action url)
            //.logoutSuccessUrl("/login") // 로그아웃 성공 후 targetUrl, 
            // logoutSuccessHandler 가 있다면 효과 없으므로 주석처리.
            .addLogoutHandler((request, response, authentication) -> { 
                // 사실 굳이 내가 세션 무효화하지 않아도 됨. 
                // LogoutFilter가 내부적으로 해줌.
                HttpSession session = request.getSession();
                if (session != null) {
                    session.invalidate();
                }
            })  // 로그아웃 핸들러 추가
            .logoutSuccessHandler((request, response, authentication) -> {
                response.sendRedirect("/login");
            }) // 로그아웃 성공 핸들러
            .deleteCookies("remember-me"); // 로그아웃 후 삭제할 쿠키 지정
    }
}

addLogoutHandler api 추가설명

  • Logout 기능을 활성화하면 LogoutFilter 가 생김
  • 해당 필터 내부에는 이미 로그아웃 핸들러 존재
    • 세션 무효화 핸들러
    • 인증토큰, SecurityContext 삭제 핸들러
    • 쿠키정보 삭제 핸들러
  • 기본 로그아웃 핸들러 외에도 사용자가 직접 핸들러를 등록하고 싶다면 addLogoutHandler 사용
  • 추가하게 되면 모든 핸들러 중에서 가장 먼저 수행

참고로 스프링 시큐리티는 로그아웃을 기본으로 POST 방식을 사용하도록 한다.
GET 방식으로 바꿀 수 있다. 그 방법은 추후에 알아보는 걸로...



🥥 LogoutFilter

  • logoutUrl 메소드로 지정했한 url 로 온 요청인지 확인
  • 맞다면 SecurityContext 에서 인증 객체를 꺼냄
  • 그것을 SecurityContextLogoutHandler 에 전달
    • 이후에 핸들러를 사용해서 세션무효화, 쿠키삭제, SecurityContext 삭제를 수행
  • 로그아웃이 다 끝나면 SimpleUrlLogoutSuccessHandler 호출하여 로그인 페이지 이동



위의 과정들을 코드를 통해서 확인해보자.
아래 코드는 LogoutFilter 클래스의 doFilter 메소드의 내용이다.

보면 알겠지만, 인증 객체를 SecurityContext 에서 빼와서

  • this.handler.logout(request, response, auth)
  • this.logoutSuccessHandler.onLogoutSuccess(request, response, auth)

각각에게 인자값으로 보내는 것을 확인할 수 있다.
즉 위 2개의 메소드 내에서 어떤 처리를 한다는 의미다.
각 메소드의 내용을 하나하나 확인해보자.


1. handler.logout 메소드

위 그림은 this.handler.logout(request, response, auth) 의 내용이다.
메소드 내에서는 for loop 를 돌아서 갖고 있는 Handler 들의 작업을 수행한다.

logoutHandlers 리스트의 내용물을 자세히 보면 아래와 같은 것들이 있다.

  • SecurityConfig :
    • http.logout().addLogoutHandler api 로 추가한 커스텀 LogoutHandler 이다.
    • 가장 먼저 수행된다.
  • CookieCleaningLogoutHandler
    • http.logout().deleteCookies("remember-me"); api 에서 지정한 쿠키를 지움
  • CsrfLogoutHandler : CSRF 토큰 삭제
  • SecurityContextLogoutHandler : 인증객체, SecurityContext 삭제
  • LogoutSuccessEventPublishingLogoutHandler : 잘 모르겠다. 패스.



위의 핸들러 중에서도 SecurityContextLogoutHandler 가 중요하므로 해당 내용만 보자.

하는 일은 아래와 같다.

  • 세션 무효화
  • 인증객체 삭제
  • SecurityContext 삭제



2. logoutSuccessHandler.onLogoutSuccess 메소드

이 메소드는 http.logout().logoutSuccessHandler() api 를 통해서 직접
SuccessHandler를 작성했다면 해당 Handler가 호출되고 끝난다.


하지만 만약에 그렇지 않다면? SimpleUrlLogoutSuccessHandler 가 기본으로
호출되고 내부적으로 redirect 시킬 targetUrl 을 결정한 후, redirect 시킨다.

만약 어떠한 사용자 지정도 없다면 targetUrl="/login?logout" 이 되고,
만약 사용자가 http.logout().logoutSuccessUrl("/login") API 로 targetUrl 을 지정했다면 해당 위치로 redirect 시킨다.

profile
백엔드를 계속 배우고 있는 개발자입니다 😊

0개의 댓글