From 8770885ff87d720afdbda2c98d8908af2c1e6fbb Mon Sep 17 00:00:00 2001 From: Somi Date: Wed, 13 Jul 2022 23:52:01 +0900 Subject: [PATCH 1/9] feat : add error code about social login --- .../domain/user/repository/UserRepository.java | 1 + .../Team_C/global/config/RestTemplateConfig.java | 13 +++++++++++++ .../Team_C/global/error/exception/ErrorCode.java | 4 ++++ 3 files changed, 18 insertions(+) create mode 100644 src/main/java/com/Techeer/Team_C/global/config/RestTemplateConfig.java diff --git a/src/main/java/com/Techeer/Team_C/domain/user/repository/UserRepository.java b/src/main/java/com/Techeer/Team_C/domain/user/repository/UserRepository.java index 2d5fc23..892c690 100755 --- a/src/main/java/com/Techeer/Team_C/domain/user/repository/UserRepository.java +++ b/src/main/java/com/Techeer/Team_C/domain/user/repository/UserRepository.java @@ -13,4 +13,5 @@ public interface UserRepository { Optional findById(Long Userid); //회원의 email 값으로 정보 찾기 Optional findByEmail(String email); + } diff --git a/src/main/java/com/Techeer/Team_C/global/config/RestTemplateConfig.java b/src/main/java/com/Techeer/Team_C/global/config/RestTemplateConfig.java new file mode 100644 index 0000000..78401e8 --- /dev/null +++ b/src/main/java/com/Techeer/Team_C/global/config/RestTemplateConfig.java @@ -0,0 +1,13 @@ +package com.Techeer.Team_C.global.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/src/main/java/com/Techeer/Team_C/global/error/exception/ErrorCode.java b/src/main/java/com/Techeer/Team_C/global/error/exception/ErrorCode.java index 300f62f..afbb78e 100644 --- a/src/main/java/com/Techeer/Team_C/global/error/exception/ErrorCode.java +++ b/src/main/java/com/Techeer/Team_C/global/error/exception/ErrorCode.java @@ -21,6 +21,10 @@ public enum ErrorCode { NO_PERMISSION(403, "M005", "Do not have permission."), USER_NOT_FOUND(401,"M006","Not found this User"), + //Social Login + ACCESS_TOKEN_NOT_FOUND(400, "E00001", "Cannot access to SocialLogin"), + SOCIAL_LOGIN_USER_NOT_FOUND(401, "E00002", "Cannot find user from Social"), + //jwtToken INVALID_JTW_TOKEN_SIGNATURE(401, "J001", "The token's signature is invalid."), EXPIRED_JTW_TOKEN(401, "J002", "Token data has expired"), From 45834c3085766e3dda0c2f933316efc1e1baaac7 Mon Sep 17 00:00:00 2001 From: Somi Date: Wed, 13 Jul 2022 23:52:33 +0900 Subject: [PATCH 2/9] feat : create AuthorizationKakao.java file --- .../domain/auth/entity/AuthorizationKakao.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/com/Techeer/Team_C/domain/auth/entity/AuthorizationKakao.java diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/entity/AuthorizationKakao.java b/src/main/java/com/Techeer/Team_C/domain/auth/entity/AuthorizationKakao.java new file mode 100644 index 0000000..b02a97f --- /dev/null +++ b/src/main/java/com/Techeer/Team_C/domain/auth/entity/AuthorizationKakao.java @@ -0,0 +1,13 @@ +package com.Techeer.Team_C.domain.auth.entity; + +import lombok.Data; + +@Data +public class AuthorizationKakao { + private String access_token; + private String token_type; + private String refresh_token; + private int expires_in; + private String scope; + private int refresh_token_expires_in; +} From 2128cb0bc1e2ac0986d130f74b68808ccc5d1cb6 Mon Sep 17 00:00:00 2001 From: Somi Date: Thu, 14 Jul 2022 01:55:33 +0900 Subject: [PATCH 3/9] feat : create Kakao Social Login by auth code --- .../Team_C/domain/auth/dto/SessionUser.java | 1 + .../domain/auth/service/Oauth2Kakao.java | 122 ++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/dto/SessionUser.java b/src/main/java/com/Techeer/Team_C/domain/auth/dto/SessionUser.java index 90e01de..5a52fca 100644 --- a/src/main/java/com/Techeer/Team_C/domain/auth/dto/SessionUser.java +++ b/src/main/java/com/Techeer/Team_C/domain/auth/dto/SessionUser.java @@ -2,6 +2,7 @@ import com.Techeer.Team_C.domain.user.entity.User; import lombok.Getter; +import lombok.Setter; import java.io.Serializable; diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java b/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java new file mode 100644 index 0000000..e27f177 --- /dev/null +++ b/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java @@ -0,0 +1,122 @@ +package com.Techeer.Team_C.domain.auth.service; + +import com.Techeer.Team_C.domain.auth.entity.AuthorizationKakao; +import com.Techeer.Team_C.domain.user.entity.Role; +import com.Techeer.Team_C.domain.user.entity.User; +import com.Techeer.Team_C.domain.user.repository.UserRepository; +import com.Techeer.Team_C.global.error.exception.BusinessException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.util.Optional; + +import static com.Techeer.Team_C.global.error.exception.ErrorCode.*; + +@Slf4j +@Service +@RequiredArgsConstructor +public class Oauth2Kakao { + private final RestTemplate restTemplate; + private final ObjectMapper objectMapper; + private final UserRepository userRepository; + + @Value("${spring.security.oauth2.client.registration.kakao.authorization-grant-type}") + private String grantType; + + @Value("${spring.security.oauth2.client.registration.kakao.client-id}") + private String kakaoOauth2ClientId; + + @Value("${spring.security.oauth2.client.registration.kakao.redirect-uri}") + private String redirectUrl; + + @Value("${spring.security.oauth2.client.registration.kakao.client-secret}") + private String clientSecret; + + public AuthorizationKakao getAccessTokenByCode(String code) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("grant_type", grantType); + params.add("client_id", kakaoOauth2ClientId); + params.add("redirect_uri", redirectUrl); + params.add("code", code); + params.add("client_secret", clientSecret); + + HttpEntity> request = new HttpEntity<>(params, headers); + + String url = "https://kauth.kakao.com/oauth/token"; + try { + ResponseEntity response = restTemplate.postForEntity(url, request, String.class); + + AuthorizationKakao authorization = objectMapper.readValue(response.getBody(), AuthorizationKakao.class); + + return authorization; + } catch (RestClientException | JsonProcessingException ex) { + ex.printStackTrace(); + throw new BusinessException("소셜로그인 접근 오류", ACCESS_TOKEN_NOT_FOUND); + } + } + + + /** + * accessToken 을 이용한 유저정보 받기 + * @return + */ + public User getUserByAccessToken(String accessToken) { + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + accessToken); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap params = new LinkedMultiValueMap<>(); + HttpEntity> request = new HttpEntity<>(params, headers); + + String url = "https://kapi.kakao.com/v2/user/me"; + Optional userByEmail = null; + try { + String userInfo = restTemplate.postForObject(url, request, String.class); + System.out.println(userInfo); + + JsonNode jsonNode = objectMapper.readTree(userInfo); + String email = String.valueOf(jsonNode.get("kakao_account") + .get("email")); + String name = String.valueOf(jsonNode.get("kakao_account") + .get("profile") + .get("nickname")); + + userByEmail = userRepository.findByEmail(email); + if (!userByEmail.isPresent()) { + User user = new User(); + user.setEmail(email.substring(1, email.length() - 1)); + user.setMemberName(name.substring(1, name.length() - 1)); + user.setRole(Role.ROLE_USER); + user.setActivated(true); + + return userRepository.save(user); + } + return userByEmail.get(); + } catch (RestClientException ex) { + ex.printStackTrace(); + throw new BusinessException("Social login user not found", SOCIAL_LOGIN_USER_NOT_FOUND); + } catch (JsonMappingException e) { + e.printStackTrace(); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return userByEmail.get(); + } +} From 4534594bf342d9f1dcbf67bb3d1484cf70e3d3ca Mon Sep 17 00:00:00 2001 From: Somi Date: Thu, 14 Jul 2022 01:55:47 +0900 Subject: [PATCH 4/9] feat : create jwt token by social login email --- .../domain/auth/jwt/JwtTokenProvider.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/jwt/JwtTokenProvider.java b/src/main/java/com/Techeer/Team_C/domain/auth/jwt/JwtTokenProvider.java index 9e2f3bf..f5a962e 100644 --- a/src/main/java/com/Techeer/Team_C/domain/auth/jwt/JwtTokenProvider.java +++ b/src/main/java/com/Techeer/Team_C/domain/auth/jwt/JwtTokenProvider.java @@ -2,6 +2,7 @@ package com.Techeer.Team_C.domain.auth.jwt; import com.Techeer.Team_C.domain.auth.dto.TokenDto; +import com.Techeer.Team_C.domain.user.entity.Role; import io.jsonwebtoken.*; import javax.servlet.ServletRequest; import lombok.extern.slf4j.Slf4j; @@ -80,6 +81,35 @@ public TokenDto createToken(Authentication authentication) { } + public TokenDto createTokenSocialLogin(String email) { + + Role authority = Role.ROLE_USER; + long now = (new Date()).getTime(); + + // Access Token 생성 + Date accessTokenExpiresIn = new Date(now + tokenValidTime); + String accessToken = Jwts.builder() + .setSubject(email) // payload "sub": "name" + .claim(AUTHORITIES_KEY, authority) // payload "auth": "ROLE_USER" + .setExpiration(accessTokenExpiresIn) // payload "exp": 1516239022 (예시) + .signWith(SignatureAlgorithm.HS256, secretKey) // header "alg": "HS512" + .compact(); + + // Refresh Token 생성 + String refreshToken = Jwts.builder() + .setExpiration(new Date(now + refreshTokenValidTime)) + .signWith(SignatureAlgorithm.HS256, secretKey) + .compact(); + + return TokenDto.builder() + .grantType(BEARER_TYPE) + .accessToken(accessToken) + .accessTokenExpiresIn(accessTokenExpiresIn.getTime()) + .refreshToken(refreshToken) + .build(); + } + + public Authentication getAuthentication(String accessToken) { // 토큰 복호화 Claims claims = parseClaims(accessToken); From bf9157d15a096317a9224cbc1b5eddb4a55b652a Mon Sep 17 00:00:00 2001 From: Somi Date: Thu, 14 Jul 2022 16:51:24 +0900 Subject: [PATCH 5/9] feat : create google sns login func --- .../auth/controller/AuthController.java | 17 +-- .../auth/entity/AuthorizationGoogle.java | 16 +++ .../domain/auth/service/AuthService.java | 33 ++++++ .../domain/auth/service/Oauth2Google.java | 110 ++++++++++++++++++ .../domain/auth/service/Oauth2Kakao.java | 1 - 5 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/Techeer/Team_C/domain/auth/entity/AuthorizationGoogle.java create mode 100644 src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Google.java diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/controller/AuthController.java b/src/main/java/com/Techeer/Team_C/domain/auth/controller/AuthController.java index 3605fd6..f715a88 100644 --- a/src/main/java/com/Techeer/Team_C/domain/auth/controller/AuthController.java +++ b/src/main/java/com/Techeer/Team_C/domain/auth/controller/AuthController.java @@ -1,5 +1,6 @@ package com.Techeer.Team_C.domain.auth.controller; +import com.Techeer.Team_C.domain.auth.entity.AuthorizationKakao; import com.Techeer.Team_C.domain.user.dto.LoginFormDto; import com.Techeer.Team_C.domain.auth.dto.TokenDto; import com.Techeer.Team_C.domain.auth.dto.TokenRefreshDto; @@ -7,9 +8,9 @@ import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; -import org.json.simple.JSONObject; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.oauth2.core.OAuth2Token; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -55,15 +56,17 @@ public ResponseEntity logout(@RequestBody TokenRefreshDto tokenRefreshDto) return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } - @GetMapping("/kakao") + @GetMapping("/token/kakao") @ApiOperation(value = "kakao 소셜 로그인", notes = "소셜로그인 API (kakao)") - public void kakaoCallback(@RequestParam String code) { - System.out.println(code); + public ResponseEntity kakaoCallback(@RequestParam("code") String code) { + + return ResponseEntity.ok(authService.oauth2AuthorizationKakao(code)); } - @GetMapping("/google") + @GetMapping("/token/google") @ApiOperation(value = "google 소셜 로그인", notes = "소설로그인 API (google)") - public void googleCallback(@RequestParam String code) { - System.out.println(code); + public ResponseEntity googleCallback(@RequestParam("code") String code) { + + return ResponseEntity.ok(authService.oauth2AuthorizationGoogle(code)); } } diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/entity/AuthorizationGoogle.java b/src/main/java/com/Techeer/Team_C/domain/auth/entity/AuthorizationGoogle.java new file mode 100644 index 0000000..e3346c3 --- /dev/null +++ b/src/main/java/com/Techeer/Team_C/domain/auth/entity/AuthorizationGoogle.java @@ -0,0 +1,16 @@ +package com.Techeer.Team_C.domain.auth.entity; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class AuthorizationGoogle { + private String access_token; + private String expires_in; + private String id_token; + private String scope; + private String token_type; +} diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/service/AuthService.java b/src/main/java/com/Techeer/Team_C/domain/auth/service/AuthService.java index cee74be..d71ccc4 100644 --- a/src/main/java/com/Techeer/Team_C/domain/auth/service/AuthService.java +++ b/src/main/java/com/Techeer/Team_C/domain/auth/service/AuthService.java @@ -1,5 +1,7 @@ package com.Techeer.Team_C.domain.auth.service; +import com.Techeer.Team_C.domain.auth.entity.AuthorizationGoogle; +import com.Techeer.Team_C.domain.auth.entity.AuthorizationKakao; import com.Techeer.Team_C.domain.auth.jwt.JwtTokenProvider; import com.Techeer.Team_C.domain.auth.entity.RefreshToken; import com.Techeer.Team_C.domain.auth.repository.RefreshTokenRepository; @@ -7,6 +9,7 @@ import com.Techeer.Team_C.domain.user.dto.LoginFormDto; import com.Techeer.Team_C.domain.auth.dto.TokenDto; import com.Techeer.Team_C.domain.user.dto.UserDto; +import com.Techeer.Team_C.domain.user.entity.User; import com.Techeer.Team_C.domain.user.service.UserService; import com.Techeer.Team_C.global.error.exception.BusinessException; import io.jsonwebtoken.SignatureException; @@ -28,6 +31,8 @@ public class AuthService { private final RefreshTokenRepository refreshTokenRepository; private final UserService userService; private final PasswordEncoder passwordEncoder; + private final Oauth2Kakao oauth2Kakao; + private final Oauth2Google oauth2Google; /** * 로그인 @@ -132,5 +137,33 @@ public RefreshToken refreshTokenValidCheck(Authentication authentication, } return refreshToken; } + + public TokenDto oauth2AuthorizationKakao(String code) { + AuthorizationKakao authorization = oauth2Kakao.getAccessTokenByCode(code); + User userInfoFromKakao = oauth2Kakao.getUserByAccessToken(authorization.getAccess_token()); + + // JWT 토큰 생성 + TokenDto tokenDto = tokenProvider.createTokenSocialLogin(userInfoFromKakao.getEmail()); + + RefreshToken refreshToken = RefreshToken.builder().key(userInfoFromKakao.getEmail()) + .value(tokenDto.getRefreshToken()).build(); + refreshTokenRepository.save(refreshToken); + + return tokenDto; + } + + public TokenDto oauth2AuthorizationGoogle(String code) { + AuthorizationGoogle authorization = oauth2Google.getAccessTokenByCode(code); + User userInfoFromGoogle = oauth2Google.getUserByAccessToken(authorization.getAccess_token()); + + TokenDto tokenDto = tokenProvider.createTokenSocialLogin(userInfoFromGoogle.getEmail()); + + RefreshToken refreshToken = RefreshToken.builder().key(userInfoFromGoogle.getEmail()) + .value(tokenDto.getRefreshToken()).build(); + refreshTokenRepository.save(refreshToken); + + return tokenDto; + } + } diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Google.java b/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Google.java new file mode 100644 index 0000000..4ddb0c7 --- /dev/null +++ b/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Google.java @@ -0,0 +1,110 @@ +package com.Techeer.Team_C.domain.auth.service; + +import com.Techeer.Team_C.domain.auth.entity.AuthorizationGoogle; +import com.Techeer.Team_C.domain.user.entity.Role; +import com.Techeer.Team_C.domain.user.entity.User; +import com.Techeer.Team_C.domain.user.repository.UserRepository; +import com.Techeer.Team_C.global.error.exception.BusinessException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.util.Optional; + +import static com.Techeer.Team_C.global.error.exception.ErrorCode.ACCESS_TOKEN_NOT_FOUND; +import static com.Techeer.Team_C.global.error.exception.ErrorCode.SOCIAL_LOGIN_USER_NOT_FOUND; + +@Slf4j +@Service +@RequiredArgsConstructor +public class Oauth2Google { + private final RestTemplate restTemplate; + private final ObjectMapper objectMapper; + private final UserRepository userRepository; + + @Value("${spring.security.oauth2.client.registration.google.client-id}") + private String GOOGLE_SNS_CLIENT_ID; + + @Value("${spring.security.oauth2.client.registration.google.redirect-uri}") + private String GOOGLE_SNS_CALLBACK_URL; + + @Value("${spring.security.oauth2.client.registration.google.client-secret}") + private String GOOGLE_SNS_CLIENT_SECRET; + + public AuthorizationGoogle getAccessTokenByCode(String code) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("code", code); + params.add("client_id", GOOGLE_SNS_CLIENT_ID); + params.add("client_secret", GOOGLE_SNS_CLIENT_SECRET); + params.add("redirect_uri", GOOGLE_SNS_CALLBACK_URL); + params.add("grant_type", "authorization_code"); + + HttpEntity> request = new HttpEntity<>(params, headers); + + String url = "https://oauth2.googleapis.com/token"; + try { + ResponseEntity response = restTemplate.postForEntity(url, request, String.class); + AuthorizationGoogle authorization = objectMapper.readValue(response.getBody(), AuthorizationGoogle.class); + return authorization; + } catch (RestClientException | JsonProcessingException ex) { + ex.printStackTrace(); + throw new BusinessException("소셜로그인 접근 오류", ACCESS_TOKEN_NOT_FOUND); + } + } + + public User getUserByAccessToken(String accessToken) { + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + accessToken); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + HttpEntity> request = new HttpEntity<>(headers); + + String url = "https://www.googleapis.com/oauth2/v1/userinfo"; + Optional userByEmail = null; + try { + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, request, String.class); + String userInfo = response.getBody(); + + JsonNode jsonNode = objectMapper.readTree(userInfo); + String email = String.valueOf(jsonNode.get("email")); + String name = String.valueOf(jsonNode.get("name")); + + userByEmail = userRepository.findByEmail(email); + if (!userByEmail.isPresent()) { + User user = new User(); + user.setEmail(email.substring(1, email.length() - 1)); + user.setMemberName(name.substring(1, name.length() - 1)); + user.setRole(Role.ROLE_USER); + user.setActivated(true); + + return userRepository.save(user); + } + return userByEmail.get(); + } catch (RestClientException ex) { + ex.printStackTrace(); + throw new BusinessException("Social login user not found", SOCIAL_LOGIN_USER_NOT_FOUND); + } catch (JsonMappingException e) { + e.printStackTrace(); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return userByEmail.get(); + } +} diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java b/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java index e27f177..5e9d6a5 100644 --- a/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java +++ b/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java @@ -89,7 +89,6 @@ public User getUserByAccessToken(String accessToken) { Optional userByEmail = null; try { String userInfo = restTemplate.postForObject(url, request, String.class); - System.out.println(userInfo); JsonNode jsonNode = objectMapper.readTree(userInfo); String email = String.valueOf(jsonNode.get("kakao_account") From f458b94b643aadfbc6ce1a7f3cdf7f12fc3e5615 Mon Sep 17 00:00:00 2001 From: Somi Date: Thu, 14 Jul 2022 16:51:36 +0900 Subject: [PATCH 6/9] chore : delete unnecessary code --- src/main/java/com/Techeer/Team_C/domain/user/entity/User.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/Techeer/Team_C/domain/user/entity/User.java b/src/main/java/com/Techeer/Team_C/domain/user/entity/User.java index c426be7..2854bff 100755 --- a/src/main/java/com/Techeer/Team_C/domain/user/entity/User.java +++ b/src/main/java/com/Techeer/Team_C/domain/user/entity/User.java @@ -1,7 +1,6 @@ package com.Techeer.Team_C.domain.user.entity; import com.Techeer.Team_C.domain.product.entity.ProductRegister; -//import com.Techeer.Team_C.domain.product.entity.ProductRegisterId; import com.Techeer.Team_C.global.utils.dto.BaseTimeEntity; import com.Techeer.Team_C.global.utils.dto.BooleanToYNConverter; import java.util.List; @@ -28,7 +27,6 @@ import lombok.Setter; - import org.springframework.data.jpa.domain.support.AuditingEntityListener; import org.springframework.lang.Nullable; import org.springframework.security.core.GrantedAuthority; From 4cb45fbbb572abdb1b5b34778db8a08ec4390b92 Mon Sep 17 00:00:00 2001 From: Somi Date: Thu, 14 Jul 2022 17:11:47 +0900 Subject: [PATCH 7/9] chore : handle duplicated email problem --- .../domain/auth/service/Oauth2Kakao.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java b/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java index 5e9d6a5..865ed99 100644 --- a/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java +++ b/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Kakao.java @@ -72,9 +72,9 @@ public AuthorizationKakao getAccessTokenByCode(String code) { } } - /** * accessToken 을 이용한 유저정보 받기 + * * @return */ public User getUserByAccessToken(String accessToken) { @@ -91,19 +91,23 @@ public User getUserByAccessToken(String accessToken) { String userInfo = restTemplate.postForObject(url, request, String.class); JsonNode jsonNode = objectMapper.readTree(userInfo); - String email = String.valueOf(jsonNode.get("kakao_account") + String rawEmail = String.valueOf(jsonNode.get("kakao_account") .get("email")); - String name = String.valueOf(jsonNode.get("kakao_account") + String rawName = String.valueOf(jsonNode.get("kakao_account") .get("profile") .get("nickname")); + String email = rawEmail.substring(1, rawEmail.length() - 1); + String name = rawName.substring(1, rawName.length() - 1); + userByEmail = userRepository.findByEmail(email); if (!userByEmail.isPresent()) { - User user = new User(); - user.setEmail(email.substring(1, email.length() - 1)); - user.setMemberName(name.substring(1, name.length() - 1)); - user.setRole(Role.ROLE_USER); - user.setActivated(true); + User user = User.builder() + .email(email) + .memberName(name) + .role(Role.ROLE_USER) + .activated(true) + .build(); return userRepository.save(user); } From 11ea47c8dc4da524ec9a834ac15ab68dacf654b7 Mon Sep 17 00:00:00 2001 From: Somi Date: Thu, 14 Jul 2022 17:12:01 +0900 Subject: [PATCH 8/9] refactor : use user.builder --- .../domain/auth/service/Oauth2Google.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Google.java b/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Google.java index 4ddb0c7..adbc56d 100644 --- a/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Google.java +++ b/src/main/java/com/Techeer/Team_C/domain/auth/service/Oauth2Google.java @@ -83,16 +83,20 @@ public User getUserByAccessToken(String accessToken) { String userInfo = response.getBody(); JsonNode jsonNode = objectMapper.readTree(userInfo); - String email = String.valueOf(jsonNode.get("email")); - String name = String.valueOf(jsonNode.get("name")); + String rawEmail = String.valueOf(jsonNode.get("email")); + String rawName = String.valueOf(jsonNode.get("name")); + + String email = rawEmail.substring(1, rawEmail.length() - 1); + String name = rawName.substring(1, rawName.length() - 1); userByEmail = userRepository.findByEmail(email); if (!userByEmail.isPresent()) { - User user = new User(); - user.setEmail(email.substring(1, email.length() - 1)); - user.setMemberName(name.substring(1, name.length() - 1)); - user.setRole(Role.ROLE_USER); - user.setActivated(true); + User user = User.builder() + .email(email) + .memberName(name) + .role(Role.ROLE_USER) + .activated(true) + .build(); return userRepository.save(user); } From 9f3b7e457e8b7777fe87c58d11cd8e3412f993b4 Mon Sep 17 00:00:00 2001 From: Somi Date: Fri, 15 Jul 2022 13:52:34 +0900 Subject: [PATCH 9/9] chore : make sns jwt token by userId --- .../domain/auth/jwt/JwtTokenProvider.java | 47 ++++++++++++------- .../domain/auth/service/AuthService.java | 27 +++++++---- 2 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/jwt/JwtTokenProvider.java b/src/main/java/com/Techeer/Team_C/domain/auth/jwt/JwtTokenProvider.java index f5a962e..c88911f 100644 --- a/src/main/java/com/Techeer/Team_C/domain/auth/jwt/JwtTokenProvider.java +++ b/src/main/java/com/Techeer/Team_C/domain/auth/jwt/JwtTokenProvider.java @@ -1,10 +1,11 @@ - package com.Techeer.Team_C.domain.auth.jwt; import com.Techeer.Team_C.domain.auth.dto.TokenDto; import com.Techeer.Team_C.domain.user.entity.Role; import io.jsonwebtoken.*; + import javax.servlet.ServletRequest; + import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -31,14 +32,14 @@ public class JwtTokenProvider { @Value("${jwt.secret}") //.yml파일을 통해 secret키 관리 private String secretKey; - private long tokenValidTime = 30 * 60 * 1000L; //토큰 유효시간 30분 private long refreshTokenValidTime = 7 * 24 * 60 * 60 * 1000L; // refresh 유효시간 : 7일 // 객체 초기화, secretKey를 Base64로 인코딩한다. @PostConstruct protected void init() { - secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); + secretKey = Base64.getEncoder() + .encodeToString(secretKey.getBytes()); } // JWT 토큰 생성 @@ -51,7 +52,8 @@ protected void init() { */ public TokenDto createToken(Authentication authentication) { // 권한들 가져오기 - String authorities = authentication.getAuthorities().stream() + String authorities = authentication.getAuthorities() + .stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(",")); @@ -80,8 +82,7 @@ public TokenDto createToken(Authentication authentication) { .build(); } - - public TokenDto createTokenSocialLogin(String email) { + public TokenDto createTokenSocialLogin(Long userId) { Role authority = Role.ROLE_USER; long now = (new Date()).getTime(); @@ -89,7 +90,7 @@ public TokenDto createTokenSocialLogin(String email) { // Access Token 생성 Date accessTokenExpiresIn = new Date(now + tokenValidTime); String accessToken = Jwts.builder() - .setSubject(email) // payload "sub": "name" + .setSubject(String.valueOf(userId)) // payload "sub": "name" .claim(AUTHORITIES_KEY, authority) // payload "auth": "ROLE_USER" .setExpiration(accessTokenExpiresIn) // payload "exp": 1516239022 (예시) .signWith(SignatureAlgorithm.HS256, secretKey) // header "alg": "HS512" @@ -109,7 +110,6 @@ public TokenDto createTokenSocialLogin(String email) { .build(); } - public Authentication getAuthentication(String accessToken) { // 토큰 복호화 Claims claims = parseClaims(accessToken); @@ -119,10 +119,11 @@ public Authentication getAuthentication(String accessToken) { } // 클레임에서 권한 정보 가져오기 - Collection authorities = - Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) - .map(SimpleGrantedAuthority::new) - .collect(Collectors.toList()); + Collection authorities = Arrays.stream(claims.get(AUTHORITIES_KEY) + .toString() + .split(",")) + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); // UserDetails 객체를 만들어서 Authentication 리턴 UserDetails principal = new User(claims.getSubject(), "", authorities); @@ -133,18 +134,24 @@ public Authentication getAuthentication(String accessToken) { //토큰 복호화 함수 private Claims parseClaims(String accessToken) { try { - return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(accessToken).getBody(); + return Jwts.parser() + .setSigningKey(secretKey) + .parseClaimsJws(accessToken) + .getBody(); } catch (ExpiredJwtException e) { return e.getClaims(); } } - // 토큰의 유효성 + 만료일자 확인 public boolean validateToken(ServletRequest request, String jwtToken) { try { - Jws claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken); - return !claims.getBody().getExpiration().before(new Date()); + Jws claims = Jwts.parser() + .setSigningKey(secretKey) + .parseClaimsJws(jwtToken); + return !claims.getBody() + .getExpiration() + .before(new Date()); } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { log.info("잘못된 JWT 서명입니다."); request.setAttribute("exception", INVALID_JTW_TOKEN_SIGNATURE); @@ -170,8 +177,12 @@ public boolean validateToken(ServletRequest request, String jwtToken) { public boolean validateToken(String jwtToken) { try { - Jws claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken); - return !claims.getBody().getExpiration().before(new Date()); + Jws claims = Jwts.parser() + .setSigningKey(secretKey) + .parseClaimsJws(jwtToken); + return !claims.getBody() + .getExpiration() + .before(new Date()); } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { log.info("잘못된 JWT 서명입니다."); } catch (ExpiredJwtException e) { diff --git a/src/main/java/com/Techeer/Team_C/domain/auth/service/AuthService.java b/src/main/java/com/Techeer/Team_C/domain/auth/service/AuthService.java index d71ccc4..12717c1 100644 --- a/src/main/java/com/Techeer/Team_C/domain/auth/service/AuthService.java +++ b/src/main/java/com/Techeer/Team_C/domain/auth/service/AuthService.java @@ -61,8 +61,10 @@ public TokenDto login(LoginFormDto loginformDto) { TokenDto tokenDto = tokenProvider.createToken(authentication); // 4. RefreshToken 저장 - RefreshToken refreshToken = RefreshToken.builder().key(authentication.getName()) - .value(tokenDto.getRefreshToken()).build(); + RefreshToken refreshToken = RefreshToken.builder() + .key(authentication.getName()) + .value(tokenDto.getRefreshToken()) + .build(); refreshTokenRepository.save(refreshToken); @@ -124,7 +126,7 @@ public Authentication authenticationValidCheck(TokenRefreshDto tokenRefreshDto) } public RefreshToken refreshTokenValidCheck(Authentication authentication, - TokenRefreshDto tokenRefreshDto) { + TokenRefreshDto tokenRefreshDto) { // 저장소에서 Member ID 를 기반으로 Refresh Token 값 가져옴 RefreshToken refreshToken = refreshTokenRepository.findByKey(authentication.getName()) .orElseThrow( @@ -132,7 +134,8 @@ public RefreshToken refreshTokenValidCheck(Authentication authentication, // Refresh Token 일치하는지 검사 - if (!refreshToken.getValue().equals(tokenRefreshDto.getRefreshToken())) { + if (!refreshToken.getValue() + .equals(tokenRefreshDto.getRefreshToken())) { throw new BusinessException("토큰과 유저정보가 서로 일치하지 않습니다.", MISMATCHED_USER_INFORMATION); } return refreshToken; @@ -143,10 +146,12 @@ public TokenDto oauth2AuthorizationKakao(String code) { User userInfoFromKakao = oauth2Kakao.getUserByAccessToken(authorization.getAccess_token()); // JWT 토큰 생성 - TokenDto tokenDto = tokenProvider.createTokenSocialLogin(userInfoFromKakao.getEmail()); + TokenDto tokenDto = tokenProvider.createTokenSocialLogin(userInfoFromKakao.getUserId()); - RefreshToken refreshToken = RefreshToken.builder().key(userInfoFromKakao.getEmail()) - .value(tokenDto.getRefreshToken()).build(); + RefreshToken refreshToken = RefreshToken.builder() + .key(String.valueOf(userInfoFromKakao.getUserId())) + .value(tokenDto.getRefreshToken()) + .build(); refreshTokenRepository.save(refreshToken); return tokenDto; @@ -156,10 +161,12 @@ public TokenDto oauth2AuthorizationGoogle(String code) { AuthorizationGoogle authorization = oauth2Google.getAccessTokenByCode(code); User userInfoFromGoogle = oauth2Google.getUserByAccessToken(authorization.getAccess_token()); - TokenDto tokenDto = tokenProvider.createTokenSocialLogin(userInfoFromGoogle.getEmail()); + TokenDto tokenDto = tokenProvider.createTokenSocialLogin(userInfoFromGoogle.getUserId()); - RefreshToken refreshToken = RefreshToken.builder().key(userInfoFromGoogle.getEmail()) - .value(tokenDto.getRefreshToken()).build(); + RefreshToken refreshToken = RefreshToken.builder() + .key(String.valueOf(userInfoFromGoogle.getUserId())) + .value(tokenDto.getRefreshToken()) + .build(); refreshTokenRepository.save(refreshToken); return tokenDto;