OAuth(Open Authorization)란 표준 인증 프로토콜로 사용자들이 특정 플랫폼에 있는 자신의 정보에 대해 접근 권한을 부여하는 수단으로 사용이 된다.
사용자 인증(Authentication) 처리를 카카오, 네이버, 구글과 같이 OAuth 로그인 API를 제공하는 대형 서비스에게 맡기고 필요한 권한(Authorization)을 사용자 동의를 통해 받아서 필요한 Resource를 받아오는 것이다.
SecurityConfig
이전에는 스프링 시큐리티 설정을 WebSecurityConfigurerAdapter의 configure()로 했었지만 현재는 deprecated 되어서 SecurityFilterChain을 Bean으로 등록해야 한다.
oauth2Login() 밑에 부분을 보면 userService()에 CustomOAuth2UserService를 등록하는데 이는 함수형 인터페이스인 OAuth2UserService의 loadUser() 메서드를 구현한 것으로 로그인 성공 이후 Access Token을 이용해서 유저 정보(resource owner)를 받아서 OAuth2User(AuthenticatedPrincipal)를 반환한다.
CustomOAuth2UserService
사용자가 성공적으로 인증이 되면 OAuth2User에는 GrantedAutority Set이 포함되어 있는데 이 GrantedAuthority 컬렉션은 반환할 OAuth2AuthenticationToken에 매핑하는데 사용 될 수 있다.
매핑 옵션으로는 GrantedAuthoritiesMapper 또는 OAuth2UserService를 사용한 Delegation-based 전략이 있는데 후자의 경우 OAuth2UserRequest 및 OAuth2User에 대한 액세스를 제공하기 때문에 더 유연하고 향상된 방법이라고 한다. 나는 DefaultOAuth2UserService(표준 OAuth 2.0 공급자를 지원하는 OAuth2UserService의 구현)를 사용하였다.
DefaultOAuth2UserService 생성자를 통해 userRequest로 OAuth2User를 가져오고 Attributes 정보로 User Entity를 조회(기존 회원)하여 가져오거나 저장(새로운 회원)한다.
UserNameAttributeName()은 키 값으로 카카오의 경우 id가 된다
마지막으로 OAuth2User(AuthenticatedPrincipal)를 생성하여 반환한다.
아래는 전체적인 순서도와 내부적으로 어떻게 처리가 되는지 정리를 해봤다.
순서도
먼저 로그인을 누르면 프론트에서 "/oauth2/authorization/kakao"로 요청을 보내고 OAuth2AuthorizationRequestRedirectFilter는 기본 경로로 받아서 yml 파일에 정의해둔 authorization url로 요청을 보낸다.
이때 앱의 Rest API 키 값인 client_id랑 Authorization Code를 받을 redirect_url, response_type을 같이 보내는데 이 정보는 yml 파일에 저장을 해놓는다.
참고로 스프링 시큐리티가 yml 파일을 읽어서 OAuth2ClientProperties를 생성하고 ClientRegistration 객체(kakao, google, naver ..)를 생성해서 InMemoryClientRegistrationRepository에 저장한다.
위에서 말한 OAuth2AuthorizationRequestRedirectFilter는 OAuth2AUthorizationRequestResolver의 resolve()를 호출하는데 로그인 요청 URL에서 registrationId를 확인하고 InMemoryClientRegistrationRepository에서 ClientRegistration 객체를 꺼내 OAuth2AuthorizagtionRequest 객체를 만든다.
그리고 OAuth2AuthorizationRequest를 받아서 redirect를 보내면 카카오 로그인 창이 뜨게 되는 것이다.
사용자가 카카오 로그인 인증을 성공하면 카카오 서버에서는 위에서 요청시 보낸 redirect_url로 인가 코드(Authorization Code)를 보내기 때문에 OAuth2LoginAuthenticationFilter의 기본 경로에 맞춰서 redirect_url을 yml 파일에 정의해두었다.
OAuth2LoginAuthenticationFilter도 OAuth2AuthorizationRequestRedirectFilter처럼 registrationId를 통해 ClientRegistation을 가져오고 Authorization Code를 사용해서 Access Token을 가져온다.
OAuth2LoginAuthenticationProvider는 authenticate() 메서드를 실행하여 Access Token을 받고 loadUser 메서드를 통해 유저 정보를 가져오는 처리까지 하는데 이때 loadUser()를 실행하는 UserService가 처음에 SecuriryConfig에서 userService()에 주입해준 CustomUserService가 된다.
다음에는 JWT를 발급하는 부분을 처리해봐야겠다.
[참고]
'Spring' 카테고리의 다른 글
[Spring] 댓글 더보기 기능 구현 (LIMIT) (0) | 2023.04.28 |
---|---|
Servlet과 Servlet Container (5) | 2023.03.25 |
[Spring Rest Docs] Rest API 문서화 (0) | 2023.01.28 |
[Spring] 템플릿 메서드 패턴, 팩토리 메서드 패턴 (0) | 2022.10.04 |
[SpringFox] Swagger3.0 사용하기 1 (Spring Security, JWT) (0) | 2022.09.29 |