본문 바로가기
SPRING

[Spring] Google Login API 사용하기

by 킹명주 2022. 10. 10.

현재 프로젝트를 진행하고 있는데 회원 가입 부분에서 많은 시행착오를 겪었다. 그래서 이번에는 진짜 간단하게 google login api를 연동하는 방법에 대해 소개하고자 한다. (나의 시행착오를 바탕으로..)


흐름

 

많은 블로그에서 로그인 flow를 알려주기 보다는 oauth2 flow를 알려주곤 한다. 여기서 이해한다고 굉장히 많은 시간을 사용했다.. 그래서 정말 쉽게 어떻게 로그인이 되는지에 대해 그림으로 나타내보겠다.

 

이게 끝이다. 모든 과정을 거치게 되면 자신이 원하는 정보를 쏘옥~ 빼서 사용하면 된다. 가장 많이 사용하는 정보는 아마 이메일일 것이다. 왜냐하면 이메일을 통해 가입 또는 로그인 시킬 것이기 때문이다. 예를 들어 4번 과정을 통해 킹명주@gmail.com 이라는 계정을 얻어왔다고 하자. 그러면 두 가지 flow가 있을 것이다.

 

  • 이미 킹명주@gmail.com이 db에 저장되어 있는 경우   =>   이 경우 그냥 바로 로그인 시켜주면 끝!
  • 킹명주@gmail.com이 db에 없는 경우   =>   이 경우 계정정보를 바탕으로 회원가입을 진행해주어야 한다.

Google Cloud Platform

 

https://console.cloud.google.com/apis/dashboard?pli=1 

 

Google 클라우드 플랫폼

로그인 Google 클라우드 플랫폼으로 이동

accounts.google.com

 

우선 구글 로그인을 사용하기 위해서는 client 등록이 필수다. 위의 url로 들어가 로그인을 하게 되면

 

 

 

해당 홈화면이 보이게 될 것이다. 

그리고 왼쪽 상단의 버튼을 클릭하면 프로젝트 선택 및 생성하는 모달창이 출력될 것이다. 새 프로젝트를 클릭해보자

 

 

 

아무 이름으로 하나 만들기를 클릭해보자. 그러면 다시 홈화면으로 오게되고 

 

프로젝트 선택을 클릭해주자.

그리고 화면 중앙에 위치하는 대시보드를 클릭 -> API 개요로 이동을 클릭하자. 그렇다면 아래와 같은 화면이 출력된다.

 

여기서 동의 화면 구성을 클릭하자. 이제 쭉 사진을 참고해서 따라 진행하면 된다.

 

범위 추가 또는 삭제를 클릭하면 오른쪽에 모달창이 출력된다. 이는 사용자의 어떤 정보를 받아올지 결정하는 것이라고 생각하면 된다. 해당 과정이 끝나면 테스트 사용자는 추가하지 않고 쭉쭉 넘어가면 된다.

그리고 이제 client를 등록해보자.

애플리케이션 유형은 자신이 만드는 앱이 모바일이면 모바일, 웹이면 웹 등으로 설정하면 된다. 

이제 클라이언트 ID와 PW를 발급받았으므로 설정은 끄~읕이다. 이제 프로젝트에 적용해보자.


Spring Code

 

Spring에 적용하기도 굉장히 쉽다. 설명도 물론 하면서 진행할 것이지만, 시간이 없다면 복사 붙여넣기만 해도 돌아갈 것이다.

앞서 보았던 그림의 순서대로 API를 작성해보려고 한다.

 

1. URL API

구글 로그인 URL을 만들어 클라이언트에게 전송하려고 한다. 즉, 사용자가 구글 로그인 버튼을 클릭하면 내가 발급한 url로 이동하는 것이다.

application.properties

google.client.id=발급받은 Client ID
google.client.pw=발급받은 Client PW

LoginCotroller

@RestController
@CrossOrigin("*")
public class LoginController {
    @Value("${google.client.id}")
    private String googleClientId;
    @Value("${google.client.pw}")
    private String googleClientPw;

    @RequestMapping(value="/api/v1/oauth2/google", method = RequestMethod.POST)
    public String loginUrlGoogle(){
        String reqUrl = "https://accounts.google.com/o/oauth2/v2/auth?client_id=" + googleClientId
                + "&redirect_uri=http://localhost:8080/api/v1/oauth2/google&response_type=code&scope=email%20profile%20openid&access_type=offline";
        return reqUrl;
    }

}

CrossOrigin("*")을 사용한 이유는 로컬 환경에서 CORS 에러가 발생하여 선언해준 것이다. 그리고 localhost:8080/api/v1/oauth2/google로 post요청을 보내게 되면 구글 로그인 폼으로 연결해줄 url을 반환한다.

postman을 사용해보면 다음과 같이 url이 출력되는 것을 확인할 수 있다. 그리고 해당 url로 이동하면 다음과 같은 화면이 출력된다.

여기서 유심히 봐야할 것은 redirect_uri= ~ 부분이다. 해당 url의 get 요청으로 redirection 될 예정이다.

2. DTO 설정

크게 3가지의 dto를 설정해주어야 한다. 첫 번째로는 token을 받기 위한 Request를 만들어 주어야하고, 두 번째로는 token을 받아 올 Response dto가 필요하다. 마지막으로는 해당 token을 바탕으로 개인 정보를 가져오기 위한 Response도 필요할 것이다.

 

GoogleRequest

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class GoogleRequest {
    private String clientId;    // 애플리케이션의 클라이언트 ID
    private String redirectUri; // Google 로그인 후 redirect 위치
    private String clientSecret;    // 클라이언트 보안 비밀
    private String responseType;    // Google OAuth 2.0 엔드포인트가 인증 코드를 반환하는지 여부
    private String scope;   // OAuth 동의범위
    private String code;
    private String accessType;  // 사용자가 브라우저에 없을 때 애플리케이션이 액세스 토큰을 새로 고칠 수 있는지 여부
    private String grantType;
    private String state;
    private String includeGrantedScopes;    // 애플리케이션이 컨텍스트에서 추가 범위에 대한 액세스를 요청하기 위해 추가 권한 부여를 사용
    private String loginHint;   // 애플리케이션이 인증하려는 사용자를 알고 있는 경우 이 매개변수를 사용하여 Google 인증 서버에 힌트를 제공
    private String prompt;  // default: 처음으로 액세스를 요청할 때만 사용자에게 메시지가 표시
}

GoogleResponse

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class GoogleResponse {
    private String access_token; // 애플리케이션이 Google API 요청을 승인하기 위해 보내는 토큰
    private String expires_in;   // Access Token의 남은 수명
    private String refresh_token;    // 새 액세스 토큰을 얻는 데 사용할 수 있는 토큰
    private String scope;
    private String token_type;   // 반환된 토큰 유형(Bearer 고정)
    private String id_token;
}

GoogleInfResponse

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class GoogleInfResponse {
    private String iss;
    private String azp;
    private String aud;
    private String sub;
    private String email;
    private String email_verified;
    private String at_hash;
    private String name;
    private String picture;
    private String given_name;
    private String family_name;
    private String locale;
    private String iat;
    private String exp;
    private String alg;
    private String kid;
    private String typ;
}

 

3. Redirection 

로그인을 하게되면 redirection 주소로 정보를 받게 된다. 그러므로 아래와 같이 LoginController 코드에 추가 작성해주면 된다.

@RestController
@CrossOrigin("*")
public class LoginController {
    @Value("${google.client.id}")
    private String googleClientId;
    @Value("${google.client.pw}")
    private String googleClientPw;

    @RequestMapping(value="/api/v1/oauth2/google", method = RequestMethod.POST)
    public String loginUrlGoogle(){
        String reqUrl = "https://accounts.google.com/o/oauth2/v2/auth?client_id=" + googleClientId
                + "&redirect_uri=http://localhost:8080/api/v1/oauth2/google&response_type=code&scope=email%20profile%20openid&access_type=offline";
        return reqUrl;
    }
    @RequestMapping(value="/api/v1/oauth2/google", method = RequestMethod.GET)
    public String loginGoogle(@RequestParam(value = "code") String authCode){
        RestTemplate restTemplate = new RestTemplate();
        GoogleRequest googleOAuthRequestParam = GoogleRequest
                .builder()
                .clientId(googleClientId)
                .clientSecret(googleClientPw)
                .code(authCode)
                .redirectUri("http://localhost:8080/api/v1/oauth2/google")
                .grantType("authorization_code").build();
        ResponseEntity<GoogleResponse> resultEntity = restTemplate.postForEntity("https://oauth2.googleapis.com/token",
                googleOAuthRequestParam, GoogleResponse.class);
        String jwtToken=resultEntity.getBody().getId_token();
        Map<String, String> map=new HashMap<>();
        map.put("id_token",jwtToken);
        ResponseEntity<GoogleInfResponse> resultEntity2 = restTemplate.postForEntity("https://oauth2.googleapis.com/tokeninfo",
                map, GoogleInfResponse.class);
        String email=resultEntity2.getBody().getEmail();       
        return email;
    }
}

restTemplate를 통해 token 정보를 받아오고 해당 id_token이라는 정보를 한번 더 요청하는 형태이다.

이런식으로 이메일을 받아오게 되는 것이다. 물론 원하는 정보가 이메일이 아닐 수 있으므로 이는 충분히 커스텀이 가능하다.

만약, 해당 이메일 정보가 db에 저장되어 있다면 로그인을 진행시키고, db에 저장되어 있지 않다면 회원가입을 시키면 된다

 

 


2023.10.17
해당 게시물이 hot하다보니 여러 질문들을 받았고, 추가 내용을 적고자 한다. 

우선, 400 redirect_uri 문제가 발생하는 경우 해당하는 uri이 허용되어 있는지를 체크해야 한다.

 

그리고 최근에 다시 개발한 github 자료도 남기려고 한다. 다들 참고하셔서 구글 연동에 성공하셨으면 좋겠다!!
https://github.com/myeongju-kim/google_login

 

GitHub - myeongju-kim/google_login

Contribute to myeongju-kim/google_login development by creating an account on GitHub.

github.com

 

'SPRING' 카테고리의 다른 글

[Spring] @Setter vs @Builder  (2) 2023.02.20
[Spring] Exception Custom  (0) 2023.02.13
[Spring] JPA CASCADE  (2) 2022.08.31
[Spring] JWT 실습 - 1  (2) 2022.07.27
[JAVA] JPA 연관관계 매핑  (0) 2022.07.25