카테고리 없음
[스프링 부트] 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 카카오 로그인 구현하기
네이버 로그인 API 등록
1) 네이버 개발자 센터 이동
https://developers.naver.com/main/
2) 상단 메뉴 Application > 애플리케이션 등록
3) 애플리케이션 이름 입력 > 네이버 로그인 API 추가 > 회원 이름, 이메일 필수 값 지정
4) 서비스 환경 PC웹 추가
5) 등록 완료
Spring Security와 네이버 로그인 API 연동
지난번 작성했던 카카오에 이어서 코드를 작성하겠습니다.
2022.09.14 - [Programming/Spring] - [스프링 부트] Spring Data JPA로 Oauth2 카카오 로그인 구현하기
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;
}
}
반응형