OAuth2 로그인시 다형성을 이용해 구글, 페이스북 로그인하기

Sol's·2023년 2월 1일
0

팀프로젝트

목록 보기
13/25

앞서 Oauth로그인을 어떻게 진행할지 알아보았습니다.

이제 PrincipalOauth2UserServiceloadUser를 작성해 보겠습니다.

구글 로그인과, 페이스북 로그인을 해보면서
다형성이 필요한 이유를 알아보겠습니다.

구글 로그인

@Service
@RequiredArgsConstructor
@Slf4j
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {

    private final BCryptPasswordEncoder encoder;
    private final UserRepository userRepository;
    private final JwtProvider jwtProvider;
    private final HttpSession session;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
       
        OAuth2User oAuth2User = super.loadUser(userRequest);

        String provider = userRequest.getClientRegistration().getRegistrationId(); // google
        String providerId = oAuth2User.getAttribute("sub");
        String username = oAuth2User.getAttribute("name");
        String userId = provider + "_" + providerId; //google_3894u2138413
        String password = encoder.encode("poco a poco");
//        String email = oAuth2User.getAttribute("email");
        UserRole role = UserRole.ROLE_USER;

        User user = userRepository.findOauthUser(userId);
        if (user == null) {
            user = User.builder()
                    .userId(userId)
                    .userName(username)
                    .password(password)
                    .role(role)
                    .provider(provider)
                    .providerId(providerId)
                    .build();
            userRepository.save(user);
        } else log.info("이미 Oauth 로그인을 한 적이있습니다.");

        String token = jwtProvider.generateAccessToken(user);
        session.setAttribute("Authorization", "Bearer " + token);
        session.setMaxInactiveInterval(60 * 25);
        return oAuth2User;
    }
}

우선 앞서 설명한 회원가입 시나리오처럼 전달받은 프로필 정보를 기반으로 강제 회원가입을 진행합니다.

그리고 만들어진 user정보로 JWT를 만들어 session에 넣어주면 회원가입과 동시에 로그인이 진행됩니다.

로그인 처리는 Security Filter에서 Session을 확인하여 진행됩니다.

다형성을 사용해보자

자 이제 Facebook 로그인을 할 차례입니다.

그런데 Facebook에서 전달하는 프로필 정보들은 변수명이 조금씩 다릅니다.

이럴때마다 if문으로 처리한다면 유지보수를 하거나, Naver, Kakao등 확장성을 고려할때 좋지않고, 가독성도 안좋아 지기때문에 다형성을 통해 처리를 해볼 예정입니다.

다형성을 이용한 템플릿 메서드 패턴을 해보겠습니다.

인터페이스 생성

public interface OAuth2UserInfo {
    String getProviderId();

    String getProvider();

    String getEmail();

    String getName();
}

Oauth 회원가입시 필요한 정보들을 리턴하는 메서드를 정의해 줍니다.

자식 클래스 생성

Google, Facebook에 맞는 자식클래스들을 생성합니다.

  • GoogleUserInfo
public class GoogleUserInfo implements OAuth2UserInfo{
    private Map<String, Object> attributes;

    public GoogleUserInfo(Map<String, Object> attributes){
        this.attributes = attributes;
    }

    @Override
    public String getProviderId() {
        return (String) attributes.get("sub");
    }

    @Override
    public String getProvider() {
        return "google";
    }

    @Override
    public String getEmail() {
        return (String) attributes.get("email");
    }

    @Override
    public String getName() {
        return (String) attributes.get("name");
    }
}
  • FacebookUserInfo
public class FacebookUserInfo implements OAuth2UserInfo{
    private Map<String, Object> attributes;

    public FacebookUserInfo(Map<String, Object> attributes){
        this.attributes = attributes;
    }

    @Override
    public String getProviderId() {
        return (String) attributes.get("id");
    }

    @Override
    public String getProvider() {
        return "facebook";
    }

    @Override
    public String getEmail() {
        return (String) attributes.get("email");
    }

    @Override
    public String getName() {
        return (String) attributes.get("name");
    }
}

이제 준비는 끝났습니다.
PrincipalOauth2UserServiceloadUser()를 리펙토링 해보겠습니다.

@Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);
  
        String domain = userRequest.getClientRegistration().getRegistrationId();
        OAuth2UserInfo oAuth2UserInfo = null;
        if(domain.equals("google")){
            System.out.println("구글 로그인 요청");
            oAuth2UserInfo = new GoogleUserInfo(oAuth2User.getAttributes());
        } else if (domain.equals("facebook")) {
            System.out.println("페이스북 로그인 요청");
            oAuth2UserInfo = new FacebookUserInfo(oAuth2User.getAttributes());
        }else {
            System.out.println("구글과 페이스북만 지원합니다.");
        }


        String provider = oAuth2UserInfo.getProvider(); // google
        String providerId = oAuth2UserInfo.getProviderId();
        String username = oAuth2UserInfo.getName();
        String userId = provider + "_" + providerId; //google_3894u2138413
        String password = encoder.encode("poco a poco");
        String email = oAuth2UserInfo.getEmail();

        User user = userRepository.findOauthUser(userId);
        if (user == null) {
            user = User.builder()
                    .userId(userId)
                    .userName(username)
                    .password(password)
                    .email(email)
                    .role(UserRole.ROLE_USER)
                    .provider(provider)
                    .providerId(providerId)
                    .build();
            userRepository.save(user);
        } else log.info("이미 Oauth 로그인을 한 적이있습니다.");

        String token = jwtProvider.generateAccessToken(user);
        session.setAttribute("Authorization", "Bearer " + token);
        session.setMaxInactiveInterval(60 * 25);
        return oAuth2User;
    }

위와같이 다형성을 이용해 Goolge, FaceBook을 한번에 받을 수 있게 되었습니다.

추후에 네이버로 로그인을 해도 NaverUserInfo클래스를 만들어 끼워 넣으면 되기 때문에 유지보수면에서도 가독성 면에서도 더 좋아졌습니다.

정리

Oauth로그인을 하면서 다형성에 대해 다시한번 더 고민할 수 있는 시간이였습니다.

또한 Oauth를 사용해 사용자 경험을 높이고 세계적으로 또는 국내에서 가장 많이 사용되는 기업들과 협업을 한다는 느낌도 받았습니다.

앞으로 Oauth를 더 잘 활용해서 사용자 경험을 높여 더 좋은 서비스를 만드는 방법을 고민해 보아야 겠습니다.

profile
배우고, 생각하고, 행동해라

0개의 댓글