ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring OAuth2.0(Google)
    Spring 2024. 8. 18. 17:49

    이전의 Spring Security OAuth를 이용해서 백엔드에서 처리하는 방식이 아닌, 프론트에서 Authorization Code를 전달받아

    직접 Access Token을 받아와 사용자 정보를 받아오는 방법입니다.

     

    https://kimtahwn.tistory.com/58 의 OAuth 흐름을 읽고 오시면 좋습니다.

     

    Spring OAuth2.0 Google 구현하기

    프론트에서 Authorization Server에서 Authroization Code를 받아 백엔드 서버로 전달하는 방식입니다.

     

    1. application.yml

    google:
      client_id: ${GOOGLE_CLIENT_ID}
      client_secret: ${GOOGLE_CLIENT_SECRET}
      grant_type: authorization_code
      redirect_uri: ${GOOGLE_REDIRECT_URI}

    민감한 내용이 될 수 있는 부분은 yml에 값을 넣어 사용했습니다.

     

    2. GoogleOAuthToken, GoogleProfile

    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    public class GoogleOAuthToken {
        private String access_token;
        private String expires_in;
        private String refresh_token;
        private String scope;
        private String token_type;
        private String id_token;
    }

     

    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @JsonIgnoreProperties(ignoreUnknown=true)
    public class GoogleProfile {
        private String id;
        private String email;
        private String verified_email;
        private String name;
        private String given_name;
        private String family_name;
        private String picture;
        private String locale;
    
        public Users toEntity() {
            return Users.builder()
                    .userName(name)
                    .email(email)
                    .author(Author.roleUser)
                    .build();
        }
    }

    Google에서 발급하는 AccessToken과 사용자의 정보를 JSON형태로 받아 변환하는 클래스

     

    3. LoginController

    @RestController
    @RequestMapping("/api/users")
    @RequiredArgsConstructor
    public class LoginController {
    
        private final LoginService loginService;
        
        @PostMapping("/login/google")
        public AccessTokenResponseDto googleLogin(@RequestBody OauthCodeRequestDto oauthCodeRequestDto) {
    
            return new AccessTokenResponseDto(loginService.googleLogin(oauthCodeRequestDto));
        }
    }

     

    4. LoginService

    @Slf4j
    @Service
    @RequiredArgsConstructor
    public class LoginService {
    
        @Value("${google.client_id}")
        private String clientId;
        @Value("${google.client_secret}")
        private String clientSecret;
        @Value("${google.grant_type}")
        private String grantType;
        @Value("${google.redirect_uri}")
        private String redirectUri;
        private final JwtUtil jwtUtil;
        private final UsersRepository usersRepository;
    
        @Transactional
        public String googleLogin(OauthCodeRequestDto oauthCodeRequestDto) {
            GoogleOAuthToken oAuthToken = getAccessToken(oauthCodeRequestDto.getCode());
            GoogleProfile googleProfile = getGoogleProfile(oAuthToken);
    
            Users user = usersRepository.findByEmail(googleProfile.getEmail());
            if(user == null) user = usersRepository.save(googleProfile.toEntity());
            JwtToken jwtToken = jwtUtil.generatedToken(user.getEmail(), user.getAuthor());
    
            return jwtToken.getAccessToken();
        }
    
        public GoogleOAuthToken getAccessToken(String code) {
            RestTemplate rt = new RestTemplate();
            GoogleOAuthToken oAuthToken = null;
            ObjectMapper objectMapper = new ObjectMapper();
            Map<String, String> params = new HashMap<>();
    
            params.put("grant_type", grantType);
            params.put("client_id", clientId);
            params.put("client_secret", clientSecret);
            params.put("code", code);
            params.put("redirect_uri", redirectUri);
            // google에서 token 받기
            ResponseEntity<String> response = rt.postForEntity("https://oauth2.googleapis.com/token", params, String.class);
            // token에서 값 추출
            try {
                oAuthToken = objectMapper.readValue(response.getBody(), GoogleOAuthToken.class);
            } catch (Exception e) {
                throw new CatchStudyException(ErrorCode.INVALID_OAUTH_TOKEN_ERROR);
            }
    
            return oAuthToken;
        }
    
        public GoogleProfile getGoogleProfile(GoogleOAuthToken oAuthToken) {
            RestTemplate rt = new RestTemplate();
            ObjectMapper objectMapper = new ObjectMapper();
            GoogleProfile googleProfile = null;
    
            HttpHeaders profileHeaders = new HttpHeaders();
            // 헤더에 토큰값 설정
            profileHeaders.add("Authorization", "Bearer "+ oAuthToken.getAccess_token());
            HttpEntity<MultiValueMap<String,String>>googleProfileRequest = new HttpEntity<>(profileHeaders);
            // token을 사용하여 사용자 정보 받기
            ResponseEntity<String>googleProfileResponse = rt.exchange(
                    "https://www.googleapis.com/oauth2/v2/userinfo",
                    HttpMethod.GET,
                    googleProfileRequest,
                    String.class
            );
    
            try {
                googleProfile = objectMapper.readValue(googleProfileResponse.getBody(), GoogleProfile.class);
            } catch (Exception e) {
                throw new CatchStudyException(ErrorCode.INVALID_OAUTH_TOKEN_ERROR);
            }
    
            return googleProfile;
        }
    }

     

    RestTemplate를 사용해 google에서 제공하는 OAuth Access Token과 사용자의 정보를 받기 위한 정해진 주소로 요청을 보내 JSON의 형태로 정보를 받으면 objectMapper를 사용해 객체로 변환하여 정보를 추출해 JWT AccessToken을 발급해 프론트 서버에 반환합니다.

     

    이는 전에 SpringSecurity를 이용해 OAuth를 구현하게 되면 쿼리 파라미터나 쿠키의 형태로 담아서 반환해야하는 것과 달리 ResponseBody에 담아 반환할 수 있습니다.

Designed by Tistory.