https://console.cloud.google.com/welcome/new
1. 콘솔 접속








** 승인된 리디렉션 URI를 꼭 설정해야한다!
(해당 링크로 요청을 해야 google 로그인 화면이 뜸)
build.gradle
oauth2 의존성 추가.
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
나는 진행하던 프로젝트에 소셜로그인을 추가하는거라 이미 있지만, 첫 설정이라면 spring security도 추가해야함
implementation 'org.springframework.boot:spring-boot-starter-security'
application.yml
spring:
...
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET}
scope:
- email
- profile
@RequiredArgsConstructor
@Service
public class OAuth2UserCustomService extends DefaultOAuth2UserService {
private final MemberRepository memberRepository;
@Transactional
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User user = super.loadUser(userRequest);
Map<String, Object> attributes = user.getAttributes();
OAuth2UserInfo userInfo = null;
String registrationId = userRequest.getClientRegistration().getRegistrationId();
SocialType provider = SocialType.valueOf(registrationId.toUpperCase());
if (provider.equals(SocialType.GOOGLE)){
userInfo = new GoogleUserInfo(attributes);
}
if (userInfo == null) {
throw new AuthException(AuthErrorCode.INVALID_SOCIAL_TYPE);
}
Member member = saveOrUpdate(userInfo);
return new DefaultOAuth2User(
Collections.emptySet(),
user.getAttributes(),
"sub"
);
}
private Member saveOrUpdate(OAuth2UserInfo userInfo){
String email = (String) userInfo.getEmail();
Member member = memberRepository.findByEmail(email)
.map(m -> {
if (!m.getSocialType().equals(userInfo.getProvider())) {
throw new MemberException(MemberErrorCode.DUPLICATE_SOCIAL);
}
m.updateFromOAuth(userInfo);
return m;
})
.orElse(
MemberConverter.toOAuthMember(email, userInfo)
);
return memberRepository.save(member);
}
}
Spring이 OAuth2 과정 전반을 다 다뤄주기에 받은 정보들을 저장하는 로직을 짜고, 그걸 config에 등록하면 된다.
DefaultOAuth2UserService를 상속받는 서비스 클래스를 하나 생성한 뒤 위와같이 작성한다.
기본 개념
OAuth 인증이 완료된 후, 리소스 서버에서 받은 OAuth2User(외부 사용자 정보)를 기반으로 우리 서비스의 도메인 사용자(Member)를 생성하거나 갱신한다.
public interface OAuth2UserInfo {
String getProviderId();
SocialType getProvider();
String getEmail();
String getName();
String getProfileImage();
}
만약 여러 소셜 로그인을 사용할 경우, OAuth2UserCustomService에서 다룰 attribute를 반환하는 객체용 인터페이스를 만들었다.
가령, 소셜마다 get("profile"), get("profile_photo) 이런 식으로 정보를 받아오는 방식이 다른데 이를 정의해둔다.
public class GoogleUserInfo implements OAuth2UserInfo{
private Map<String, Object> attributes;
public GoogleUserInfo(Map<String, Object> attributes){
this.attributes = attributes;
}
@Override
public String getProviderId() {
return String.valueOf(attributes.get("sub"));
}
@Override
public SocialType getProvider() {
return SocialType.GOOGLE;
}
@Override
public String getEmail() {
return String.valueOf(attributes.get("email"));
}
@Override
public String getName() {
return String.valueOf(attributes.get("name"));
}
@Override
public String getProfileImage() {
return String.valueOf(attributes.get("picture"));
}
}
@Component
@RequiredArgsConstructor
public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private final TokenService tokenService;
private final MemberRepository memberRepository;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
Member member = memberRepository.findByEmail((String) oAuth2User.getAttributes().get("email"))
.orElseThrow(() -> new MemberException(MemberErrorCode.NOT_FOUND));
TokenDto tokenDto = tokenService.issueTokens(member);
response.sendRedirect(
"http://localhost:3000/oauth/success?token=" + tokenDto.getAccessToken()
);
}
}
인증 성공 시 진행할 로직을 작성한다.
1. 토큰 발급
2. 프론트엔드 리다이렉트
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, OAuth2UserCustomService oAuth2UserCustomService, OAuth2SuccessHandler oAuth2SuccessHandler) throws Exception {
http
.authorizeHttpRequests(requests -> requests
.requestMatchers(allowUris).permitAll()
.anyRequest().authenticated()
)
.formLogin(AbstractHttpConfigurer::disable)
.addFilterBefore(jwtAuthFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf(AbstractHttpConfigurer::disable)
// 추가
.oauth2Login(oauth2 -> oauth2
.userInfoEndpoint(userInfo -> userInfo.userService(oAuth2UserCustomService))
.successHandler(oAuth2SuccessHandler));
return http.build();
}
필터 체인에 oauth2Login 관련 설정을 추가한다.


OAuth2SuccessHandler에 정의한 리다이렉트 링크로 이동되었다.