본문 바로가기
▶ Back-End/Java

SNS 간편로그인 - 구글 로그인 API 호출 및 사용자 정보 받기

by 오늘도 코딩 2024. 7. 15.
728x90
반응형

Spring Boot에서 RestTemplate을 통해 구글 Open API를 호출하여 사용자 정보 받기

*SNS 간편로그인 - 구글 API 사용 등록 방법 완료 후 진행(관련 글 참고)

*프로세스는 카카오와 네이버 모두 동일(OAuth 2.0 X)

*Swagger3  관련 소스 포함

*자세한 설명 생략

 

 

▷ application.properties 설정

#GOOGLE
google.client.id="GOOGLE에서 제공한 Client ID"
google.client.secret="GOOGLE에서 제공한 Client Secret"
google.redirect.uri="GOOGLE에 설정한 Redirect URI"

 

 

▷ google.html

*로그인 화면(간단히 버튼만 생성)

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>구글 테스트 페이지</title>
</head>
<body>

   <div align="center">
      <h1>구글 로그인 테스트</h1><br><br>
      
      <a th:href="
         @{https://accounts.google.com/o/oauth2/v2/auth(
              client_id=${clientId},
              redirect_uri=${redirectUri},
              response_type=${responseType},
              scope=${scope}
              )}">
           <img src="/images/google_login_btn.png">
      </a>
   </div>
</body>
</html>

 

 

▷ CmmUtil.java

*공통 유틸리티

/**
 * 공통 유틸리티
 */
public class CmmUtil {

   /**
     * 
     * Object to String
     * 
     * @param Object
     * @return String
     * @throws JsonProcessingException
     */
    public static String getObjStr(Object obj) throws JsonProcessingException {
        return new ObjectMapper()
                .registerModule(new JavaTimeModule())
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .writeValueAsString(obj);
    }
}

 

 

▷ GoogleController.java

*로그인 화면 진입 Controller 생성

/**
 * 구글 TEST Controller
 */
@Controller
@RequestMapping("test")
public class GoogleContrller {
   
   @Value("${google.client.id}")
   private String GOOGLE_CLIENT_ID;

   @Value("${google.redirect.uri}")
   private String GOOGLE_REDIRECT_URI;

   @GetMapping("/google.do")
    public String kakaoLogin(Model model){
      
        model.addAttribute("clientId", GOOGLE_CLIENT_ID);
        model.addAttribute("redirectUri", GOOGLE_REDIRECT_URI);
        model.addAttribute("scope", "profile email");
        model.addAttribute("responseType", "code");
        
        return "google";
    }
   
}

 

 

▷ LGN1002Controller.java

*Redirect Controller 생성

/**
 * 구글 Login Controller
 */
@Slf4j
@RestController
@RequestMapping("swai")
@Tag(name = "LGN", description = "Login Controller")
public class LGN1002Controller {
   
   @Autowired
   private LGNService lgnService;

   @GetMapping("/google/login.do")
   @Operation(summary = "SWAI-LGN-1002", description = "[구글] 로그인(Redirect)")
   public LGN1002ResVO lgn1002(@Parameter(description = "인가 코드") @RequestParam("code") String code) {

      LGN1002ResVO resVO = new LGN1002ResVO();
      
      try {
         
         resVO = lgnService.getLGN1002(code);
         
         log.info("[구글] 로그인 최종 응답");
         log.info(CmmUtil.getObjStr(resVO));
      } catch (Exception e) {
	  
      }
      
      return resVO;
   }

}

 

 

▷ LGN1002ResVO.java

*최종 응답 VO

/**
 * [구글] 로그인 응답 VO
 */
@Getter
@Setter
@ToString
@Schema(description = "[구글] 로그인 응답 VO")
public class LGN1002ResVO extends CmmResVO {

   /** 결과 값 */
   @JsonProperty("result")
   @Schema(description = "결과 값")
   public LGN1002VO result;

   @Getter
   @Setter
   @ToString
   @Schema(description = "[구글] 로그인 응답 VO")
   public static class LGN1002VO {

      @JsonProperty("sub")
      @Schema(description = "사용자의 고유 식별자. 모든 Google 계정에서 유일하며 재사용되지 않습니다.")
      public String sub;

      @JsonProperty("name")
      @Schema(description = "사용자의 전체 이름 (표시 가능한 형식).")
      public String name;

      @JsonProperty("given_name")
      @Schema(description = "사용자의 이름.")
      public String givenName;

      @JsonProperty("family_name")
      @Schema(description = "사용자의 성.")
      public String familyName;

      @JsonProperty("picture")
      @Schema(description = "사용자의 프로필 사진 URL.")
      public String picture;

      @JsonProperty("email")
      @Schema(description = "사용자의 이메일 주소. 이 값은 유일하지 않을 수 있으며 시간이 지남에 따라 변경될 수 있습니다.")
      public String email;

      @JsonProperty("email_verified")
      @Schema(description = "사용자의 이메일 주소가 확인되었는지 여부. true일 경우 이메일이 확인된 상태입니다.")
      public boolean emailVerified;

      @JsonProperty("locale")
      @Schema(description = "사용자의 언어 및 국가 정보. BCP 47 언어 태그 형식으로 제공됩니다.")
      public String locale;
   }

}

 

 

▷ CmmResVO.java

*공통 응답

/**
 * 공통 응답 VO
 */
@Getter
@Setter
@ToString
@Schema(description = "공통 응답 VO")
public class CmmResVO {

    /** Result Code */
    @JsonProperty("resultCode")
    @Schema(description = "Result Code")
    public String resultCode = "0000";

    /** Result Msg */
    @JsonProperty("resultMsg")
    @Schema(description = "Result Msg")
    public String resultMsg = "성공";

    /** Set Result */
    public CmmResVO setResCmmResult(String resultCode, String resultMsg) {
        this.resultCode = resultCode;
        this.resultMsg = resultMsg;

        return this;
    }
}

 

 

▷ LGNService.java

/**
 * Login Service
 */
public interface LGNService {
   
   /** [구글] 로그인 **/
   public LGN1002ResVO getLGN1002(String code);
}

 

 

▷ LGNServiceImpl.java

/**
 * Login ServiceImpl
 *
 */
@Service
public class LGNServiceImpl implements LGNService {

   @Autowired
   private GoogleApiService googleApi;

   /**
    * [구글] 로그인
    */
   @Override
   public LGN1002ResVO getLGN1002(String code) {

      LGN1002ResVO result = new LGN1002ResVO();

      try {
         /* [구글] 토큰 발급 */
         GoogleOAuthTokenVO token = googleApi.getOAuthToken(code);

         /* [구글] 사용자 정보 조회 */
         GoogleUserInfoVO userInfo = googleApi.getUserInfo(token.getAccess_token());

         /* 사용자 정보 저장 */

         /* 응답 값 설정 */
         LGN1002VO vo = new LGN1002VO();
         vo.setSub(userInfo.getSub());
         vo.setName(userInfo.getName());
         vo.setGivenName(userInfo.getGivenName());
         vo.setFamilyName(userInfo.getFamilyName());
         vo.setPicture(userInfo.getPicture());
         vo.setEmail(userInfo.getEmail());
         vo.setEmailVerified(userInfo.isEmailVerified());
         vo.setLocale(userInfo.getLocale());

         result.setResult(vo);

      } catch (Exception e) {
         
      }

      return result;
   }

}

 

 

▷ GoogleOAuthTokenVO.java

/**
 * [구글] 토큰 발급 응답 VO
 */
@Getter
@Setter
@ToString
@Schema(description = "[구글] 토큰 발급 응답 VO")
@JsonIgnoreProperties(ignoreUnknown = true)
public class GoogleOAuthTokenVO {

   @JsonProperty("access_token")
   @Schema(description = "애플리케이션에서 Google API 요청을 승인하기 위해 보내는 토큰입니다.")
   public String access_token;
   
   @JsonProperty("refresh_token")
   @Schema(description = "새 액세스 토큰을 얻는 데 사용할 수 있는 토큰입니다. 갱신 토큰은 사용자가 액세스 권한을 취소할 때까지 유효합니다.")
   public String refresh_token;
   
}

 

 

▷ GoogleUserInfoVO.java

/**
 * [구글] 사용자 정보 조회 응답 VO
 */
@Getter
@Setter
@ToString
@Schema(description = "[구글] 사용자 정보 조회 응답 VO")
@JsonIgnoreProperties(ignoreUnknown = true)
public class GoogleUserInfoVO {

   @JsonProperty("sub")
   @Schema(description = "사용자의 고유 식별자. 모든 Google 계정에서 유일하며 재사용되지 않습니다.")
   public String sub;

   @JsonProperty("name")
   @Schema(description = "사용자의 전체 이름 (표시 가능한 형식).")
   public String name;

   @JsonProperty("given_name")
   @Schema(description = "사용자의 이름.")
   public String givenName;

   @JsonProperty("family_name")
   @Schema(description = "사용자의 성.")
   public String familyName;

   @JsonProperty("picture")
   @Schema(description = "사용자의 프로필 사진 URL.")
   public String picture;

   @JsonProperty("email")
   @Schema(description = "사용자의 이메일 주소. 이 값은 유일하지 않을 수 있으며 시간이 지남에 따라 변경될 수 있습니다.")
   public String email;

   @JsonProperty("email_verified")
   @Schema(description = "사용자의 이메일 주소가 확인되었는지 여부. true일 경우 이메일이 확인된 상태입니다.")
   public boolean emailVerified;

   @JsonProperty("locale")
   @Schema(description = "사용자의 언어 및 국가 정보. BCP 47 언어 태그 형식으로 제공됩니다.")
   public String locale;

}

 

 

▷ GoogleApiService.java

/**
 * 구글 API Service
 * 
 * https://developers.google.com/identity/protocols/oauth2?hl=ko
 */
public interface GoogleApiService {
   
   /** [구글] 토큰 발급 **/
   public GoogleOAuthTokenVO getOAuthToken(String code);
   
   /** [구글] 사용자 정보 조회 **/
   public GoogleUserInfoVO getUserInfo(String accessToken);
   
}

 

 

 ▷ GoogleApiServiceImpl.java

/**
 * 구글 API ServiceImpl
 *
 */
@Slf4j
@Service
public class GoogleApiServiceImpl implements GoogleApiService {

   @Value("${google.client.id}")
   private String GOOGLE_CLIENT_ID;

   @Value("${google.client.secret}")
   private String GOOGLE_API_SECRET;

   @Value("${google.redirect.uri}")
   private String GOOGLE_REDIRECT_URI;

   /**
    * [구글] 토큰 발급
    */
   @Override
   public GoogleOAuthTokenVO getOAuthToken(String code) {

      GoogleOAuthTokenVO result = new GoogleOAuthTokenVO();

      try {
         log.info("[구글] 토큰 발급 시작");

         if (code == null) {
            return result;
         }

         /* 요청 */
         final String uri = "https://oauth2.googleapis.com/token";

         HttpHeaders headers = new HttpHeaders();
         headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // MultiValueMap

         MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
         body.add("grant_type", "authorization_code");
         body.add("client_id", GOOGLE_CLIENT_ID);
         body.add("client_secret", GOOGLE_API_SECRET);
         body.add("redirect_uri", GOOGLE_REDIRECT_URI);
         body.add("code", code);

         /* 응답 */
         ResponseEntity<String> res = new RestTemplate().postForEntity(uri, new HttpEntity<>(body, headers),
               String.class);
         result = new ObjectMapper().readValue(res.getBody(), GoogleOAuthTokenVO.class);

         log.info("[구글] 토큰 발급 종료");
      } catch (Exception e) {
        
      }

      return result;
   }

   /**
    * [구글] 사용자 정보 조회
    */
   @Override
   public GoogleUserInfoVO getUserInfo(String accessToken) {

      GoogleUserInfoVO result = new GoogleUserInfoVO();

      try {
         log.info("[구글] 사용자 정보 조회 시작");

         if (accessToken == null) {
            return result;
         }

         /* 요청 */
         final String uri = "https://www.googleapis.com/oauth2/v3/userinfo";

         HttpHeaders headers = new HttpHeaders();
         headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // MultiValueMap
         headers.add("Authorization", "Bearer " + accessToken);

         /* 응답 */
         ResponseEntity<String> res = new RestTemplate().postForEntity(uri, new HttpEntity<>(headers), String.class);
         result = new ObjectMapper().readValue(res.getBody(), GoogleUserInfoVO.class);

         log.info("[구글] 사용자 정보 조회 종료");
      } catch (Exception e) {
         
      }

      return result;
   }
}

 

 

▷ 결과

소스가 수정됨에 결과화면과 다를 수 있음

 

 

▷ 관련 글

 

SNS 간편로그인 - 구글 API 사용 등록 방법

구글 Open API를 사용하기 위한 구글 developers 사용 설정*자세한 설명 생략  ▷ 구글  API 사용 등록 방법 ⓛ 프로젝트 생성 Google 클라우드 플랫폼로그인 Google 클라우드 플랫폼으로 이동accounts.goog

coding-today.tistory.com

 

RestTemplate Post 요청 보내기

RestTemplate로 Post 요청을 보내고, 응답 코드를 확인하는 메서드*Header 값(Authorization)만 추가하고, Body는 빈 값으로 요청*자세한 설명 생략   /** * POST 요청 */public static boolean sendPost(String jwt) { boolean r

coding-today.tistory.com

 

Objcet to String

Object를 String으로 변환하는 간단한 방법 /** * * Object to String * * @param Object * @return String */ public static String getObjStr(Object obj) { String str = ""; try { str = new ObjectMapper().writeValueAsString(obj); } catch (Exception e)

coding-today.tistory.com

 

JSON Unrecognized field 해결방법(@JsonIgnoreProperties)

JSON 데이터를 구성하는 요소가 가변적일 때 무시하는 방법 *소스는 변하지 않았지만 연동 했던 API 응답 값이 변했다고 가정 ▷ ERROR - JSON 데이터를 매핑하지 못해 에러 발생 com.fasterxml.jackson.databi

coding-today.tistory.com

 

SNS 간편로그인 - 카카오 API 사용 등록 방법

카카오 Open API를 사용하기 위한 카카오 developers 앱 등록*자세한 설명 생략  ▷ 카카오 API 사용 등록 방법  ① 내 애플리케이션 생성 Kakao Developers카카오 API를 활용하여 다양한 어플리케이션을

coding-today.tistory.com

 

SNS 간편로그인 - 네이버 API 사용 등록 방법

네이버 Open API를 사용하기 위한 네이버 developers 앱 등록*자세한 설명 생략  ▷ 네이버 API 사용 등록 방법 ① 내 애플리케이션 생성 애플리케이션 - NAVER Developers developers.naver.com  ▷ 관련 글 S

coding-today.tistory.com

 

 

▷ 참고

 

OAuth 2.0 홈  |  Apigee  |  Google Cloud

projects.locations.apis.versions.definitions

cloud.google.com

 

OAuth 2.0을 사용하여 Google API에 액세스하기  |  Authorization  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 의견 보내기 OAuth 2.0을 사용하여 Google API에 액세스하기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

developers.google.com

 

웹 서버 애플리케이션용 OAuth 2.0 사용  |  Authorization  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 의견 보내기 웹 서버 애플리케이션용 OAuth 2.0 사용 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이

developers.google.com

 

OpenID Connect  |  Authentication  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 의견 보내기 OpenID Connect 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Google의 OAuth 2.0 API는 인증과 승

developers.google.com

 

 

728x90
728x90

댓글