이번시간에는 Spring, OAuth 를 이용하여 소셜로그인을 구현해보도록 하겠습니다
소셜로그인을 사용하는 가장 큰 이유중 하나는 접근성이 뛰어나기 때문입니다
소셜로그인의 흐름을 이해하기 위해 다른 블로그를 참고하여 Flow를 아래와 작성해보았습니다
Flow 흐름도
시퀀스 다이어그램
소셜로그인을 구현하기 위해 아래의 블로그를 많이 참고하였습니다
소셜로그인을 구현하는 방법은 다양합니다 한가지로 정해진 것이 아닌 요구사항에 따라 어떻게 구현하는지가 다를 수 있습니다
예를 들어
1. 클라이언트에서 엑세스토큰을 구글 인증 서버로부터 전달받고 api서버에서 리소스서버로 유저정보를 요청하게 구현
2. 클라이언트에서는 로그인만 진행하고 엑세스토큰부터 리소스서버로부터 유저 정보 요청까지 모든과정을 api서버가 진행하도로 구현
할 수 있습니다
저는 2번과정으로 진행하였으며 이유는 이 방식이 OAuth 에서 권장되고 있는 방식이기 때문입니다
위의 시퀀스 다이어그램을 보게되면 보라색, 노란색칸에 있는 인증서버, 리소스서버가 있습니다
인증서버는 사용자를 인증하는 서버이며 사용자가 로그인에 성공하면 api 서버가 사용자의 정보를 얻을 수 있도록(=리소스서버로 정보를 요청할 수 있도록) auth code, access token을 주게되며 리소스 서버는 사용자의 정보들이 저장되어있는 서버로 access token을 가지고 정보를 요청하게 되면 정보를 반환하는 서버입니다
상단에도 정리를 해보았지만 다시 요청흐름을 정리해보면
- 클라이언트가 api서버로 사용자인증을 위해 소셜 로그인페이지 리다이렉션을 요청합니다
- api서버는 설정한 내용 값대로 소셜 로그인요청 페이지를 반환합니다
- 여기서 로그인 요청 페이지의 역할은 구글의 인증 access token을 받기 위함입니다
- 예시링크(https://accounts.google.com/?client_id=123&scope=profile,email&redirect_uri=http://localhost)
- 쿼리 스트링을 살펴보면 client_id는 123, scope는 profile과 email, redirect_uri는 http://localhost임을 알 수 있습니다
- 클라이언트는 소셜로그인 페이지를 사용자에게 보여주며 사용자는 로그인을 완료합니다
이때부터 api서버(백엔드), 구글 인증서버, 구글 리소스서버가 서로 통신을 시작합니다
- api서버는 로그인 진행 내용을 바탕으로 구글 인증 서버로 인증 code를 요청합니다
- 인증 code는 리소스서버로 정보를 요청하기 위해 필요한 access token을 발급받기 위한 용도입니다
- 인증서버로 부터 받은 인증 code로 access token을 다시 요청합니다
- access token을 가지고 리소스 서버로 유저 정보를 요청합니다
- 이때 전달받은 유저정보를 바탕으로 회원가입 및 JWT(사이트이용을 위한 access token, refresh token)를 생성을 합니다
- 생성된 JWT를 클라이언트로 반환합니다
OAuth2 인증 과정
config에서 설정한 oauth2Login() 로 OAuth2 로그인 관련 처리를 설정할 수 있습니다
.oauth2Login()
.authorizationEndpoint()
.baseUri("/oauth2/authorization")
.authorizationRequestRepository(oAuth2AuthorizationRequestBasedOnCookieRepository())
.and()
.redirectionEndpoint()
.baseUri("/*/oauth2/code/*")
.and()
.userInfoEndpoint()
.userService(oAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler())
.failureHandler(oAuth2AuthenticationFailureHandler())
.and().addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
AbstractAuthenticationProcessingFilter 에서 OAuth2 로그인 과정을 호출합니다
Resource Server에서 넘겨주는 정보를 토대로 OAuth2LoginAuthenticationFilter의 attemptAuthentication() 에서 인증 과정을 수행합니다
attemptAuthentication() 처리 과정에서 OAuth2AuthenticationToken을 생성하기 위해 OAuth2LoginAuthenticationProvider의 authenticate()를 호출합니다
authenticate() 처리 과정에서 OAuth2USer를 생성하기 위해 OAuth2UserService의 loadUser()를 호출합니다
OAuth2UserService의 기본 구현체는 DefaultOAuth2UserService이지만, 커스텀한 OAuth2User를 반환하도록 구현하고 싶었으므로 직접 구현한 CustomOAuth2UserService의 loadUser()가 호출합니다