본문 바로가기

TIL

2022.08.15.TIL

구글로 로그인하기:

https://console.cloud.google.com/welcome?project=caramel-graph-359505 

 

Google 클라우드 플랫폼

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

accounts.google.com

API 및 서비스 - 사용자 인증 정보 - 사용자 인증 정보 만들기 - OAuth 클라이언트 ID - 앱 유형 선택 - 이름 설정 - 승인된 리디렉션 URI 지정. 이 때 발급되는 클라이언트ID(Client Id), 클라이언트 보안비밀(Client Secret)을 잘 저장해 두어야 하며, 노출되어선 안 됨.

(콜백주소 추가는 localhost아니면 도메인!만 가능하다(카카오도 동일))

 

RestController에서는 json으로 출력되어 redirect로 리턴해도 문자열 그대로 나오기 때문에 Oauth용 컨트롤러를 추가함

@Controller
@CrossOrigin(origins = "*", allowedHeaders = "*")
public class OAtuhController {
    private final MemberService memberService;

    @Autowired
    OAtuhController(MemberService memberService){
        this.memberService = memberService;
    }
    @Value("${google.client.id}")
    private String GOOGLE_CLIENT_ID;
    @Value("${google.redirect.url}")
    private String GOOGLE_REDIRECT_URI;

    @Value("${kakao.client.id}")
    private String KAKAO_CLIENT_ID;
    @Value("${kakao.redirect.url}")
    private String KAKAO_REDIRECT_URI;

    private final String RESPONSE_TYPE = "code";

    @GetMapping("/user/google/login")
    public String googleLogin(){
        String SCOPE = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile";
        String ENDPOINT = "https://accounts.google.com/o/oauth2/v2/auth";
        return "redirect:" + ENDPOINT + "?client_id=" + GOOGLE_CLIENT_ID + "&redirect_uri=" + GOOGLE_REDIRECT_URI
                + "&response_type=" + RESPONSE_TYPE + "&scope=" + SCOPE;
    }

    @ResponseBody
    @GetMapping("/user/google/callback")
    public ResponseDto<?> oauthLogin(@RequestParam String code, HttpServletResponse response) {
        return memberService.googleLogin(code, response);
    }

    @GetMapping("/user/kakao/login")
    public String kakaoLogin(){
        String ENDPOINT = "https://kauth.kakao.com/oauth/authorize";
        return "redirect:" + ENDPOINT + "?client_id=" + KAKAO_CLIENT_ID + "&redirect_uri=" +
                KAKAO_REDIRECT_URI + "&response_type=" + RESPONSE_TYPE;
    }

    @ResponseBody
    @GetMapping("/user/kakao/callback")
    public ResponseDto<?> kakaoLogin(@RequestParam String code, HttpServletResponse response){
        return memberService.kakaoLogin(code, response);
    }
}

리다이렉트로 구글로그인 api를 불러오고 code를 받아온다.

properties에 숨길 변수 정보들을 저장함

google.client.id={구글 클라이언트 아이디}
google.client.pw={구글 클라이언트 페스워드}
google.redirect.url={구글 로그인 콜백주소}

kakao.client.id={카카오 클리이언트 아이디}
kakao.redirect.url={카카오 로그인 콜백주소}

서비스 속 내용은 카카오 로그인 구현과 매우 비슷하다

@Value("${google.client.id}")
private String CLIENT_ID;
@Value("${google.client.pw}")
private String CLIENT_SECRET;
@Value("${google.redirect.url}")
private String REDIRECT_URI;
public ResponseDto<?> googleLogin(String code, HttpServletResponse response) {
  try {
    // 1. "인가 코드"로 "액세스 토큰" 요청
    String accessToken = getGoogleAccessToken(code);
    // 2. 토큰으로 카카오 API 호출
    GoogleUserInfoDto googleUserInfo = getGoogleUserInfo(accessToken);

    // DB 에 중복된 Kakao Id 가 있는지 확인
    String googleId = googleUserInfo.getId();
    Member googleUser = memberRepository.findByGoogleId(googleId)
            .orElse(null);
    if(googleUser != null){
      TokenDto tokenDto = tokenProvider.generateTokenDto(googleUser);
      tokenToHeaders(tokenDto, response);
      return ResponseDto.success(
              MemberResponseDto.builder()
                      .id(googleUser.getId())
                      .username(googleUser.getUsername())
                      .nickname(googleUser.getNickname())
                      .createdAt(googleUser.getCreatedAt())
                      .modifiedAt(googleUser.getModifiedAt())
                      .build()
      );
    }
    else{
      // 회원가입
      Member member = Member.builder()
              .username(UUID.randomUUID().toString())
              .nickname(googleUserInfo.getName())
              .password(passwordEncoder.encode(UUID.randomUUID().toString()))
              .googleId(googleId)
              .build();
      memberRepository.save(member);
      TokenDto tokenDto = tokenProvider.generateTokenDto(member);
      tokenToHeaders(tokenDto, response);
      return ResponseDto.success(
              MemberResponseDto.builder()
                      .id(member.getId())
                      .username(member.getUsername())
                      .nickname(member.getNickname())
                      .createdAt(member.getCreatedAt())
                      .modifiedAt(member.getModifiedAt())
                      .build()
      );
    }


  }catch (JsonProcessingException e){
    return ResponseDto.fail("403","Failed to log in with Google");
  }
}

1. 콜백으로 받은 코드로 엑세스 토큰을 요청해 받아온다

2. 엑세스 토큰으로 유저 정보를 받아온다

3. 유저정보속 id로 가입여부를 확인 후 가입or로그인 후 토큰 발급하고 헤더에 담아리턴

 

private String getGoogleAccessToken(String code) throws JsonProcessingException {
  String url = "https://oauth2.googleapis.com/token";

  MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
  params.add("code", code);
  params.add("client_id", CLIENT_ID);
  params.add("client_secret", CLIENT_SECRET);
  params.add("redirect_uri", REDIRECT_URI);
  params.add("grant_type", "authorization_code");

  HttpHeaders headers = new HttpHeaders();
  headers.add("Content-Type", "application/x-www-form-urlencoded");


  HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(params, headers);
  RestTemplate rt = new RestTemplate();//서버 대 서버 요청을 보냄
  ResponseEntity<String> accessTokenResponse = rt.exchange(url, HttpMethod.POST, httpEntity, String.class);

  // HTTP 응답 (JSON) -> 액세스 토큰 파싱
  String responseBody = accessTokenResponse.getBody();//바디부분
  ObjectMapper objectMapper = new ObjectMapper();
  JsonNode jsonNode = objectMapper.readTree(responseBody);//json형태를 객체형태로 바꾸기
  return jsonNode.get("access_token").asText();
}

code로 구글 서버에 response를 받아온 뒤 json으로 바꾸고 필요한 access_token을 빼낸다.

응답 온 respose 예시: 

{
"access_token":"어쩌구",

"expires_in":3599,
"scope":"openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile",
"token_type":"Bearer",
"id_token":"어쩌구"
}

 

private GoogleUserInfoDto getGoogleUserInfo(String accessToken) throws JsonProcessingException {
  String url = "https://www.googleapis.com/oauth2/v1/userinfo";
  // HTTP Header 생성
  HttpHeaders headers = new HttpHeaders();
  headers.add("Authorization", "Bearer " + accessToken);
  // HTTP 요청 보내기
  HttpEntity<MultiValueMap<String, String>> request = new HttpEntity(headers);
  RestTemplate rt = new RestTemplate();//서버 대 서버 요청을 보냄
  ResponseEntity<String> response = rt.exchange(url, HttpMethod.GET, request, String.class);

  ObjectMapper objectMapper = new ObjectMapper();
  JsonNode jsonNode = objectMapper.readTree(response.getBody());
  String id = jsonNode.get("id").toString();
  String name = jsonNode.get("name").toString();
  return new GoogleUserInfoDto(id,name);
}

엑세스 토큰으로 구글 서버에서 사용자 정보를 response로 받아온다

역시나 동일하게 json으로 바꾼 후 원하는 정보를 뽑아낸다.

사용자 정보 예시:

{
"id":"구글개인번호",
"email":"이메일@gmail.com",
"verified_email":true,
"name":"길동홍",
"given_name":"홍",
"family_name":"길동",
"picture":"사진url",
"locale":"ko"
}

카카오와 다르게 구글은 id가 string이기에 long으로 받으면 0이 들어온다 주의!

 

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String username;

@Column(nullable = false)
private String nickname;

@Column(nullable = false)
@JsonIgnore
private String password;

@Column(unique = true)
private Long kakaoId;

@Column(unique = true)
private String googleId;

멤버테이블은 카카오 때와 동일하게 사용자를 구분할 googleId를 추가한다(String으로)

이후 일반 로그인 단계에서 카카오와 동일하게 googleId가 null인지 확인하는 단계를 추가한다.

 

properties로 중요한 변수 숨기기

https://wpioneer.tistory.com/223

 

[Spring Boot] properties 를 통해서 키 값 숨기기

우리가 프로젝트를 진행하다보면 API key와 같은 민감 정보들을 사용할때가 있다. 그런 민감 정보들을 소스에다가 적어두고 깃허브에 올리거나 타인에게 공유를 하게 되면 민감 정보들이 유출될

wpioneer.tistory.com

도움을 준 블로그: https://bibi6666667.tistory.com/313

 

[Spring] 구글 OAuth 구현하기 (+JWT)

[Spring] 구글 OAuth 구현하기 (+JWT) 출처 https://preamtree.tistory.com/167 https://withseungryu.tistory.com/116 구글 공식문서 - OAuth2.0으로 구글API 액세스하기 특히 "서버측 웹 앱용 OAuth 2.0" 참고..

bibi6666667.tistory.com

세션방식(인듯?)구현 (참고)

https://mieumje.tistory.com/79

 

[Spring Boot] 구글 로그인 구현

구글 서비스 등록 구글 서비스에 신규 서비스를 생성한다. https://console.cloud.google.com/ Google Cloud Platform 하나의 계정으로 모든 Google 서비스를 Google Cloud Platform을 사용하려면 로그인하세요. a..

mieumje.tistory.com

 

'TIL' 카테고리의 다른 글

2022.08.18.TIL  (0) 2022.08.18
2022.08.16.TIL  (0) 2022.08.16
2022.08.14.TIL  (0) 2022.08.14
2022.08.11.TIL  (0) 2022.08.11
2022.08.09.TIL  (0) 2022.08.09