- OAuthAttributes.class
OAuth2를 통해 로그인한 사용자를 Member 객체로 저장하기 위한 클래스
package project.board_service.oauth;
import lombok.Builder;
import lombok.Getter;
import project.board_service.entity.Member;
import project.board_service.entity.MemberRole;
import java.util.Map;
@Builder
@Getter
public class OAuthAttributes {
private Map<String, Object> attributes;
private String nameAttributeKey;
private String username;
private String email;
private String role;
public static OAuthAttributes of(String registrationId,
String usernameAttributeName,
Map<String, Object> attributes) {
//1. Google
if (registrationId.equals("google")) {
return ofGoogle(usernameAttributeName, attributes);
}
//2. Kakao
if (registrationId.equals("kakao")) {
return ofKakao(usernameAttributeName, attributes);
}
//3. Naver
if (registrationId.equals("naver")) {
return ofNaver(usernameAttributeName, attributes);
}
return null;
}
private static OAuthAttributes ofGoogle(String usernameAttributeName,
Map<String, Object> attributes) {
return OAuthAttributes.builder()
.username((String) attributes.get("email"))
.email((String) attributes.get("email"))
.attributes(attributes)
.nameAttributeKey(usernameAttributeName)
.build();
}
private static OAuthAttributes ofKakao(String usernameAttributeName,
Map<String, Object> attributes) {
Map<String, Object> response = (Map<String, Object>) attributes.get("kakao_account");
return OAuthAttributes.builder()
.username((String) response.get("email"))
.email((String) response.get("email"))
.attributes(response)
.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()
.username((String) response.get("email"))
.email((String) response.get("email"))
.attributes(response)
.nameAttributeKey(usernameAttributeName)
.build();
}
public Member toEntity() {
return Member.builder()
.username(email)
.email(email)
.role(MemberRole.SOCIAL)
.build();
}
}
- CustomOAuth2UserService.class
package project.board_service.oauth;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
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 project.board_service.entity.Member;
import project.board_service.repository.MemberRepository;
@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
private final MemberRepository memberRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
//OAuth2 서비스 id 구분 코드 ("google", "kakao", "naver")
String registrationId = userRequest.getClientRegistration().getRegistrationId();
//OAuth2 로그인 진행시 키가 되는 필드 값 (PK)
String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails()
.getUserInfoEndpoint().getUserNameAttributeName();
//OAuth2UserService - 인증자 구분 메서드
OAuthAttributes attributes = OAuthAttributes.of(registrationId, userNameAttributeName, oAuth2User.getAttributes());
Member member = saveOrUpdate(attributes);
// OAuth2User 객체로부터 UserDetails 객체 생성
UserDetails userDetails = User.builder()
.username(member.getUsername()) // 사용자 이름 또는 ID
.password("") // 비밀번호 필드
.roles(String.valueOf(member.getRole())) // 사용자 역할, "ROLE_" 접두사 없이 설정
.build();
// Spring Security가 인증 수행
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
return new DefaultOAuth2User(
userDetails.getAuthorities(),
attributes.getAttributes(),
"email");
}
/*소셜 로그인시,
* 기존 회원 - 수정일자(updateAt)만 업데이트
* 새로운 회원 - 회원(member) 저장*/
private Member saveOrUpdate(OAuthAttributes attributes) {
Member member = memberRepository.findByEmail(attributes.getEmail())
.map(Member::updateUpdateAt)
.orElse(attributes.toEntity());
return memberRepository.save(member);
}
}
- WebSecurityConfig.class
@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class WebSecurityConfig {
/*OAuth*/
private final CustomOAuth2UserService customOAuth2UserService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
/*다른 요소 중략...*/
.oauth2Login(oauth2 -> oauth2
.userInfoEndpoint(userInfo -> userInfo
.userService(customOAuth2UserService))
);
return http.build();
}
}