From ff533a251c42c97d181a9f2b5e707b2f21ab70b3 Mon Sep 17 00:00:00 2001 From: Lee Haewon Date: Sun, 11 Feb 2024 17:17:27 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=EC=84=9C=EB=B2=84=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=B6=94=EA=B0=80=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/gdsc/hearo/global/config/SecurityConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/gdsc/hearo/global/config/SecurityConfig.java b/src/main/java/com/gdsc/hearo/global/config/SecurityConfig.java index 03b1458..fdb2980 100644 --- a/src/main/java/com/gdsc/hearo/global/config/SecurityConfig.java +++ b/src/main/java/com/gdsc/hearo/global/config/SecurityConfig.java @@ -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); From ce9c11c5ff0f593f1af7ecc185f01fe044c07c01 Mon Sep 17 00:00:00 2001 From: Lee Haewon Date: Sun, 11 Feb 2024 17:19:51 +0900 Subject: [PATCH 2/6] =?UTF-8?q?chore:=20google=20OAuth=202.0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5a0dec1..98fc368 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -17,6 +17,12 @@ 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: From 345c42e9f75cc234bb4aa018ce47bcc0797bcfc3 Mon Sep 17 00:00:00 2001 From: Lee Haewon Date: Sun, 11 Feb 2024 17:20:37 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EC=86=8C=EC=85=9C=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20DTO=20=EC=B6=94=EA=B0=80=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/oauth/dto/GoogleOAuthToken.java | 12 ++++++++++++ .../hearo/domain/oauth/dto/GoogleProfile.java | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/main/java/com/gdsc/hearo/domain/oauth/dto/GoogleOAuthToken.java create mode 100644 src/main/java/com/gdsc/hearo/domain/oauth/dto/GoogleProfile.java diff --git a/src/main/java/com/gdsc/hearo/domain/oauth/dto/GoogleOAuthToken.java b/src/main/java/com/gdsc/hearo/domain/oauth/dto/GoogleOAuthToken.java new file mode 100644 index 0000000..3c94c58 --- /dev/null +++ b/src/main/java/com/gdsc/hearo/domain/oauth/dto/GoogleOAuthToken.java @@ -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; +} diff --git a/src/main/java/com/gdsc/hearo/domain/oauth/dto/GoogleProfile.java b/src/main/java/com/gdsc/hearo/domain/oauth/dto/GoogleProfile.java new file mode 100644 index 0000000..1d78b3e --- /dev/null +++ b/src/main/java/com/gdsc/hearo/domain/oauth/dto/GoogleProfile.java @@ -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; +} From 112447446ac3e7edbf048ce2f27ac1b311f8614f Mon Sep 17 00:00:00 2001 From: Lee Haewon Date: Sun, 11 Feb 2024 17:20:49 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=EC=86=8C=EC=85=9C=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20API=20=EA=B5=AC=ED=98=84=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth/controller/OauthController.java | 45 ++++++++ .../domain/oauth/service/OauthService.java | 60 ++++++++++ .../oauth/service/social/GoogleOauth.java | 106 ++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 src/main/java/com/gdsc/hearo/domain/oauth/controller/OauthController.java create mode 100644 src/main/java/com/gdsc/hearo/domain/oauth/service/OauthService.java create mode 100644 src/main/java/com/gdsc/hearo/domain/oauth/service/social/GoogleOauth.java diff --git a/src/main/java/com/gdsc/hearo/domain/oauth/controller/OauthController.java b/src/main/java/com/gdsc/hearo/domain/oauth/controller/OauthController.java new file mode 100644 index 0000000..4cb4010 --- /dev/null +++ b/src/main/java/com/gdsc/hearo/domain/oauth/controller/OauthController.java @@ -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()); + } + } +} diff --git a/src/main/java/com/gdsc/hearo/domain/oauth/service/OauthService.java b/src/main/java/com/gdsc/hearo/domain/oauth/service/OauthService.java new file mode 100644 index 0000000..bd1b356 --- /dev/null +++ b/src/main/java/com/gdsc/hearo/domain/oauth/service/OauthService.java @@ -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; + } +} diff --git a/src/main/java/com/gdsc/hearo/domain/oauth/service/social/GoogleOauth.java b/src/main/java/com/gdsc/hearo/domain/oauth/service/social/GoogleOauth.java new file mode 100644 index 0000000..9e59f94 --- /dev/null +++ b/src/main/java/com/gdsc/hearo/domain/oauth/service/social/GoogleOauth.java @@ -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 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 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 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> googleProfileRequest = new HttpEntity<>(header); + + ResponseEntity 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; + } +} From 4136985adab98506f809627f55893a59b56c1887 Mon Sep 17 00:00:00 2001 From: Lee Haewon Date: Sun, 11 Feb 2024 18:55:11 +0900 Subject: [PATCH 5/6] =?UTF-8?q?chore:=20SSL=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 98fc368..da88b82 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -29,3 +29,8 @@ gemini: url: ${GEMINI_URL} key: ${GEMINI_KEY} +server: + ssl: + key-store: classpath:keystore.p12 + key-store-type: PKCS12 + key-store-password: ${KEY_STORE_PASSWORD} \ No newline at end of file From 9671155f6b86458c682916cf24049e52df77dc6e Mon Sep 17 00:00:00 2001 From: Lee Haewon Date: Sun, 11 Feb 2024 19:47:04 +0900 Subject: [PATCH 6/6] =?UTF-8?q?chore:=20SSL=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index da88b82..f1c96c1 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -28,9 +28,7 @@ gemini: api: url: ${GEMINI_URL} key: ${GEMINI_KEY} - server: ssl: - key-store: classpath:keystore.p12 - key-store-type: PKCS12 + key-store: /etc/letsencrypt/live/hearo-server.shop/keystore.p12 key-store-password: ${KEY_STORE_PASSWORD} \ No newline at end of file