

OAuth2 클라이언트 의존성에 의해 필터나 Provider는 기본적으로 제공을 해준다. 하지만 리소스 서버에서 제공해주는 유저 정보는 직접 구현해야 한다.
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
public CustomOAuth2UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
System.out.println(oAuth2User);
String registrationId = userRequest.getClientRegistration().getRegistrationId();
OAuth2Response oAuth2Response = null;
if (registrationId.equals("naver")) {
oAuth2Response = new NaverResponse(oAuth2User.getAttributes());
}
else if (registrationId.equals("google")) {
oAuth2Response = new GoogleResponse(oAuth2User.getAttributes());
}
else {
return null;
}
String username = oAuth2Response.getProvider()+" "+oAuth2Response.getProviderId();
UserEntity existData = userRepository.findByUsername(username);
if (existData == null) {
UserEntity userEntity = new UserEntity();
userEntity.setUsername(username);
userEntity.setEmail(oAuth2Response.getEmail());
userEntity.setName(oAuth2Response.getName());
userEntity.setRole("ROLE_USER");
userRepository.save(userEntity);
UserDTO userDTO = new UserDTO();
userDTO.setUsername(username);
userDTO.setName(oAuth2Response.getName());
userDTO.setRole("ROLE_USER");
return new CustomOAuth2User(userDTO);
}
else {
existData.setEmail(oAuth2Response.getEmail());
existData.setName(oAuth2Response.getName());
userRepository.save(existData);
UserDTO userDTO = new UserDTO();
userDTO.setUsername(existData.getUsername());
userDTO.setName(oAuth2Response.getName());
userDTO.setRole(existData.getRole());
return new CustomOAuth2User(userDTO);
}
}
}
OAuth2Response
{
resultcode=00, message=success, response={id=123123123, name=원승현}
}
{
resultcode=00, message=success, id=123123123, name=원승현
}
OAuth2Response 인터페이스(DTO)
public interface OAuth2Response {
//제공자 (Ex. naver, google, ...)
String getProvider();
//제공자에서 발급해주는 아이디(번호)
String getProviderId();
//이메일
String getEmail();
//사용자 실명 (설정한 이름)
String getName();
}
NaverResponse(DTO)
public class NaverResponse implements OAuth2Response{
private final Map<String, Object> attribute;
public NaverResponse(Map<String, Object> attribute) {
this.attribute = (Map<String, Object>) attribute.get("response");
}
@Override
public String getProvider() {
return "naver";
}
@Override
public String getProviderId() {
return attribute.get("id").toString();
}
@Override
public String getEmail() {
return attribute.get("email").toString();
}
@Override
public String getName() {
return attribute.get("name").toString();
}
}
GoogleResponse(DTO)
public class GoogleResponse implements OAuth2Response{
private final Map<String, Object> attribute;
public GoogleResponse(Map<String, Object> attribute) {
this.attribute = attribute;
}
@Override
public String getProvider() {
return "google";
}
@Override
public String getProviderId() {
return attribute.get("sub").toString();
}
@Override
public String getEmail() {
return attribute.get("email").toString();
}
@Override
public String getName() {
return attribute.get("name").toString();
}
}
OAuth2UserService 등록(SecurityConfig)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final CustomOAuth2UserService customOAuth2UserService;
public SecurityConfig(CustomOAuth2UserService customOAuth2UserService) {
this.customOAuth2UserService = customOAuth2UserService;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//csrf disable
http
.csrf((auth) -> auth.disable());
//From 로그인 방식 disable
http
.formLogin((auth) -> auth.disable());
//HTTP Basic 인증 방식 disable
http
.httpBasic((auth) -> auth.disable());
//oauth2
http
.oauth2Login((oauth2) -> oauth2
.userInfoEndpoint((userInfoEndpointConfig) -> userInfoEndpointConfig
.userService(customOAuth2UserService)));
//경로별 인가 작업
http
.authorizeHttpRequests((auth) -> auth
.requestMatchers("/").permitAll()
.anyRequest().authenticated());
//세션 설정 : STATELESS
http
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
}
UserEntity
@Entity
@Getter
@Setter
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String name;
private String email;
private String role;
}
UserDTO
@Getter
@Setter
public class UserDTO {
private String role;
private String name;
private String username;
}
CustomOAuth2User
public class CustomOAuth2User implements OAuth2User {
private final UserDTO userDTO;
public CustomOAuth2User(UserDTO userDTO) {
this.userDTO = userDTO;
}
@Override
public Map<String, Object> getAttributes() {
return null;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collection = new ArrayList<>();
collection.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return userDTO.getRole();
}
});
return collection;
}
@Override
public String getName() {
return userDTO.getName();
}
public String getUsername() {
return userDTO.getUsername();
}
}
스프링 시큐리티 기본 방식에서(OAuth2가 아닌) UsernamePasswordAuthenticationFilter를 커스텀한 필터에서 authenticationManager를 통해, 전달 받은 로그인 정보를 UserDetailService에서 가져온 UserDetails와 비교해서 정보가 일치하면 로그인 성공을 하듯이 CustomOAuth2UserService는 CustomOAuth2User(DB에 저장된 정보)를 전달해준다.
참고자료: 개발자 유미 스프링 OAuth2 클라이언트 JWT