-
spring security white list 어노테이션 만들기글또 2023. 11. 1. 23:11
안녕하세요.
이번에는 spring security의 PreAuthorize를 커스텀한 이야기를 해보려 합니다.
목표는 인증인가 없이도 white list에 등록된 ip라면 자원에 접근이 가능하도록 하는 것입니다.
먼저 이게 필요했던 이유는 MSA 구조에서 타 서비스 간에 로그인 필요 없이 접근이 가능해야 하기 때문이었습니다.
Auth Gateway를 썼다면 인증 절차는 앞에서 진행이 되었다고 판단을 하고 허용했겠지만(그림 1) 현재 구조는 각 서비스가 각자의 방식대로 인증을 구현하고 있었기에 white list를 구현해야 했습니다.(그림 2)
모든 서비스는 spring security를 통해 구현되어 있기 때문에 spring security를 커스텀해야 합니다.
이를 어떻게 구현할까요?
먼저 생각한 것은 config에서 사용할 수 있는 hasIpAddress입니다.
.
그럼 아래와 같은 방법으로 하면 되지 않나?라고 생각했지만 hasIpAddress는 Web Security Expression이고 preAuthorize는 Method Security Expressions이기 때문에 preAuthorize에서는 사용할 수 없습니다.
config에서 작성하는 것은 필터 체인 단에서 검사를 진행하는 것이고 메서드 혹은 클래스에 정의한 것은 스프링 컨텍스트안에서 검사를 진행하게 됩니다.
1. 필터체인 단에서는 FilterSecurityInterceptor가 AccessDecisionManager를 통해 인증 인가를 검사하고
2. 스프링 컨텍스트안에서는 MethodSecurityInterceptor가 메서드 단에 붙어있는 어노테이션에 따라 검증을 진행합니다.
그렇다면 저 둘에게서 통과를 허락받는 다면 인증인가 없이 자원에 접근할 수 있게 됩니다.
구현
저 둘에게 허락을 받는다는 것은 AccessDecisionManager에 속한 투표자들 중 하나에게만 통과를 하면 된다는 것입니다.(AffirmativeBased 설정 기준.)
그래서 white list를 설정을 보고 찬성을 던져줄 투표자를 넣어주면 될 거 같습니다.
저 둘은 AccessDecisionManager를 각자 구현하고 있으니 따로따로 설정을 해줘야 합니다.
1. 필터 체인 계층에서 커스텀
FilterSecurityInterceptor에서 사용하는 AccessDecisionManager에 whilte list에 무조건 찬성표를 던지는 voter를 추가하면 됩니다.
2. 스프링 컨텍스트에서 커스텀
필터체인을 넘어서 스프링 컨텍스트까지 왔습니다. 다만 인증 정보가 없기 때문에 인가를 진행할 수 없습니다. 우리는 여기서도 white list를 검사해서 넘겨주면 됩니다.
이번에는 voter를 넣어줄 필요가 없습니다. SPEL 핸들링하는 PreInvocationAuthorizationAdviceVoter를 활용해 볼 생각입니다.
PreInvocationAuthorizationAdviceVoter는 메서드에 붙어있는 @PreAuthorize에 대한 검증을 진행합니다.
@PreAuthorize는 SPEL을 사용할 수 있으므로 SPEL로 white list를 판단하는 bean을 호출해 주면 됩니다.
코드
1. FilterSecurityInterceptor에 voter 추가
FilterSecurityInterceptor에서 사용하는 AccessDecisionManager에 white list voter를 넣어주어 무조건 인증을 찬성하면 됩니다.
2. 스프링 컨텍스트 커스텀
메서드 계층은 PreAuthorize를 사용하겠습니다. 이는 PreInvocationAuthorizationAdviceVoter에서 사용됩니다.
ip는 RequestContextHolder에서 꺼내서 사용하고 사용자의 권한은 SercurityContextHolder에서 꺼내서 사용합니다.
둘 다 thread local에 저장되어 있는 값이라 thread safe 합니다.
매번 SPEL을 작성하는 것은 힘드니 전용 어노테이션을 생성해서 사용할 수 있습니다.
이를 통해 Annotation 사용 시 설정했던 권한 + properties에 설정한 whitelist를 annotation 방식으로 구현할 수 있습니다.
+
추가로 PreAuthorize는 결국 PreinvocationAuthorizationAdviceVoter에 의해서 실행되고 결정됩니다.
그렇기에 voter 자체를 추가하는 방법이 있을 수 있지만 부분적으로 white list를 도입하고 싶었기에 annotation 방법을 사용했습니다.
전역적으로 white list 만들고 사용하고 싶다면 voter를 MethodSecurityInterceptor의 accessDecisionManager에 voter를 추가하면 됩니다.
글에서 사용된 Code
'글또' 카테고리의 다른 글
테크니컬 라이팅 강의를 듣고나서 (0) 2024.01.06 Spring Async에서 SecurityContextHolder 접근하기 (0) 2023.11.04 Spring kafka에서 Multi Type Converter 여행기 (0) 2023.09.25 인프콘 2023 (0) 2023.08.22 스프링 카프카 리트라이 정책 정하기 (0) 2023.08.08