처음 활용할때 인터페이스 명이 너무 길고 어떤 역할을 할 때 써야하는지 잘 알지 못했는데, 오늘 기회가 생겨 정리하게 되었다.
위 인터페이스는 OAuth 2.0 로그인 프로세스 중에 OAuth 2.0 또는 OpenID Connect(OIDC) 제공자(Google, Facebook 등)로부터 사용자 정보 검색 및 처리를 담당하는 Sprint Security에서 제공하는 클래스이다.
사용자가 OAuth 2.0 공급자를 사용하여 인증을 시도하면 Spring Security가 자동으로 OAuth 2.0 인증 흐름을 처리한다. 이 프로세스의 핵심 구성 요소 중 하나는 사용자가 인증된 후 OAuth 2.0 공급자로부터 사용자 프로필 정보를 가져오는 'DefaultOAuth2UserService'이다.
: 사용자가 Google(또는 다른 OAuth 공급자)로 로그인하기로 선택하면 Sprint Security는 사용자를 공급자의 인증 끝점으로 리디렉션한다.
: 사용자는 Google에 로그인하고 자신의 프로필 정보에 액세스 할 수 있는 권한을 애플리케이션에 부여
: 권한이 부여되면 Google은 승인 코드를 사용하여 사용자를 애플리케이션에 지정된 리디렉션 URI로 다시 리디렉션
: Spring Security는 OAuth2LoginAuthenticationFilter 를 사용하여 OAuth 공급자의 토큰 엔드 포인트에 요청하여 액세스 토큰에 대한 인증 코드 교환을 자동으로 처리
: 액세스 토큰을 얻으면 DefaultOAuth2UserService가 호출된다. 이메일, 프로필사진, 이름 등과 같은 인증된 사용자의 세부 정보를 검색하기 위해 액세스 토큰을 사용하여 OAuth 공급자의 사용자 정보 엔드포인트에 요청
: DefaultOAuth2UserService는 공급자의 응답을 처리하고 사용자 정보를 추출한 후 이를 OAuth2User 객체에 래핑합니다. 그런 다음 이 객체는 Spring Security에서 인증된 주체를 생성하는 데 사용
: 'OAuth2User' 개체는 'SecurityContext'에 저장되며 사용자는 인증된 것으로 간주도니다. 애플리케이션은 이 정보를 사용하여 사용자 세션을 관리하거나 데이터베이스에 저장할 수 있다.
처음 OAuth 2.0을 예시 코드를 따라치며 생각했던 궁금증이 있었다.
"왜 컨트롤러로 URL 매핑설정을 하지 않았는데 어떻게 돌아가는거지?"
이것에 스스로에 대한 답변을 다뤄보겠다.
: Sprint Security의 필터(OAuth2LoginAuthenticationFilter, DefaultOAuth2UserService 등)은 공급자(Authorization Server)로의 리디렉션, 토큰용 코드 교환, 사용자 정보 가져오기에 이르기짜기 전체 OAuth 흐름을 관리한다.
: OAuth 2.0 로그인을 위해 Sprint Security를 구성하면 사용자 정의 컨트롤러 코드를 작성할 필요 없이 로그인 프로세스를 처리하는 데 필요한 엔드포인터와 필터가 자동으로 설정된다.
: OAuth 2.0 로그인 흐름은 컨트롤러가 아닌 보안 필터에 의해 시작되고 처리된다. 이러한 필터는 요청을 가로채고 OAuth 2.0을 사용하여 사용자를 인증해야 하는지 결정한다. 인증 해야한다면, OAuth 공급자로 리디렉션하고 후속 통신을 처리한다.
: 사용자가 권한을 부여한 후 OAuth 공급자는 컨트롤러가 아닌 Sprint Security에서 관리하는 콜백 URL로 사용자를 다시 리디렉션한다. 다음, Spring Security는 OAuth 공급자와 상호작용하고, 사용자에 대한 인증된 세션을 생성하여 인증프로세스를 완료한다.
사용자가 보안 리소스에 액세스하려고 합니다 → Spring Security는 인증을 위해 사용자를 Google로 리디렉션합니다.
사용자를 인증하고 권한을 부여합니다 → Google은 인증 코드를 사용하여 사용자를 애플리케이션으로 다시 리디렉션합니다.
Spring Security는 코드를 액세스 토큰으로 교환합니다 → DefaultOAuth2UserService는 액세스 토큰을 사용하여 Google에서 사용자 정보를 검색합니다.
사용자가 인증되었습니다 → Spring Security가 인증된 사용자 세션 생성을 처리합니다.
@Service
@RequiredArgsConstructor
@Slf4j
public class CustomOauth2UserService extends DefaultOAuth2UserService {
private final MemberRepository memberRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
//super.loadUser(userRequest): 이는 Google과 같은 OAuth2 제공자로부터 사용자 세부정보를 로드하는 기본 구현을 호출합니다. 반환된 OAuth2User에는 사용자의 속성이 포함됩니다.
OAuth2User oAuth2User = super.loadUser(userRequest);
// 유져의요청을 받은 attirubutes를 로그로 찍어본다.
// 공급자 id 식별 ex) google
String provider = userRequest.getClientRegistration().getRegistrationId();
OAuth2UserInfo oAuth2UserInfo = null;
// 뒤에 진행할 다른 소셜 서비스 로그인을 위해 구분 => 구글
if(provider.equals("google")){
log.info("구글 로그인");
// oAuth2UserInfo 인터페이스를 구현하는 구글유져 디테일의 attributes를 받는다.
oAuth2UserInfo = new GoogleUserDetails(oAuth2User.getAttributes());
}else if (provider.equals("kakao")) {
log.info("카카오 로그인");
oAuth2UserInfo = new KakaoUserDetails(oAuth2User.getAttributes());
}else if (provider.equals("naver")) {
log.info("네이버 로그인");
oAuth2UserInfo = new NaverUserDetails(oAuth2User.getAttributes());
}
String providerId = oAuth2UserInfo.getProviderId();
String email = oAuth2UserInfo.getEmail();
String loginId = provider + "_" + providerId;
String name = oAuth2UserInfo.getName();
Member findMember = memberRepository.findByLoginId(loginId);
Member member;
log.info("getAttributes : {}",oAuth2User.getAttributes());
// 존재하지 않다면 바로 회원가입
if (findMember == null) {
member = Member.builder()
.loginId(loginId)
.name(name)
.provider(provider)
.providerId(providerId)
.role(MemberRole.USER)
.build();
log.info("Saving member: {}", member);
memberRepository.save(member);
} else{
// 존재한다면, 찾아서 member에 저장
member = findMember;
}
return new CustomOauth2UserDetails(member, oAuth2User.getAttributes());
}
loadUser 메소드
:이 메소드는 OAuth 2.0 인증이 성공한 후, OAuth 2.0 제공자로부터 사용자 정보를 가져오고 이를 처리하는 메소드이다.
super.loadUser(userRequest)를 호출하여 기본적인 사용자 정보 로딩을 수행합니다. 이 호출로 OAuth2User 객체를 얻을 수 있습니다.
CustomOauth2UserDetails 객체 반환
:CustomOauth2UserDetails 객체를 반환한다. 이 객체는 Spring Security의 SecurityContext에 저장되어 애플리케이션에서 사용자 정보를 참조할 수 있게 된다.
이 더운날 모두가 각자 소프트웨어하는 시간을 가졌다. 오늘은 Sprint Security의 정수 DefaultOAuth2UserService를 사용해서 어떻게 어플리케이션 입장에서 정의한 컨트롤러 없이 OAuth 2.0 과정이 돌아가게되는지 공부해 봤다. 이제 로그인 관련해서는 JWT 나 access Token 발급에 대해 과정이 남아있지만, 먼저 상품 관련한 기능을 만들고 고객의 장바구니를 구분지을 때 다시 생각해보도록 하자. 오늘도 고생했따