OAuth2 AutoConfigure 과정에서 어떤 3자 인증을 제공하며 어떤 url로 요청할지 등 yaml에 등록한 정보를 기반으로 다른 Authorization Server와 소통을 하게 된다.
내부 스펙을 보면 여러가지 컬럼이 있지만, 각 외부 Oauth2인증을 제공하는 사이트 문서를 보고 필요한 설정들만 해놓으면 된다.
Spring Security OAuth2 구현체에서는 CodeGrantFilter가 존재해 실제 코드 기반으로 외부 Authorization Server와 토큰을 교환한다.
SpringSercurity OAuth 매커니즘 분석
FilterChainProxy Filter 모습을 보면 3,4,5번째에 Oauth2xxxxxFilter가 2개 추가된 것을 볼 수 있다.
이제 FIlter마다 어떤일을 수행하는지 분석할 것이다.
OAuth2AuthorizationRequestRedirectFilter
OAuth2AuthorizationRequestRedirectFilter 에서는에서는 resolve 메소드를 호출하는 것을 볼 수있다.
내부에서 DefaultOauth2AuthorizationRequestResolver에 의해 redircet url을 리턴하고 사용자를 해당 외부 로그인 화면으로 이동시킨다. 실제 로그인 화면으로 이동시키는 html은 DefaultLoginPageGeneratorFilter가 만들어준다
이것이 oauth2 인증 전 사용자에게 보여지는 화면 html이다. [카카오 OAuth2 예]
registrationId를 뽑아내는 행위를 하는데 보통 Oauth2의 패턴은 /oauth2/authorization/{registrationId} 패턴의 URL 요청을 처리한다.
{registrationId} 부분에는 인증 Provider 식별키(kakako || naver || google 등)가 입력된다.
그리고 resolve가 정상적으로 반환되면 실제 sendxx 메소드에서 리디렉션 정보를 입력한다.
AuthorizationRequestRepository 인터페이스 구현체에는 application.yml 파일에 설정한 OAuth 연동 정보가 저장되어 있다.
인증 Provider 식별키로 AuthorizationRequestRepository 인터페이스에서 OAuth 연동 정보를 가져옴
authorization-uri 주소로 사용자를 리다이렉트 시킴
그리고 이제 실제 oauth2인증을 진행하기 위해 해당 버튼을 누르게 되면 외부에서 인증 절차(카카오, 네이버, 구글 등의 페이지) 를 밟을 뷰가 보여질 것이다. 인증이 완료되면 Authorization Server(외부 카카오 인증 서버, 네이버 인증서버 등)가 우리 서버로 1회성 승인 code를 보내줄 것이다.
이제 그것을 기반으로 token을 요청하고 응답받게 되는데 스프링부트 oauth2 내부에서는 OAuthLoginAuthentication 객체가 이를 수행한다.
OAuthLoginAuthentication
위에서 언급했듯이 일회성 승인코드와 state값을 받게 된다.
code는 토큰을 교환하기 위한 우리가 만든 서버임을 사전에 등록했던 서버에게만 주는 것이고 state는 CSRF 공격을 막기위한 임의의 문자열이다.
attemptAuthentication 메소드 내부를 보면 AuthenticationManager, AuthenticationProvider 와 같은 SpringSecurity 기본 구조를 그대로 활용하고 있다.
실제 내부로 들어가 보면 provider에 의해 인증을 진행하고 있다.
provider 정보를 보면 3개의 provider 구현체들이 있다.
autenticate 메소드를 호출하는 내부 구현체를 따라가면
실제로 외부 Authorization 서버에서 부여받은 일회성코드를 기반으로 토큰 발행을 요청하게 되는 부분이다.
해당 부분에는 일회성 코드를 기반으로 토큰 발생을 요청시킬 수 있는 authorizationCodeAuthenticationProvider를 호출하게 되고 이름 그대로 (일회성)코드 기반으로 토큰을 발생시키는 것을 알 수 있다.
내부로 들어가보면 autenticate메소드 를 요청한다.
실제 토큰 발행 요청과 인증서버에 있는 정보 가져오기는 해당 메소드에서 모든 이루어지게 된다
실제 토큰 발행은 authorizationCodeAuthenticationProvider 구현체가 직접 수행한다.
해당 구현체(authorizationCodeAuthenticationProvider)는OAuth2LoginAuthenticationProvider 상위 객체인 AuthenticationProvider 하위 타입중 하나이다. [이름이 진짜 비슷하다. 주의]
실제로 CodeAuthenticationProvider를 보면 getTokenResponse 메소드를 호출하는 것이 일회성 코드 기반으로 엑세스 토큰을 가져온다.
OAuth2AccessTokenResponseClient는 상위 타입의 인터페이스이다 실제 호출은 DefaultAuthorizationCodeTokenResponseClient 에서 일어난다.
일회성 코드 기반으로 토큰 발행 요청을 하고 응답을 받은 후 인증 서버에 관련 정보를 가져오는 실제 로직이다.
토큰 기반으로 인증서버에 저장된 정보를 가져오게 된다. loadByUser 이름처럼 사용자를 서버로 적재하는 의미로 내부 로직을 보면 exchange에 호출이 이루어지고 있는 것을 확인할 수 있다.
토큰 방행이 완료되면 OAuth2User 객체에 사전에 카카오에서 필요한 정보들을 정확히 가져오며 yaml에서 설정한 파일 기반으로 요청한 것을 알 수 있다.
해당 인증과정에서 보면 provider, manager 뿐만 아니라 사용하는 객체들도 기존 SpringSecurity 객체들에서 확장한 것이다.
스프링 시큐리티 인증 플로우를 보면 대표적으로 UserNamePasswordToken 구현체를 사용하지만 oauth2에서는 Oauth2AuthenticationToken을 사용하여 객체를 생성한다.
OAuth2User 상속구조
Oauth2AuthenticationToken 상속구조
실제로 Authentication 인터페이스를 구현한 객체들이 여러가지가 있는 것을 알 수 있고 oauth2 라이브러리를 추가함과 동시에 더많은 Token 구현체가 생긴것을 알 수 있다.
이렇게 하위 클래스에 모든 인증이 완료된 후에는 상위 클래스 successfulxxx 메소드를 호출하고