카테고리 없음

[스프링 부트] Spring Data JPA로 Oauth2 네이버 로그인 구현하기

hyunipad 2022. 10. 1. 15:12
반응형
본 포스팅은 Spring Boot에서 Spring Data JPA를 사용하며 Oauth2 로그인 구현하는 방법을 설명합니다.

이번 포스팅에서는 카카오에 이어 네이버에서 제공하는 소셜 로그인 구현을 해보도록 하겠습니다.

카카오는 아래를 참고해주세요.

2022.09.14 - [Programming/Spring] - [스프링 부트] Spring Data JPA로 Oauth2 카카오 로그인 구현하기

 

[스프링 부트] Spring Data JPA로 Oauth2 카카오 로그인 구현하기

본 포스팅은 Spring Boot에서 Spring Data JPA를 사용하며 Oauth2 로그인 구현하는 방법을 설명합니다. 이번 포스팅에서는 카카오에서 제공하는 소셜 로그인 구현을 해보도록 하겠습니다. Oauth2 로그인은

hyunipad.tistory.com

 

 

네이버 로그인 API 등록

1) 네이버 개발자 센터 이동

https://developers.naver.com/main/

 

NAVER Developers

네이버 오픈 API들을 활용해 개발자들이 다양한 애플리케이션을 개발할 수 있도록 API 가이드와 SDK를 제공합니다. 제공중인 오픈 API에는 네이버 로그인, 검색, 단축URL, 캡차를 비롯 기계번역, 음

developers.naver.com

 

2) 상단 메뉴 Application > 애플리케이션 등록

3) 애플리케이션 이름 입력 > 네이버 로그인 API 추가 > 회원 이름, 이메일 필수 값 지정

 

4) 서비스 환경 PC웹 추가

 

5) 등록 완료

 

 

Spring Security와 네이버 로그인 API 연동

지난번 작성했던 카카오에 이어서 코드를 작성하겠습니다.

2022.09.14 - [Programming/Spring] - [스프링 부트] Spring Data JPA로 Oauth2 카카오 로그인 구현하기

 

[스프링 부트] Spring Data JPA로 Oauth2 카카오 로그인 구현하기

본 포스팅은 Spring Boot에서 Spring Data JPA를 사용하며 Oauth2 로그인 구현하는 방법을 설명합니다. 이번 포스팅에서는 카카오에서 제공하는 소셜 로그인 구현을 해보도록 하겠습니다. Oauth2 로그인은

hyunipad.tistory.com

1) application.yml

카카오와 마찬가지로 네이버도 스프링에서 지원해주지 않기 때문에 provider를 직접 입력해줘야 합니다.

  security:
    oauth2:
      client:
        registration:
          naver:
            client-id: 자신의 Client ID
            client-secret: 자신의 Client Secret
            redirect-uri: "http://localhost:8080/login/oauth2/code/{registrationId}"
            authorization-grant-type: authorization_code
            scope: name, email
            client-name: Naver
          kakao:
            client-id: 자신의 REST API 키
            redirect-uri: "http://localhost:8080/login/oauth2/code/{registrationId}" # http://localhost:8080/login/oauth2/code/kakao
            client-authentication-method: POST
            authorization-grant-type: authorization_code
            scope: profile_nickname, account_email
            client-name: Kakao
        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
          kakao:
            authorization-uri: https://kauth.kakao.com/oauth/authorize
            token-uri: https://kauth.kakao.com/oauth/token
            user-info-uri: https://kapi.kakao.com/v2/user/me
            user-name-attribute: id

 

2) SecurityConfig.java

package com.hyuni.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

import com.hyuni.demo.service.CustomOAuth2UserService;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig{
	
	private final CustomOAuth2UserService customOAuth2UserService;
	
	@Bean
	public SecurityFilterChain  filterChain(HttpSecurity http) throws Exception {
		  http.csrf().disable();
		  http.authorizeRequests()
		  	  .anyRequest().permitAll()
//			  .antMatchers("/**").authenticated() // 인가된 사용자만 접근 가능하도록 설정
//			  .antMatchers("게시물등").hasRole(Role.USER.name()) // 특정 ROLE을 가진 사용자만 접근 가능하도록 설정
			  .and()
			  .logout()
			  .logoutSuccessUrl("/")
			  .and()
			  .oauth2Login()
			  .userInfoEndpoint()
			  .userService(customOAuth2UserService);
		  
		  return http.build();
	}

}

 

3) CustomOAuth2UserService.java

package com.hyuni.demo.service;

import java.util.Collections;

import javax.servlet.http.HttpSession;
import javax.transaction.Transactional;

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 com.hyuni.demo.domain.OAuthAttributes;
import com.hyuni.demo.domain.SessionUser;
import com.hyuni.demo.domain.User;
import com.hyuni.demo.repository.UserRepository;

import lombok.RequiredArgsConstructor;

@Service
@Transactional
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService{

	private final UserRepository userRepository;
	private final HttpSession httpSession;
	
	@Override
	public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
		
		OAuth2UserService<OAuth2UserRequest, OAuth2User> service = new DefaultOAuth2UserService();
		OAuth2User oAuth2User = service.loadUser(userRequest); // Oath2 정보를 가져옴
		
		String registrationId = userRequest.getClientRegistration().getRegistrationId(); // 소셜 정보 가져옴
        String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName(); 
        
        OAuthAttributes attributes = OAuthAttributes.of(registrationId, userNameAttributeName, oAuth2User.getAttributes());

        User user = saveOrUpdate(attributes);
        httpSession.setAttribute("user", new SessionUser(user));

        return new DefaultOAuth2User(Collections.singleton(new SimpleGrantedAuthority(user.getRole().getKey())),
                attributes.getAttributes(),
        		attributes.getNameAttributeKey());
	}
	
    private User saveOrUpdate(OAuthAttributes attributes){
        User user =  userRepository.findOneByEmail(attributes.getEmail())
        		.map(entity -> entity.update(attributes.getName()))
        		.orElse(attributes.toEntity());

        return userRepository.save(user);
    }

}

 

4) OAtuhAttributes.java

기존에 있던 카카오에 네이버를 추가하였습니다.

package com.hyuni.demo.domain;

import java.util.Map;

import lombok.Builder;
import lombok.Data;
import lombok.Getter;

@Getter
public class OAuthAttributes {
	private Map<String, Object> attributes;
	private String nameAttributeKey;
    private String name;
    private String email;
    
    @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;
    }
    
    public static OAuthAttributes of(String socialName, String userNameAttributeName, Map<String, Object> attributes){        
        if("kakao".equals(socialName)){
            return ofKakao("id", attributes);
        }else if("naver".equals(socialName)) {
        	return ofNaver("id", attributes);
        }
        
        return null;
    }
    
    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()
                .name((String) kakaoProfile.get("nickname"))
                .email((String) kakaoAccount.get("email"))
                .nameAttributeKey(userNameAttributeName)
                .attributes(attributes)
                .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"))
                .attributes(response)
                .nameAttributeKey(userNameAttributeName)
                .build();
    }
    
    public User toEntity(){
        return User.builder()
                .name(name)
                .email(email)
                .role(Role.USER)
                .build();
    }
}

 

5) SessionUser.java스프링 시큐리티에서 세선 정보를 담을 도메인입니다.

package com.hyuni.demo.domain;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
public class SessionUser {

	private String name;
	private String email;
	private String profile_yn;
	
	public SessionUser(User user){
        this.name = user.getName();
        this.email = user.getEmail();
        this.profile_yn = user.getProfile_yn();
    }
}

 

6) UserRepository.java

package com.hyuni.demo.repository;

import java.util.Optional;

import javax.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import com.hyuni.demo.domain.User;

import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
public class UserRepository {

	private final EntityManager em;
	
	public Optional<User> findOneByEmail(String email){
		return em.createQuery("select u from User u where u.email = :email", User.class)
				.setParameter("email", email)
				.getResultList()
				.stream().findAny();
	}
	
	public User save(User user) {
		if(user.getId() == null) {
			user.setProfile_yn("N");
			em.persist(user);
		} else {
			em.merge(user);
		}
		return user;
	}

}
반응형