Java/Spring

[SpringBoot] Spring Security 네이버 로그인 연동

suhaha 2023. 9. 13. 10:07

SpringBoot와 AWS로 혼자 구현하는 웹서비스

SpringBoot 환경에서 네이버 로그인 연동하기 

 

1. 네이버 개발자 페이지를 통해서 애플리케이션을 등록합니다 

 

 

 

 

 

애플리케이션 이름을 등록하고 필요한 정보를 선택합니다. 

 

 

로그인 API를 사용할 환경을 선택하고 여기서는 웹 서비스이므로 PC 웹, 로컬에서 돌리기 때문에 localhost로 설정했습니다. 

 

 

 

2. SpringBoot 프로퍼티 설정

spring:
  security:
    oauth2:
      client:
        registration:
          naver:
            client-id: client 아이디
            client-secret: client secret
            redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}'
            authorization_grant_type: authorization_code
            scope: name, email, profile_image
            client-name: Naver
        provider:
            naver:
              authorization_uri: https://nid.naver.com/oauth2.0/authorize
              token_uri: https://nid.naver.com/oauth2.0/token
              user-info-uri: https://openapi.naver.com/v1/nid/me
              user_name_attribute: response

 

 

3. SpringBoot OAuth2UserService 설정하기 

package com.example.springbootAWS.config.auth;

import com.example.springbootAWS.config.auth.dto.OAuthAttributes;
import com.example.springbootAWS.config.auth.dto.SessionUser;
import com.example.springbootAWS.domain.user.Member;
import com.example.springbootAWS.domain.user.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import java.util.Collections;

@RequiredArgsConstructor
@Service
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    private final UserRepository userRepository;
    private final HttpSession httpSession;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();

        OAuth2User oAuth2User = delegate.loadUser(userRequest);

        String registrationId = userRequest.getClientRegistration().getRegistrationId();    // 로그인 서비스 구분
        String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails()
                .getUserInfoEndpoint().getUserNameAttributeName();

        OAuthAttributes attributes = OAuthAttributes.of(registrationId, userNameAttributeName, oAuth2User.getAttributes());

        Member member = saveOrUpdate(attributes);

        httpSession.setAttribute("user", new SessionUser(member));    // 세션에 저장

        return new DefaultOAuth2User(Collections.singleton(new SimpleGrantedAuthority(member.getRoleKey())), attributes.getAttributes(), attributes.getNameAttributeKey());
    }

    private Member saveOrUpdate(OAuthAttributes attributes) {
        Member member = userRepository.findByEmail(attributes.getEmail())
                .map(entity -> entity.update(attributes.getName(), attributes.getPicture()))
                .orElse(attributes.toEntity());

        return userRepository.save(member);
    }
}

OAuth2User 객체에 로그인을 통해 받은 유저의 정보들이 들어있습니다. 

해당 정보를 로그인 종류에 따라 구분할 수 있습니다. (여기서는 구글 로그인이 함께 사용되어있어서 구분 필요)

 

 

 

Login 정보를 받을 객체

package com.example.springbootAWS.config.auth.dto;

import com.example.springbootAWS.domain.user.Member;
import com.example.springbootAWS.domain.user.Role;
import lombok.Builder;
import lombok.Getter;

import java.util.Map;

@Getter
public class OAuthAttributes {

    private Map<String, Object> attributes;
    private String nameAttributeKey;
    private String name;
    private String email;
    private String picture;

    @Builder
    public OAuthAttributes(Map<String, Object> attributes, String nameAttributeKey, String name, String email, String picture) {
        this.attributes = attributes;
        this.nameAttributeKey = nameAttributeKey;
        this.name = name;
        this.email = email;
        this.picture = picture;
    }

    // google attribute 변환 과정
    public static OAuthAttributes of(String registrationId, String userNameAttributeName, Map<String, Object> attributes) {
        if ("naver".equals(registrationId)) {
            return ofNaver("id", attributes);
        }
        return ofGoogle(userNameAttributeName, attributes);
    }

    private static OAuthAttributes ofGoogle(String userNameAttributeName, Map<String, Object> attributes) {
        return OAuthAttributes.builder()
                .name((String) attributes.get("name"))
                .email((String) attributes.get("email"))
                .picture((String) attributes.get("picture"))
                .attributes(attributes)
                .nameAttributeKey(userNameAttributeName)
                .build();
    }

    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"))
                .picture((String) response.get("profile_image"))
                .attributes(response)
                .nameAttributeKey(userNameAttributeName)
                .build();
    }

    public Member toEntity() {
        return Member.builder()
                .name(name)
                .email(email)
                .picture(picture)
                .role(Role.GUEST)
                .build();
    }
}