이 시리즈에 나오는 모든 내용은 인프런 인터넷 강의 - 스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 에서 기반된 것입니다. 그리고 여기서 인용되는 PPT 이미지 또한 모두 해당 강의에서 가져왔음을 알립니다.
SecurityContext 객체를 생성, 저장, 조회를 하는 필터이다.
이 필터는 인증 안한 상태(익명사용자), 인증 시, 인증 후에 따른 동작을 달리한다.
이번 게시물에서는
SecurityContextPersistenceFilter
라는 명칭 대신SCP-Filter
라고 부르겠습니다!
너무 길어서요 😅
SCP-Filter
에서 SecurityContext 생성 ==> SecurityContextHolder 에 저장
한다.
이후에 익명 사용자에 대한 처리를 위한 AnonymouseAuthenticaitonFilter
에서 익명 사용자용 인증토큰(= AnonymouseAuthenticationToken
)을 생성하여 SCP-Filter
에서 생성한 SecurityContext 에 저장한다.
SCP-Filter
에서 SecurityContext 생성 ==> SecurityContextHolder 에 저장
한다.
UsernamePasswordAuthenticationFilter
에서 인증을 성공하면 인증 토큰(UsernamePasswordAuthentication
)을 SecurityContext 에 저장한다.
최종적으로 Session
에도 SecurityContext 를 저장한다.
Session 에서 SecurityContext 를 꺼내서 SecurityContextHolder 에 저장한다.
SecurityContext 안에 Authentication 객체 존재 시, 인증 상태를 계속 유지하게 된다.
SCP-Filter
안에 있는 HttpSecurityContextRepository
가 SecurityContext
를 생성하고 조회한다.스프링 부트 애플리케이션을 실행시키면 SCP-Filter
에서 doFilter 메소드가 동작하고 이 과정에서 this.repo.loadContext(holder);
를 호출하여 HttpSecurityContextRepository
에서 SecurityContext
로딩을 시도한다.
여기서는 readSecurityContextFromSession 이라는 메소드명을 보면 알다시피, 현재 세션에 저장된 SecurityContext 가 있는지를 확인한다.
그래서 있으면 가져오고, 없으면 null 을 반환한다. 현재는 세션 자체가 null 이라서 이 메소드의 결과도 null 을 반환한다.
참고: this.springSecurityContextKey = "SPRING_SECURITY_CONTEXT" 이다.
이후에 null 이 return 되고 나면 다시 SCP-Filter.doFilter
메소드로 돌아온다.
그리고 나서 generateNewContext() 메소드를 호출하여 새로운 SecurityContext 생성을 시도한다.
인증 객체를 갖고 있지 않는 비어있는 SecurityContext
를 생성 및 반환한다.
최종적으로 SCP-Filter.doFilter
에서는 계속해서 체이닝된 필터들을 호출하게 된다.
현재는 인증을 하지 않은 사용자가 자원에 접근하는 것이므로
AnonymousAuthenticationFilter
에 걸리게 된다.
이 필터에서는 위처럼 익명사용자 인증토큰을 생성하고,
SecurityContext 를 새로 또 생성한다.
그리고 SecurityContext 에 인증토큰을 세팅하고, 최종적으로
해당 SecurityContext 를 Holder 에 저장하고 다음 필터를 호출한다.
참고: 익명 사용자 필터를 거치게 되면 Session 에 SecurityContext 를 저장하는 과정을 거치지 않는다!
최종적으로 response 를 보내기 전에 기존 SecurityContext 정보를 지운다.
위 익명 사용자 접근 시와 비슷한 게 많으므로 몇가지 과정은 설명을 생략 또는 요약한다.
일단 SecurityContext 객체를 미리 빼와서 contextAfterChainExecution 에 저장.
SecurityContextHolder.clearContext(); 를 호출하여 SecurityContextHolder 내부의 ThreadLocal 내에서 SecurityContext 를 지운다. 하지만 현재 contextAfterChainExecution 에 미리 저장했기 때문에 계속해서 SecurityContext에 접근이 가능하다. 이러는 이유는 이후 메소드에 SecurityContext 객체를 인자로 사용하기 위함이다.
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
이게 현재 세션에 SecurityContext 정보를 저장할지 말지를 결정한다.
자세한 내용은 바로 아래에 있다.
일단 현재 세션에 SecurityContext 정보가 있는지 뒤져본다.
그리고 이어서 내부에 있는 인증 객체를 꺼내서 null 인지, 익명 토큰인지를 알아본다.
만약 둘 중 하나라도 true
면 HttpSession
에 SecurityContext
를 저장하는
작업을 하지 않고 메소드가 return 된다.
반대로 SecurityContext
가 보관하는 인증객체가 인증에 성공한 인증 객체면 HttpSession
에 SecurityContext
를 저장하는 과정을 거치게 된다.
this.repo.loadContext 메소드에 의해서 현재 세션에서 저장되어 있는 SecurityContext 를 읽어온다. 인증 이후면 SecurityContext 를 반환받는다.
현재는 인증 이후이므로 당연히 SecurityContext 가 존재한다.
꺼내온 SecurityContext 를 이제 SecurityContextHolder 에 저장해서 코드 내에서 전역적으로 사용할 수 있도록 한다.
이후에 마찬가지로 다음 필터에게 요청을 위임한다.
이전과 달리 현재 SecurityContext 에 어떤 변화가 있는지를 확인한다.
변화가 없다면 세션 저장 과정을 거치지 않는다. 그냥 기존 세션에 있는 SecurityContext 를 계속 쓰겠다는 의미다.