ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 구글 로그인
    스프링 2023. 12. 31. 17:56

     

     

    Controller 랑 Dto 생성


    Service

     

    @Slf4j
    @Service
    @RequiredArgsConstructor
    public class GoogleService {
        private final PasswordEncoder passwordEncoder;
        private final UserRepository userRepository;
        private final JwtUtil jwtUtil;

        @Value("${spring.security.oauth2.client.registration.google.client-id}")
        private String googleClientId;

        @Value("${spring.security.oauth2.client.registration.google.client-secret}")
        private String googleClientSecret;

    // https://accounts.google.com/o/oauth2/auth?client_id=694048623782-hd9kdhx349dqu8ja6aol2ie9ng6ls9o3f.apps.googleusercontent.com&redirect_uri=https://ppol.pro/api/users/google&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email&access_type=offline&prompt=consent

        public ResponseDto<String> googleLogin(String code, String scope, HttpServletResponse response) throws JsonProcessingException {
            // 1. "인가 코드"로 "액세스 토큰" 요청
            String accessToken = getToken(code, scope);

            // 2. 토큰으로 구글 API 호출 : "액세스 토큰"으로 "구글 사용자 정보" 가져오기
            GoogleUserInfoDto googleUserInfoDto = getGoogleUserInfo(accessToken);

            // 3. 필요시에 회원가입
            User googleUser = registerGoogleUserIfNeeded(googleUserInfoDto);

            // 4. JWT 토큰 반환
            jwtUtil.createAndSetToken(response, googleUser.getEmail(), googleUser.getId());

            return ResponseDto.setSuccess(HttpStatus.OK, "구글 로그인 성공!");
        }

        // 1. "인가 코드"로 "액세스 토큰 & 리프레시 토큰" 요청
        private String getToken(String code, String scope) throws JsonProcessingException {
            // HTTP Header 생성
            HttpHeaders headers = new HttpHeaders();
            headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
            // HTTP Body 생성
            MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
            body.add("grant_type", "authorization_code");
            body.add("client_id", googleClientId);
            body.add("client_secret", googleClientSecret);
            body.add("redirect_uri", googleRedirectionUri);
            body.add("code", code);
            body.add("scope", scope);
            // HTTP 요청 보내기
            HttpEntity<MultiValueMap<String, String>> tokenRequest =
                    new HttpEntity<>(body, headers);
            RestTemplate rt = new RestTemplate();
            ResponseEntity<String> response = rt.exchange(
                    "https://oauth2.googleapis.com/token",
                    HttpMethod.POST,
                    tokenRequest,
                    String.class
            );

            // HTTP 응답 (JSON) -> 액세스 토큰
            String responseBody = response.getBody();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonNode = objectMapper.readTree(responseBody);
            return jsonNode.get("access_token").asText();

        }

        // 2. 토큰으로 구글 API 호출 : "액세스 토큰"으로 "구글 사용자 정보" 가져오기
        private GoogleUserInfoDto getGoogleUserInfo(String accessToken) throws JsonProcessingException {
            HttpHeaders headers = new HttpHeaders();
            headers.add("Authorization", "Bearer " + accessToken);
            headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

            HttpEntity<MultiValueMap<String, String>> googleUserInfoRequest = new HttpEntity<>(headers);
            RestTemplate rt = new RestTemplate();
            ResponseEntity<String> response = rt.exchange(
                    "https://www.googleapis.com/oauth2/v3/userinfo",
                    HttpMethod.POST,
                    googleUserInfoRequest,
                    String.class
            );

            String responseBody = response.getBody();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonNode = objectMapper.readTree(responseBody);
            Long id = jsonNode.get("id").asLong();
            String email = jsonNode.get("email").asText();
            String nickname = jsonNode.get("name") != null ? jsonNode.get("name").asText() : "google";
            String profileImage = jsonNode.get("properties")
                    .get("profile_image").asText();

            log.info("구글 사용자 정보: " + id + ", " + nickname + ", " + email + ", " + profileImage);
            return new GoogleUserInfoDto(id, email, nickname, profileImage);
        }

        // 3. 필요시에 회원가입
        private User registerGoogleUserIfNeeded(GoogleUserInfoDto googleUserInfo) {
            Long googleId = googleUserInfo.getId();
            String profileImage = googleUserInfo.getProfileImage();
            User googleUser = userRepository.findByGoogleId(googleId)
                    .orElse(null);
            if (googleUser == null) {
                // 구글 사용자 email 동일한 email 가진 회원이 있는지 확인
                String googleEmail = googleUserInfo.getEmail();
                User sameEmailUser = userRepository.findByEmail(googleEmail).orElse(null);
                if (sameEmailUser != null) {
                    googleUser = sameEmailUser;
                    // 기존 회원정보에 구글 Id, 프로필 이미지 추가
                    googleUser = googleUser.googleUpdate(googleId, profileImage);
                } else {
                    // 신규 회원가입
                    // password: random UUID
                    String password = UUID.randomUUID().toString();
                    String encodedPassword = passwordEncoder.encode(password);

                    // email: google email
                    String email = googleUserInfo.getEmail();

                    googleUser = new User(null, null, googleId, email, googleUserInfo.getNickname(), encodedPassword, profileImage);
                }
                userRepository.save(googleUser);
            }
            return googleUser;
        }
    }

    '스프링' 카테고리의 다른 글

    결제 API  (0) 2024.03.18
    CORS  (0) 2024.03.18
    네이버 로그인  (0) 2023.12.31
    카카오 로그인  (0) 2023.12.31
    DB접속  (0) 2023.12.31
Designed by Tistory.