1. customOAuth2UserService.
@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService implements OAuth2UserService{
private final MemberRepository memberRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
// 엑세스 토큰으로 사용자 정보를 가지고 온다.
OAuth2UserService service = new DefaultOAuth2UserService();
OAuth2User oAuth2User = service.loadUser(userRequest);
//사용자의 attributes를 가져 온 후에 OAuth2User 를 통해 반환
String registeredId = userRequest.getClientRegistration().getRegistrationId();
// 엑세스 토큰으로 가져온 정보 1: registeredId - 현재 로그인 진행중인 서비스를 구현하는 코드(구글, 네이버, 카카오)
String userNameAttributeName = userRequest.getClientRegistration()
.getProviderDetails()
.getUserInfoEndpoint()
.getUserNameAttributeName();
// 엑세스 토큰으로 가져온 정보 2: userNameAttributeName - OAUTH2로그인 진행시 키가 되는 필드값, 기본키와 같은 의미로 생각하면 된다.
OAuthAttributes attributes = OAuthAttributes.of(
registeredId, userNameAttributeName, oAuth2User.getAttributes()
);
//OAuth2UserService를 통해 가져온 oAuth2User의 attribute를 담는 클래스
Member member = saveOrUpdate(attributes);
//user 값이 있다면 update 없다면 save를 해준다.
return new MemberOAuth2User(member, attributes);
//MemberOAuth2User클래스에 정보를 담아서 return 해준다.
}
private Member saveOrUpdate(OAuthAttributes attributes){
Member member = memberRepository.findByEmail(attributes.getEmail())
// memberRepository 를 통해 이메일이 있는지 확인한다
.map(entity -> entity.update(attributes.getName(), attributes.getEmail(), attributes.getNickname()))
// 있다면 이름, 이메일, 닉네임을 수정해준다.
.orElse(attributes.toMember());
// 없다면 새로 등록시켜준다.
return memberRepository.save(member);
// 수정, 등록한 member을 memberRepository 에 저장시켜준다.
}
}
2. MemberOAuth2User
@Getter
public class MemberOAuth2User extends DefaultOAuth2User {
// OAuth2User의 기본 구현으로 표준을 만들어서 제공한다
// 이러한 정보들은 REST API로 클라이언트 정보에 보내어 Controller에서 사용할 예정이다.
private final Member member;
public MemberOAuth2User(Member member, OAuthAttributes attributes) {
super(
List.of(new SimpleGrantedAuthority(member.getMemberType().name())),
attributes.getAttributes(),
attributes.getNameAttributeKey()
);
this.member = member;
}
@Override
public String getName() {
return member.getEmail();
}
}
3. OAuthAttributes
@Getter
public class OAuthAttributes {
// OAuth2User정보를 담아주기 위한 클래스이다.
// 직렬화 문제를 해결하기 위해 생성한 dto
private Map<String, Object> attributes;
private String nameAttributeKey;
private String name;
private String oAuthType;
private String nickname;
private String email;
// 내가 가져오고 싶은 정보들을 정의 해준다.
@Builder
public OAuthAttributes(Map<String, Object> attributes, String nameAttributeKey
,String name, String nickname ,String email, String oAuthType) {
this.attributes = attributes;
this.nameAttributeKey = nameAttributeKey;
this.name = name;
this.nickname = nickname;
this.email = email;
this.oAuthType = oAuthType;
}
//빌더 패턴 적용
public static OAuthAttributes of(String registeredId, String userNameAttributeName, Map<String, Object> attributes) {
if ("naver".equals(registeredId)) {
return ofNaver("id", attributes);
}
if ("kakao".equals(registeredId)) {
return ofKAKAO("id", attributes);
}
return ofGoogle(userNameAttributeName, attributes);
}
//registeredId를 가져와 네이버, 구글, 카카오 로그인 중 무엇인지 구별해준다.
private static OAuthAttributes ofGoogle(String usernameAttributeName, Map<String, Object> attributes ){
return OAuthAttributes.builder()
.name((String)attributes.get("name"))
.email((String)attributes.get("email"))
.nickname((String)attributes.get("name"))
.attributes(attributes)
.oAuthType("GOOGLE")
.nameAttributeKey(usernameAttributeName)
.build();
}
// registeredId를 통해 매핑하여서 들어온다. 빌더 패턴을 사용하여 db에 가져온 값들을 매핑시킨다.
private static OAuthAttributes ofNaver(String usernameAttributeName, Map<String, Object> attributes ){
Map<String, Object> response = (Map<String, Object>) attributes.get("response");
return OAuthAttributes.builder()
.name((String) response.get("name"))
.email((String) response.get("email"))
.nickname((String) response.get("nickname"))
.attributes(response)
.oAuthType("NAVER")
.nameAttributeKey(usernameAttributeName)
.build();
}
private static OAuthAttributes ofKAKAO(String usernameAttributeName, Map<String, Object> attributes ){
Map<String, Object> kakaoAccount = (Map<String, Object>)attributes.get("kakao_account");
Map<String, Object> kakaoProfile = (Map<String, Object>)kakaoAccount.get("profile");
return OAuthAttributes.builder()
.email((String) kakaoAccount.get("email"))
.name((String) kakaoProfile.get("nickname"))
.nickname((String) kakaoProfile.get("nickname"))
.attributes(attributes)
.oAuthType("KAKAO")
.nameAttributeKey(usernameAttributeName)
.build();
}
public Member toMember(){
return Member.builder()
.email(email)
.oAuthType(OAuthType.valueOf(oAuthType))
.nickname(nickname)
.memberType(MemberType.GENERAL)
.name(name)
.build();
}
// 새로운 member의 값을 db에 저장시킨다.
}