Spring Security 6 - 세션 기반 인증 유지 | Authentication Persistence

결과본

https://github.com/jsween5723/spring-security-tistory/tree/dd40c435a818a75a28c9bab2db165867e023d907

세션에 저장 여부 확인

https://github.com/jsween5723/spring-security-tistory/commit/428d69c226f2fe499f7518fc2ccb7cef87e046ca
해당 커밋을 통해 테스트

비 로그인 시

 

간편 로그인 후

 

일반 로그인 후

 

위처럼 로그인 이후에는 요청시에 Authentication이 유지되는 것을 알 수 있다. 매 요청 시마다 이렇게 Authentication이 유지되어 컨트롤러에서 반환될 수 있는 이유는 SecurityContextHolderFilter가 있기 때문이다.

해당 필터에서 HttpSessionSecurityContextRepository를 활용하여 이전에 세션에 저장한 context를 불러와

현재 SecurityContextHolder에 setContext를 활용해 스레드로컬에 주입하는 역할을 한다.

 

이전 버전에서는 SecuritycontextPersistenceFilter가 존재했지만, 이제는 그 필터를 사용하지 않고 이전에 AbstractAuthenticationProcessingFilter.successfulAuthentication 처럼 명시적으로 필터중 인증정보 저장이 필요한 부분에서 securityContextRepository.saveContext(securityContext, httpServletRequest, httpServletResponse);를 명시적으로 사용하도록 변경되었다.

https://sween.tistory.com/17#section-8

 

Spring Security 6 - 일반 로그인 | 폼 로그인

질문은 자유롭게 남겨주세요!추가된 코드와 커밋private final ObjectMapper objectMapper; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.cors(AbstractHttpConfigurer::disable).csrf(AbstractHtt

sween.tistory.com

 

활용방법

Authentication을 포함하는 SecurityContext가 세션에 저장되는 것을 알게됐으므로 success handler에서 추가적으로 변조를 주어
간편 로그인 시와 일반 로그인 시 공통 객체로 변환하는 단계를 추가할 수 있다. 공통객체로 변환하면 컨트롤러에서 두 클래스를 분리해 로직을 고민할 필요성이 없어진다 Me 인터페이스와 POJO인 Provider 클래스를 추가하여 활용한다.

 

4. Authentication Persistence 활용 공통 객체로 전환 · jsween5723/spring-security-tistory@4df29e3

jsween5723 committed Sep 1, 2024

github.com

 

 

4. 커스텀 컨텍스트 레포지터리 추가 · jsween5723/spring-security-tistory@dd40c43

jsween5723 committed Sep 2, 2024

github.com

 

SecurityContextHolder.getContext().setAuthentication

를 통해 login success handler의 현재 컨텍스트에 저장만 해도 정상동작 하긴 하지만, 이는 현재 세션이 인메모리 레포지터리여서 컨텍스트 자체가 참조변수이기 때문에 정상동작하는 것이다.

추후 레디스나 다른 저장소로 확장할 경우, session.setAttribute 메소드는 SecurityContextRepository 내부에서만 동작하기 때문에 저장되지 않는다. 

 - 프론트엔드에서 state의 속성을 변화시킨다고 재렌더링 되지 않는것과 같은 원리이다. 추후 Spring Session을 설명할 때 함께 설명하겠다.

물론 SessionManagementFilter에서 한번 더 securityContextRepository.saveContext를 호출하지만, 로그인 시에는 곧바로 redirect하거나 반환하도록 구현하는 것이 일반적이기 때문에 해당 필터까지 진행되지 않는다. success handler에서 변경하면 저장될 틈도 없이 스레드가 종료되는 것이다.

따라서 저장 전에 컨버팅을 하기위해 위에있는 securityContextRepository.saveContext를 오버라이딩하는 클래스를 만들어 주입하겠다.

https://docs.spring.io/spring-security/reference/servlet/authentication/persistence.html#requestattributesecuritycontextrepository

class MeHttpSessionSecurityContextRepository extends HttpSessionSecurityContextRepository {
    @Override
    public void saveContext(SecurityContext context, HttpServletRequest request,
        HttpServletResponse response) {
        Authentication authentication = context.getAuthentication();
        Object principal = authentication.getPrincipal();
        if (principal instanceof Me me) {
            context.setAuthentication(new UsernamePasswordAuthenticationToken(me.toProvider(), null,
                authentication.getAuthorities()));
        }
        super.saveContext(context, request, response);
    }
}

사전에 지정한 Me 구현체가 Principal일 때만 변경하도록 제한한다.

        http.securityContext(conf->conf.securityContextRepository(new MeHttpSessionSecurityContextRepository()));

필터체인에 설정을 추가한다.
정상 동작하는 것을 확인할 수 있다.

 

반응형
LIST