JAN's History
OAuth2 로그인 - 페이스북 개발자 센터로 페이스북 연동 로그인하기 3탄 본문
2탄 정리
페이스북과 로그인 연동을 진행했고, 회원정보를 정상적으로 받는 것까지 확인했다.
그리고 이제 회원가입과 로그인하는 로직을 만들어주면 완성이다!
1. 페이스북 회원정보 User에 담아 UserRepository에 담아주기
회원가입을 하기위해서는 페이스북으로부터 받은 회원정보는 User 오브젝트에 담아줘야한다.
save를 하기 위해선 UserRepository를 DI해줘야한다.
그리고 유저의 정보를 Map에 담아 필수데이터인 username, password, email, name를 다운캐스팅해 유저 오브젝트에 담아준다.
package com.cos.photogramstart.config.oauth;
import com.cos.photogramstart.config.auth.PrincipalDetails;
import com.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.domain.user.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.UUID;
@RequiredArgsConstructor
@Service
public class OAuth2DetailsService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
// System.out.println("oauth2 서비스 탐");
OAuth2User oAuth2User = super.loadUser(userRequest);
// System.out.println(oAuth2User.getAttributes());
Map <String, Object> userinfo = oAuth2User.getAttributes();
String username = "facebook_"+(String) userinfo.get("id");
String password = bCryptPasswordEncoder().encode(UUID.randomUUID().toString());
String name = (String) userinfo.get("name");
String email = (String) userinfo.get("email");
User userEntity = userRepository.findByUsername(username);
User user = User.builder()
.username(username)
.password(password)
.email(email)
.name(name)
.role("ROLE_USER")
.build();
User userEntity = userRepository.save(user);
return null;
}
password는 암호화가 되어야하기 때문에 BCryptPasswordEncoder를 사용해준다.
이제 유저 오브젝트에 페이스북에서 가져온 회원정보가 담기게 될 것이다.
여기서 문제가 있다면 실행할 때마다 계속 회원정보가 INSERT된다는 것이다.
➡️즉, 중복가입을 막기 위한 로직이 필요하다!!
2. 중복가입 막기
User userEntity = userRepository.findByUsername(username);
if(userEntity == null){ //최초로그인
User user = User.builder()
.username(username)
.password(password)
.email(email)
.name(name)
.role("ROLE_USER")
.build();
return nuserRepository.save(user);
}else{ //페이스북으로 이미 회원가입이 되어있다는 뜻
return userEntity;
}
이렇게 findByUsername를 통해 해당 유저 정보가 있는지 확인하고, 없으면 save로 진행하고, 있다면 기존 정보를 리턴하도록 한다.
여기서 return userEntity부분에 빨간줄이 뜨는데 loadUser의 메서드가 OAuth2User타입이기 때문이다.
따라서, 일반적인 formLogin 로직을 수행하면 PrincipalDetails타입이 리턴되는데, OAuth2Login로직을 수행하면 OAuth2User타입으로 나뉘어버려 문제가 생기는 것이다.
이걸 Controller에서 동적으로 매개변수를 받아줄 수 없어 에러가 난다.
따라서 PrincipalDetails에게 OAuth2User타입까지 상속해주는걸로 해결해보자 !
그러면 OAuth2로그인이던, 서버를 통한 form로그인이든 똑같이 PrincipalDetails타입이 된다.
3. PrincipalDetails에 OAuth2상속하기
package com.cos.photogramstart.config.auth;
import com.cos.photogramstart.domain.user.User;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
@Data
public class PrincipalDetails implements UserDetails, OAuth2User {
private static final long serialVersionUID = 1L;
private User user;
public PrincipalDetails(User user){
this.user = user;
}
public PrincipalDetails(User user, Map<String, Object> attributes){
this.user = user;
}
....
@Override
public Map<String, Object> getAttributes(){
return attributes;
}
@Override
public String getName() {
return (String) attributes.get("name");
}
}
UserDetails, OAuth2User를 모두 implements하도록 수정하고, 내부 메소드도 오버라이딩하도록 수정했다.
그리고 OAuth2User의 loadUser에 리턴값을 바꿔주면
User userEntity = userRepository.findByUsername(username);
if(userEntity == null){ //최초로그인
User user = User.builder()
.username(username)
.password(password)
.email(email)
.name(name)
.role("ROLE_USER")
.build();
return new PrincipalDetails(userRepository.save(user), oAuth2User.getAttributes());
}else{ //페이스북으로 이미 회원가입이 되어있다는 뜻
return new PrincipalDetails(userEntity, oAuth2User.getAttributes());
}
에러 발생
에러가 발생한다 !
에러의 내용은 SecurityConfig와 OAuth2DetailsService에서 둘다 BCryptPasswordEncoder를 로드하게 되는데,
SecurityConfig에서 메모리에 DI되기 전에 OAuth2DetailsService에서 메모리에 DI가 되어 문제가 발생한 것 같다.
이럴 경우에는 DI를 지우고 객체를 바로 만들어주면 해결된다!
마무리
package com.cos.photogramstart.config.oauth;
import com.cos.photogramstart.config.auth.PrincipalDetails;
import com.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.domain.user.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.UUID;
@RequiredArgsConstructor
@Service
public class OAuth2DetailsService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
// private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
// System.out.println("oauth2 서비스 탐");
OAuth2User oAuth2User = super.loadUser(userRequest);
// System.out.println(oAuth2User.getAttributes());
Map <String, Object> userinfo = oAuth2User.getAttributes();
String username = "facebook_"+(String) userinfo.get("id");
String password = new BCryptPasswordEncoder().encode(UUID.randomUUID().toString());
String name = (String) userinfo.get("name");
String email = (String) userinfo.get("email");
User userEntity = userRepository.findByUsername(username);
if(userEntity == null){ //최초로그인
User user = User.builder()
.username(username)
.password(password)
.email(email)
.name(name)
.role("ROLE_USER")
.build();
return new PrincipalDetails(userRepository.save(user), oAuth2User.getAttributes());
}else{ //페이스북으로 이미 회원가입이 되어있다는 뜻
return new PrincipalDetails(userEntity, oAuth2User.getAttributes());
}
}
}
이걸 저장하고 페이스북 로그인을 실행하면 정상적으로 로그인이 되는 것을 확인할 수 있다.
➡️DB에도 정상적으로 INSERT가 되었다!!
이렇게 OAuth2를 이용해 페이스북과 연동해 회원가입을 진행해봤는데요
다소 어렵고 복잡한 부분이 많았지만 처음이라 그럴거라 생각해요 .. !
다음에는 더 익숙해져서 에러에도 능숙하게 대처하는 내 자신이 되기를 🫠
'스프링' 카테고리의 다른 글
OAuth2 로그인 - 페이스북 개발자 센터로 페이스북 연동 로그인하기 2탄 (0) | 2024.06.03 |
---|---|
OAuth2 로그인 - 페이스북 개발자 센터로 페이스북 연동 로그인하기 (0) | 2024.06.01 |
Spring AOP를 이용한 전처리와 후처리 구현방법 2탄!!(+무한참조 에러해결) (0) | 2024.05.28 |
Spring AOP를 이용한 전처리와 후처리 구현방법! (0) | 2024.05.25 |
Ajax통신을 사용하는 2가지 이유 ex) 회원가입, 좋아요, 댓글.. (0) | 2024.05.22 |