Spring Security Provider Customizing 하기 (1)
회사에서 새로운 업무를 받았다.

기존에 Spring Security를 사용하여 사용자 인증 (로그인) 처리가 구현되어 있는 서비스를 고도화하는 것이었다.
현재는 사내에서 사용중인 RDBMS 사용자 테이블에 있는 암호화된 비밀번호를 이용하여
DaoAuthenticationProvider를 구성하여 사용 중 구조였고,
나는 이를 단일 Provider가 아닌 사용자가 설정한 인증 방식에 따라서 다이내믹하게 Provider를 적용할 수 있게 하는 것이었다.
기본적으로 Spring Security는 하나의 Provider를 설정해 인증처리를 하는방식이기 때문에
Spring Security의 인증 흐름중 Provider를 적용하는 부분을 적당히 재정의 하는 것이 필요해 보였다.
즉, 어떻게 변경되어야 했냐면
[ 현재 ]
사내 RDBMS에 저장된 인증정보와 DaoAuthenticationProvider를 기반으로 단일 Provider 인증방식
[ 개발 후 ]
사용자별로 등록한 인증 방식에 따라 다양한 방식을 지원 ( LDAP / AD / Keycloak ) 하여
여러 인증 Provider를 다이나믹하게 지정하여 인증을 요청하는 방식
이었다.
일단 개발을 하기에 앞서서 Spring Security의 구조와 인증 흐름을 알고 있어야지만 내가 필요한 부분을 재정의 할 수 있기 때문에
Spring Security가 무엇이고 어떻게 사용되는지를 알아봤다.
Spring Security는 책 한 권으로 나올 만큼에 많은 분량이라 전부를 파악할 순 없었지만
내가 변경하고자 하는 설계에 필요한 부분들 위주로 공부해 보았다.
Spring Security Authentication Archtecture를 검색하면 이런 이미지가 많이 나온다.
이 그림을 바탕으로 어떤 방식으로 Spring Security가 어떤 방식으로 인증처리를 하는지 살펴보았다.
간략히 정리해보자면
1. Client가 웹서버에 무언가의 Request 요청
2. AuthenticationFilter에서 UsernamePasswordAuthenticationToken을 발급
- AuthenticationFilter : 각종 요청에 대해서 인증을 받은 Client인지 아닌지에 대한 여부를 판단하여 분기 처리를 진행
- UsernamePasswordAuthenticationToken : Authentication을 상속받은 AbstractAuthentication의 하위 클래스로,
User의 ID가 Principal 역할을, Password가 Credential의 역할을 하며 UsernamePasswordAuthenticationToken의 첫 번째 생성자는 인증 전의 객체를 생성하고, 두 번째 생성자는 인증의 완료된 객체를 생성
3. 생성된 UsernamePasswordToken을 AuthenticationManager에게 전달
- AuthenticationManager : 인증에 대한 부분은 AuthenticationManager를 통해서 처리하게 되는데,
실질적으로는 AuthenticationManager에 등록된 AuthenticationProvider에 처리
4. UsernamePasswordToken을 적절한 AuthenticationProvider에게 전달
- AuthenticationProvider : 실제 인증을 처리, 인증 전의 Authentication 객체를 받아서 인증이 완료된 객체를 반환
5. UserDetailService로 부터 ID를 기반으로 계정 정보를 조회
- UserDetailService : UserDetailService 인터페이스는 UserDetails 객체를 반환하는 단 하나의 메서드를 가지고 있고,
일반적으로 이를 구현한 클래스의 내부에 UserRepository를 주입받아 Database와 연결하여 처리
6. UserDetails를 상속받은 사용자 객체를 생성하여 반환
- UserDetails : 생성된 UserDetails 객체는 UsernamePasswordAuthenticationToken을 생성하기 위해 사용
7. ID를 기반으로 조회한 사용자 객체 결과를 반환
8. AuthenticationProvider에서 UserDetailService를 통해 조회한 계정 정보의 비밀번호 데이터와
입력받은 비밀번호가 일치하는지 확인하여, 일치한다면 인증된 토큰을 생성하여 반환
9. 인증이 완료된 UsernamePasswordAuthenticationToken을 AuthenticationFilter로 반환하고,
AuthenticationFilter에서는 LoginSuccessHandler로 전달
10. Authentication 객체를 SecutiryContextHolder에 저장하며 인증 마무리
- Authentication : 현재 접근하는 주체의 정보와 권한을 담는 인터페이스로 Authentication 객체는 SecutiryCotext에 저장되며,
SecurityContextHolder를 통해 SecurityContext에 접근하고, SecurityContext를 통해 Authentication에 접근가능
- SecurityContext : Authentication을 보관하는 역할
- SecurityContextHolder : 보안 주체의 세부 정보를 포함하여 응용 프로그램의 현재 보안 컨텍스트에 대한 세부 정보가 저장
줄글로 정리하니 굉장히 어렵고 복잡해 보이지만 한 줄로 정리해보자면
사용자가 무언가의 요청을 하면 인증이 처리된 사용자인지의 여부에 따라 Spring Security가 일련의 과정을 통해서 설정한 Provider 방식으로 인증을 처리하여 결괏값을 리턴하는 간단한 방식! (한 줄 같은 두줄)
흐름을 분석하다 보니 대게 많은 부분이 인터페이스로 구성되어 있었고,
실제 인증을 담당하는 AuthenticationProvider의 종류가 여러 개로 구성됨을 알 수 있어서
내가 원하는 대로 충분히 Customizing을 할 수 있을 거라고 판단하고 업무를 진행했다.
분석을 완료했으니 개발을 진행해야겠다.
실제 개발에 대한 내용은 다음 포스팅에서 정리해야지!
