2023.03.27 - [Project/모면] - [Spring Boot] Security 없이 OAuth2로 Google 로그인 구현, 유저 정보 얻기
이전 시간에 access_token을 발급받아오는 작업까지 마쳤다. 이제 id_token 안의 정보를 복호해 <내 서버의 회원>으로 만들어버리자.
먼저 RestTemplate을 이용해 POST 요청을 보내는 코드를 수정했다.
ResponseEntity<GoogleOAuthResponseDto> responseEntity = restTemplate
.postForEntity(GOOGLE_TOKEN_URL, params, GoogleOAuthResponseDto.class);
package com.momyeon.backend.dto;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class GoogleOAuthResponseDto {
private String access_token;
private String expires_in;
private String scope;
private String token_type;
private String id_token;
}
postForEntity 메서드의 세 번째 파라미터를 CustomDto.class로 변경했다. JSON 형식의 String을 response body를 바로 클래스 객체로 역직렬화하게 했다. 여기서 회원 정보를 가지고 있는 field는 id_token이다.
Google 공식 문서에서 알 수 있듯이 id_token은 Base64Url로 암호화 되어있다.
import io.jsonwebtoken.impl.Base64UrlCodec;
// ...
public String decryptBase64UrlToken(String jwtToken) {
byte[] decode = new Base64UrlCodec().decode(jwtToken);
return new String(decode, StandardCharsets.UTF_8);
}
수많은 디코딩 방식이 존재하지만 "io.jsonwebtoken:jjwt" 의존성의 디코딩 방법을 선택했다. 가장 깔끔하기 때문.
여기서 한 가지 문제점이 발생했다.
JWT 토큰은 {header}.{payload}.{signature} 형식으로 구성되어 있다.
그래서 그런지 디코딩하면 heaer는 정상적으로 디코딩 되는데 payload 부분은 제대로 해독되지 않는 현상이 발생했다.
String decodedInfo = decryptBase64UrlToken(responseEntity.getBody().getId_token().split("\\.")[1]);
그래서 완성된 코드이다. 헤더 부분이 필요 없어서 split("\\.") 코드로 짤라버렸다. 뭐.. 나중에 필요한 경우가 생긴다면... 미래의 내가 하겠지.... 몰랐는데 온점(.)도 이스케이프 문자가 필요하다고 한다.
private MemberInfoDto transJsonToMemberInfoDto(String json) {
try {
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MemberInfoDto dto = mapper.readValue(json, MemberInfoDto.class);
saveMemberInfo(dto);
return dto;
} catch (JsonMappingException e) {
throw new RuntimeException(e);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
MemberInfoDto로 파싱해주고!
JSON 파싱하는 건 다음 글에서 확인 가능하다. (๑╹o╹)✎
2023.04.02 - [Backend/Java] - [Java] String to Json 파싱하기 | Jackson ObjectMapper
public void saveMemberInfo(MemberInfoDto dto) {
Optional<Member> member = memberRepository.findById(dto.getSub());
member.ifPresentOrElse(null, () -> memberRepository.save(dto.toEntity()));
}
MemberId가 Repository에 없을 때 저장하도록 구현하였다.
DB에 저장되는 것도 확인됐다!
'Project > 모면' 카테고리의 다른 글
[Spring Boot] 테스트 코드에서 프로퍼티 NullPointerException 오류 해결 (0) | 2023.04.06 |
---|---|
[Spring Boot] 개인 정보가 담긴 Jwt 토큰 발급 후 Cookie로 반환하기 (0) | 2023.04.04 |
[Spring Boot] 면접 질문 카테고리 분류하기 (0) | 2023.04.01 |
도망친 곳엔 낙원은 없다 (0) | 2023.03.29 |
[Spring Boot] Security 없이 OAuth2로 Google 로그인 구현, 유저 정보 얻기 (2) | 2023.03.27 |