본문 바로가기

서버/SprintBoot
[스프링부트(Spring Boot)] 소셜 로그인(구글 로그인)

소셜 로그인 마지막인 구글 로그인.

https://cloud.google.com/identity-platform/docs/use-rest-api?hl=ko 

 

REST API 사용  |  Identity Platform 문서  |  Google Cloud

의견 보내기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. REST API 사용 이 문서에서는 Identity Platform REST API를 사용하여 사용자 로그인 및 토큰 작업 등의 일

cloud.google.com


먼저, 프로젝트를 생성한다.


그리고 OAuth 동의 화면을 찾아 이동!

User Type은 외부로 선택.

네이버에서는 검수 요청을 안 하면 내 계정만 됐는데, 그 부분을 해결할 수 있을 것 같다.

그런데 OAuth 동의 화면(첫 번쨰 화면) 에서 계속 뭔가 오류가 있어서 넘어가지지가 않았다..

찾아봤는데 딱히 해결 방법이 안 나오는...

그래서 그냥 다른 계정으로 다시 프로젝트 만들었더니 됐다.

대체 왜지?.... 이유는 아직도 모른다..^^

다음으로 사용자에게 정보를 받을 범위를 추가한다.

나는 이메일만 받아오면 돼서 2개만 체크했다.

세 번째에서는 로그인 가능한 사용자 이메일 추가.

정말 실제로 사용하는 것처럼 누구나 로그인하게 하려면 네이버처럼 따로 요청해야 한다.

그래도 100개까지는 추가 가능하니까 공부 단계에서는 만족!

앱 게시까지 하면 동의 부분은 끝. 


이제 익숙한 과정.

사용자 ID를 발급받아야 한다.

클라이언트 ID와 클라이언트 보안 비밀번호를 발급받았으면 기본 설정은 끝!

여기까지 오는 게.. 굉장히 험난했던 것 같은.....


가장 먼저 할 것은 역시나 버튼 이미지 다운 받기

구글은 로고 사용이 카카오나 네이버보다 더 까다로운 것 같다.

변형하려면 꼭 가이드 참고하기.

https://developers.google.com/identity/branding-guidelines?hl=ko 

 

로그인 브랜드 가이드라인  |  Google ID 플랫폼  |  Google for Developers

로그인 브랜드 가이드라인 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 모바일 또는 웹 앱에서 기본적인 profile 또는 email 범위로 Google 로그인을 사용하고

developers.google.com


1. 인가 코드 받아오기

컴포넌트 만들고 redirec할 주소를 연결한다.

{
    path: '/google-login',
    name: 'google',
    component: () => import('@/components/GoogleLogin.vue')
  },

 

googleLogin() {
      const url = 'https://accounts.google.com/o/oauth2/v2/auth';
      location.href = `${url}?client_id=${this.googleLoginParams.client_id}&redirect_uri=${this.googleLoginParams.redirect_uri}&response_type=${this.googleLoginParams.response_type}`;
    },


넘겨줘야 할 param들은 data에 정의해두고 가져와서 연결했다.

googleLoginParams: {
        client_id: process.env.VUE_APP_GOOGLE_CLIENT_ID,
        redirect_uri: process.env.VUE_APP_GOOGLE_REDIRECT_URI,
        response_type: 'code',
        scope: 'email',
      }
  1. client_id: 구글에서 발급받은 client ID
  2. redirect_uri: 구글에서 설정한 Redirect URI
  3. response_type: 'code'로 고정
  4. scope: 받아올 내용 

카카오에서는 scope를 넘겨주지 않아도 알아서 동의한 값만 받아올 수 있었는데, 구글은 꼭 넘겨주어야 한다.


2. 토큰 받아오기

조금 전에 받아온 인가 코드를 활용해서 post 요청을 보낸다.

인가코드를 post요청으로 서버에 넘기고, 서버에서 다시 post으로 토큰 받아온 후 클라이언트에 반환.

이번에도 data에 정의해두고 매개변수를 넘겼다.(URL에 붙여서)

tokenParams: {
        client_id: process.env.VUE_APP_GOOGLE_CLIENT_ID,
        client_secret: process.env.VUE_APP_GOOGLE_CLIENT_SECRET_PW,
        code: this.$route.query.code,
        grant_type : 'authorization_code',
        redirect_uri: 'http://localhost:8080/google-login',
      },
      
      	...	
      
 await axios.post(this.$store.state.url+'googlelogin', new URLSearchParams(this.tokenParams).toString())
        .then(response => {
          console.log(response);
          // this.token.access_token = response.data.result.access_token;
          // this.token.refresh_token = response.data.result.refresh_token;
        })
        .catch(error => {
          console.log(error);
        })
  1. client_id: 구글에서 발급받은 client id
  2. client_secret: 구글에서 발급받은 client secret pw
  3. code: 인가 코드
  4. grant_type: 'authorization_code'로 고정
  5. redirect_uri: 구글에서 설정한 Redirect URI

서버: 컨트롤러 > 서비스로 넘기고, 서비스에서 post 요청

try {
            URI uri = new URI("https://oauth2.googleapis.com/token?"+params);
            Object response = restTemplate.postForObject(uri, null, Object.class);

            if (response != null) {
                rModel.setState(true);
                rModel.setMessage("구글 로그인에 성공했습니다.");
                rModel.setResult(response);
            } else {
                rModel.setState(false);
                rModel.setMessage("구글 로그인 응답을 받지 못했습니다.");
            }
        } catch (URISyntaxException e) {
            rModel.setState(false);
            rModel.setMessage("잘못된 URI 형식입니다.");
        } catch (HttpClientErrorException | HttpServerErrorException e) {
            rModel.setState(false);
            rModel.setMessage("구글 로그인 중 서버 응답 오류: " + e.getStatusCode() + " " + e.getStatusText());
        } catch (Exception e) {
            rModel.setState(false);
            rModel.setMessage("구글 로그인 중 오류가 발생했습니다: " + e.getMessage());
        }

3. 로그인한 사용자 정보 받아오기

이번에도 역시 서버 측에서 get 요청을 해야 한다.

헤더 설정하여 서버로 보내고, 서버에서 받아서 처리한다.

헤더 설정 안 하고 보내서 엄청 헤맸다.

네이버에서 헤더 설정한 거랑 똑같이 해주면 되는데..😥

const userConfig = {
      headers: {
        'Authorization' : `Bearer ${this.token.access_token}`,
      }
    }

    await axios.get(this.$store.state.url+'googlelogin', userConfig)
        .then(response => {
          console.log(response);
          this.$store.commit('setSnsUserId', response.data.result.id)
          this.$store.commit('setSnsUserEmail', response.data.result.email)

          this.userinfo.id = response.data.result.id;

          axios.post(this.$store.state.url + 'dup', this.userinfo)
              .then(response => {
                if(response.data.result.length == 0) {
                  this.$router.push({name: 'sns'})
                } else {
                  sessionStorage.setItem('id', this.userinfo.id);
                  this.getUserinfo();
                }
              })
              .catch(error => {
                console.log(error);
              })

        })
        .catch(error => {
          console.log(error);
          Swal.fire({
            title: '로그인에 실패했습니다.',
            icon: 'error'
          });
        })

 

try {
            URI uri = new URI("https://www.googleapis.com/userinfo/v2/me");

            // 헤더 설정
            HttpHeaders headers = new HttpHeaders();
            headers.set("Authorization", header);
            HttpEntity<String> entity = new HttpEntity<>("parameters", headers);

            ResponseEntity<Object> response = restTemplate.exchange(uri, HttpMethod.GET, entity, Object.class);

            if (response.getBody() != null) {
                rModel.setState(true);
                rModel.setMessage(" 구글 로그인에 성공했습니다.");
                rModel.setResult(response.getBody());
            } else {
                rModel.setState(false);
                rModel.setMessage("구글 로그인 응답을 받지 못했습니다.");
            }
        } catch (URISyntaxException e) {
            rModel.setState(false);
            rModel.setMessage("잘못된 URI 형식입니다.");
        } catch (Exception e) {
            rModel.setState(false);
            rModel.setMessage("구글 로그인 중 오류가 발생했습니다: " + e.getMessage());
        }

최초 로그인일 경우 추가 정보 입력을 위해 회원가입 진행하고,

기존에 가입한 회원의 로그인일 경우 바로 일반 로그인과 같은 로직으로 로그인 처리.