본문 바로가기

Springboot

[Spring Boot]OAuth2 (NAVER, KAKAO, GOOGLE LOGIN)

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에 저장시킨다.

}