구글로 로그인하기:
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 |