Skip to content

Commit

Permalink
Merge pull request #30 from GoogleEyes-ewha/feature/#16
Browse files Browse the repository at this point in the history
[Feature] 구글 소셜 로그인 API 구현
  • Loading branch information
Haewonny authored Feb 11, 2024
2 parents 0640a40 + 9671155 commit bf09cc9
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.gdsc.hearo.domain.oauth.controller;

import com.gdsc.hearo.domain.member.dto.LoginResponseDto;
import com.gdsc.hearo.domain.member.entity.Member;
import com.gdsc.hearo.domain.oauth.dto.GoogleProfile;
import com.gdsc.hearo.domain.oauth.service.OauthService;
import com.gdsc.hearo.global.common.BaseException;
import com.gdsc.hearo.global.common.BaseResponse;
import com.gdsc.hearo.global.common.BaseResponseStatus;
import com.gdsc.hearo.global.security.JwtUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@RestController
@CrossOrigin
@RequiredArgsConstructor
@RequestMapping(value = "/auth")
@Slf4j
public class OauthController {

private final OauthService oauthService;
private final JwtUtil jwtUtil;

@GetMapping(value = "/google")
public void socialLoginType() {
oauthService.request();
}
@GetMapping(value = "/google/callback")
public BaseResponse<?> googleLogin(@RequestParam(name = "code") String code) {

try {
GoogleProfile googleProfile = oauthService.requestGoogleProfile(code);
Member member = oauthService.googleLogin(googleProfile);

String accessToken = jwtUtil.createAccessToken(member.getLoginId());
String refreshToken = jwtUtil.createRefreshToken(member.getLoginId());

LoginResponseDto loginResponseDto = new LoginResponseDto(accessToken, refreshToken);
return new BaseResponse<>(BaseResponseStatus.SUCCESS, loginResponseDto);
} catch (BaseException e) {
return new BaseResponse<>(e.getStatus());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.gdsc.hearo.domain.oauth.dto;

import lombok.Data;

@Data
public class GoogleOAuthToken {
private String access_token;
private String expires_in;
private String scope;
private String token_type;
private String id_token;
}
17 changes: 17 additions & 0 deletions src/main/java/com/gdsc/hearo/domain/oauth/dto/GoogleProfile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.gdsc.hearo.domain.oauth.dto;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;

@Data
@JsonIgnoreProperties(ignoreUnknown=true)
public class GoogleProfile {
private String id;
private String email;
private String verified_email;
private String name;
private String given_name;
private String family_name;
private String picture;
private String locale;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.gdsc.hearo.domain.oauth.service;

import com.gdsc.hearo.domain.member.entity.Member;
import com.gdsc.hearo.domain.member.repository.MemberRepository;
import com.gdsc.hearo.domain.oauth.dto.GoogleOAuthToken;
import com.gdsc.hearo.domain.oauth.dto.GoogleProfile;
import com.gdsc.hearo.domain.oauth.service.social.GoogleOauth;
import com.gdsc.hearo.global.common.BaseException;
import com.gdsc.hearo.global.common.BaseResponseStatus;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.io.IOException;

@Service
@RequiredArgsConstructor
public class OauthService {

private final GoogleOauth googleOauth;
private final HttpServletResponse response;
private final MemberRepository memberRepository;

public void request() {
String redirectURL = googleOauth.getOauthRedirectURL();
try {
response.sendRedirect(redirectURL);
} catch (IOException e) {
e.printStackTrace();
}
}


public GoogleProfile requestGoogleProfile(String code) {
GoogleOAuthToken googleOAuthToken = googleOauth.requestAccessToken(code);
return googleOauth.requestGoogleProfile(googleOAuthToken);
}

public Member googleLogin(GoogleProfile googleProfile) throws BaseException {
String email = googleProfile.getEmail();
String googleId = googleProfile.getId();
String username = googleProfile.getName();

Member member = memberRepository.findByLoginId(email);

if (member != null) {
throw new BaseException(BaseResponseStatus.DUPICATE_USER_ID);
}
Member newMember = Member.builder()
.username(username)
.loginId(email)
.GoogleId(googleId)
.loginType(Member.LoginType.GOOGLE)
.build();

memberRepository.save(newMember);

return newMember;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.gdsc.hearo.domain.oauth.service.social;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gdsc.hearo.domain.oauth.dto.GoogleOAuthToken;
import com.gdsc.hearo.domain.oauth.dto.GoogleProfile;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
@Slf4j
public class GoogleOauth {

@Value("${spring.google.url}")
private String GOOGLE_SNS_BASE_URL;
@Value("${spring.google.client-id}")
private String GOOGLE_SNS_CLIENT_ID;
@Value("${spring.google.callback}")
private String GOOGLE_SNS_CALLBACK_URL;
@Value("${spring.google.secret}")
private String GOOGLE_SNS_CLIENT_SECRET;
@Value("${spring.google.token}")
private String GOOGLE_SNS_TOKEN_BASE_URL;

public String getOauthRedirectURL() {
Map<String, Object> params = new HashMap<>();
params.put("scope", "profile email");
params.put("response_type", "code");
params.put("client_id", GOOGLE_SNS_CLIENT_ID);
params.put("redirect_uri", GOOGLE_SNS_CALLBACK_URL);

String parameterString = params.entrySet().stream()
.map(x -> x.getKey() + "=" + x.getValue())
.collect(Collectors.joining("&"));

return GOOGLE_SNS_BASE_URL + "?" + parameterString;
}

public GoogleOAuthToken requestAccessToken(String code) {
RestTemplate restTemplate = new RestTemplate();
ObjectMapper objectMapper = new ObjectMapper();
GoogleOAuthToken oAuthToken = null;

Map<String, Object> params = new HashMap<>();
params.put("code", code);
params.put("client_id", GOOGLE_SNS_CLIENT_ID);
params.put("client_secret", GOOGLE_SNS_CLIENT_SECRET);
params.put("redirect_uri", GOOGLE_SNS_CALLBACK_URL);
params.put("grant_type", "authorization_code");

ResponseEntity<String> response =
restTemplate.postForEntity(GOOGLE_SNS_TOKEN_BASE_URL, params, String.class);

try {
oAuthToken = objectMapper.readValue(response.getBody(), GoogleOAuthToken.class);
} catch (JsonMappingException e) {
throw new RuntimeException(e);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}

return oAuthToken;
}

public GoogleProfile requestGoogleProfile(GoogleOAuthToken oAuthToken) {

RestTemplate restTemplate = new RestTemplate();
ObjectMapper objectMapper = new ObjectMapper();

HttpHeaders header = new HttpHeaders();
header.add("Authorization", "Bearer " + oAuthToken.getAccess_token());

HttpEntity<MultiValueMap<String, String>> googleProfileRequest = new HttpEntity<>(header);

ResponseEntity<String> googleProfileResponse = restTemplate.exchange(
"https://www.googleapis.com/oauth2/v2/userinfo",
HttpMethod.GET,
googleProfileRequest,
String.class
);

GoogleProfile googleProfile = null;

try {
googleProfile = objectMapper.readValue(googleProfileResponse.getBody(), GoogleProfile.class);
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (JsonProcessingException e) {
e.printStackTrace();
}

return googleProfile;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public CorsConfigurationSource corsConfigurationSource() {

corsConfig.addAllowedOrigin("http://localhost:3000");
corsConfig.addAllowedOrigin("http://localhost:8080");
corsConfig.addAllowedOrigin("http://hearo-server.shop:8080");
corsConfig.addAllowedMethod("*");
corsConfig.addAllowedHeader("*");
corsConfig.setAllowCredentials(true);
Expand Down
11 changes: 10 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,18 @@ spring:
tokenExpirationTime: ${JWT_TOKEN_EXPIRATION_TIME}
refreshTokenExpTime: ${JWT_REFRESH_TOKEN_EXPIRATION_TIME}
secret: ${JWT_SECRET_KEY}
google:
url: https://accounts.google.com/o/oauth2/v2/auth
client-id: ${GOOGLE_CLIENT_ID}
callback: ${GOOGLE_REDIRECT_URL}
secret: ${GOOGLE_CLIENT_SECRET}
token: https://oauth2.googleapis.com/token

gemini:
api:
url: ${GEMINI_URL}
key: ${GEMINI_KEY}

server:
ssl:
key-store: /etc/letsencrypt/live/hearo-server.shop/keystore.p12
key-store-password: ${KEY_STORE_PASSWORD}

0 comments on commit bf09cc9

Please sign in to comment.