diff --git a/build.gradle b/build.gradle index 88ddbc7a..6c808873 100644 --- a/build.gradle +++ b/build.gradle @@ -19,14 +19,10 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-web' - //Swagger, RestDocs + //Swagger implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.1.0' testImplementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-api', version: '2.1.0' - //외부 API 호출할 때 쓰는 RestTemplate 라이브러리 - implementation group: 'org.apache.httpcomponents.client5', name: 'httpclient5', version: '5.2.1' - implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.0' - //S3 implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' @@ -34,12 +30,16 @@ dependencies { implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5' runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' + implementation 'org.bouncycastle:bcpkix-jdk18on:1.77' //lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" + // http client + implementation 'org.apache.httpcomponents.client5:httpclient5:5.3.1' + //db implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.32' runtimeOnly 'mysql:mysql-connector-java' @@ -60,7 +60,6 @@ dependencies { //Monitoring implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'io.micrometer:micrometer-registry-prometheus' - } tasks.named('test') { diff --git a/src/main/java/com/shallwe/domain/auth/application/AuthService.java b/src/main/java/com/shallwe/domain/auth/application/AuthService.java index 628f8b2c..f0dc7169 100644 --- a/src/main/java/com/shallwe/domain/auth/application/AuthService.java +++ b/src/main/java/com/shallwe/domain/auth/application/AuthService.java @@ -2,15 +2,15 @@ import java.util.Optional; +import com.shallwe.domain.auth.domain.AppleToken; +import com.shallwe.domain.auth.domain.repository.AppleTokenRepository; import com.shallwe.domain.auth.dto.*; -import com.shallwe.domain.auth.exception.AlreadyExistEmailException; -import com.shallwe.domain.auth.exception.InvalidPasswordException; -import com.shallwe.domain.auth.exception.InvalidProviderIdException; -import com.shallwe.domain.auth.exception.UnRegisteredUserException; +import com.shallwe.domain.auth.dto.request.*; +import com.shallwe.domain.auth.dto.response.AuthRes; +import com.shallwe.domain.auth.exception.*; import com.shallwe.domain.common.Status; import com.shallwe.domain.shopowner.domain.ShopOwner; import com.shallwe.domain.shopowner.domain.repository.ShopOwnerRepository; -import com.shallwe.domain.auth.dto.ShopOwnerChangePasswordReq; import com.shallwe.domain.shopowner.exception.AlreadyExistPhoneNumberException; import com.shallwe.domain.shopowner.exception.InvalidPhoneNumberException; import com.shallwe.domain.shopowner.exception.InvalidShopOwnerException; @@ -23,7 +23,6 @@ import com.shallwe.domain.user.domain.User; import com.shallwe.global.config.security.token.UserPrincipal; import com.shallwe.global.error.DefaultAuthenticationException; -import com.shallwe.global.infrastructure.feign.apple.AppleClient; import com.shallwe.global.payload.ErrorCode; import com.shallwe.global.payload.Message; import com.shallwe.domain.auth.domain.repository.TokenRepository; @@ -32,16 +31,13 @@ import com.shallwe.global.utils.AppleJwtUtils; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; -import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import lombok.RequiredArgsConstructor; -import org.springframework.web.client.RestTemplate; @Service @@ -57,6 +53,7 @@ public class AuthService { private final TokenRepository tokenRepository; private final UserRepository userRepository; private final ShopOwnerRepository shopOwnerRepository; + private final AppleTokenRepository appleTokenRepository; @Transactional public AuthRes signUp(final SignUpReq signUpReq) { @@ -66,33 +63,41 @@ public AuthRes signUp(final SignUpReq signUpReq) { User newUser = User.builder() .providerId(signUpReq.getProviderId()) .provider(signUpReq.getProvider()) - .name(signUpReq.getNickname()) .email(signUpReq.getEmail()) - .profileImgUrl(signUpReq.getProfileImgUrl()) .password(passwordEncoder.encode(signUpReq.getProviderId())) .role(Role.USER) .build(); - userRepository.save(newUser); - UserPrincipal userPrincipal = UserPrincipal.createUser(newUser); - Authentication authentication = new UsernamePasswordAuthenticationToken( - userPrincipal, - null, - userPrincipal.getAuthorities() - ); + return getUserAuthRes(newUser); + } - TokenMapping tokenMapping = customTokenProviderService.createToken(authentication); - Token token = Token.builder() - .refreshToken(tokenMapping.getRefreshToken()) - .userEmail(tokenMapping.getUserEmail()) + @Transactional + public AuthRes appleSignUp(AppleSignUpReq appleSignUpReq) { + Claims claims = appleJwtUtils.getClaimsBy(appleSignUpReq.getIdentityToken()); + String providerId = claims.get("sub").toString(); + + if (userRepository.existsByProviderId(providerId)) { + throw new AlreadyExistsProviderIdException(); + } + + String appleRefreshToken = appleJwtUtils.getAppleToken(appleSignUpReq.getAuthorizationCode()); + AppleToken appleTokenReq = AppleToken.builder() + .providerId(providerId) + .refreshToken(appleRefreshToken) .build(); - tokenRepository.save(token); + appleTokenRepository.save(appleTokenReq); - return AuthRes.builder() - .accessToken(tokenMapping.getAccessToken()) - .refreshToken(tokenMapping.getRefreshToken()) + User newUser = User.builder() + .providerId(providerId) + .provider(Provider.APPLE) + .email(appleSignUpReq.getEmail()) + .password(passwordEncoder.encode(providerId)) + .role(Role.USER) .build(); + userRepository.save(newUser); + + return getUserAuthRes(newUser); } @Transactional @@ -103,25 +108,20 @@ public AuthRes signIn(final SignInReq signInReq) { throw new InvalidPasswordException(); } - UserPrincipal userPrincipal = UserPrincipal.createUser(user); - Authentication authentication = new UsernamePasswordAuthenticationToken( - userPrincipal, - null, - userPrincipal.getAuthorities() - ); + return getUserAuthRes(user); + } - TokenMapping tokenMapping = customTokenProviderService.createToken(authentication); - Token token = Token.builder() - .refreshToken(tokenMapping.getRefreshToken()) - .userEmail(tokenMapping.getUserEmail()) - .build(); + @Transactional + public AuthRes appleSignIn(AppleSignInReq appleSignInReq) { + Claims claims = appleJwtUtils.getClaimsBy(appleSignInReq.getIdentityToken()); + String providerId = claims.get("sub").toString(); - tokenRepository.save(token); + User user = userRepository.findByProviderId(providerId).orElseThrow(InvalidProviderIdException::new); + if (user.getName() == null || user.getPhoneNumber() == null || user.getAge() == null || user.getGender() == null) { + throw new UnRegisteredUserException(); + } - return AuthRes.builder() - .accessToken(tokenMapping.getAccessToken()) - .refreshToken(token.getRefreshToken()) - .build(); + return getUserAuthRes(user); } @Transactional @@ -148,9 +148,10 @@ public AuthRes refresh(final RefreshTokenReq tokenRefreshRequest) { Token updateToken = token.updateRefreshToken(tokenMapping.getRefreshToken()); tokenRepository.save(updateToken); - AuthRes authResponse = AuthRes.builder().accessToken(tokenMapping.getAccessToken()).refreshToken(updateToken.getRefreshToken()).build(); - - return authResponse; + return AuthRes.builder(). + accessToken(tokenMapping.getAccessToken()) + .refreshToken(updateToken.getRefreshToken()) + .build(); } @Transactional @@ -176,30 +177,9 @@ public AuthRes shopOwnerSignUp(final ShopOwnerSignUpReq shopOwnerSignUpReq) { .password(passwordEncoder.encode(shopOwnerSignUpReq.getPassword())) .marketingConsent(shopOwnerSignUpReq.getMarketingConsent()) .build(); - shopOwnerRepository.save(shopOwner); - UserPrincipal userPrincipal = UserPrincipal.createShopOwner(shopOwner); - Authentication authentication = new UsernamePasswordAuthenticationToken( - userPrincipal, - null, - userPrincipal.getAuthorities() - ); - - TokenMapping tokenMapping = customTokenProviderService.createToken(authentication); - - Token token = Token.builder() - .refreshToken(tokenMapping.getRefreshToken()) - .userEmail(tokenMapping.getUserEmail()) - .build(); - tokenRepository.save(token); - - AuthRes authRes = AuthRes.builder() - .accessToken(tokenMapping.getAccessToken()) - .refreshToken(token.getRefreshToken()) - .build(); - - return authRes; + return getShopOwnerAuthRes(shopOwner); } @Transactional @@ -211,27 +191,7 @@ public AuthRes shopOwnerSignIn(final ShopOwnerSignInReq shopOwnerSignInReq) { throw new InvalidPasswordException(); } - UserPrincipal userPrincipal = UserPrincipal.createShopOwner(shopOwner); - Authentication authentication = new UsernamePasswordAuthenticationToken( - userPrincipal, - null, - userPrincipal.getAuthorities() - ); - - TokenMapping tokenMapping = customTokenProviderService.createToken(authentication); - - Token token = Token.builder() - .refreshToken(tokenMapping.getRefreshToken()) - .userEmail(tokenMapping.getUserEmail()) - .build(); - tokenRepository.save(token); - - AuthRes authRes = AuthRes.builder() - .accessToken(tokenMapping.getAccessToken()) - .refreshToken(token.getRefreshToken()) - .build(); - - return authRes; + return getShopOwnerAuthRes(shopOwner); } @Transactional @@ -262,17 +222,30 @@ private boolean valid(final String refreshToken) { return true; } - @Transactional - public AuthRes appleSignIn(AppleSignInReq appleSignInReq) { - Claims claims = appleJwtUtils.getClaimsBy(appleSignInReq.getIdentityToken()); - String providerId = claims.get("sub").toString(); + private AuthRes getUserAuthRes(User user) { + UserPrincipal userPrincipal = UserPrincipal.createUser(user); + Authentication authentication = new UsernamePasswordAuthenticationToken( + userPrincipal, + null, + userPrincipal.getAuthorities() + ); - User user = userRepository.findByProviderId(providerId).orElseThrow(InvalidProviderIdException::new); - if(user.getName() == null || user.getPhoneNumber() == null || user.getAge() == null || user.getGender() == null) { - throw new UnRegisteredUserException(); - } + TokenMapping tokenMapping = customTokenProviderService.createToken(authentication); + Token token = Token.builder() + .refreshToken(tokenMapping.getRefreshToken()) + .userEmail(tokenMapping.getUserEmail()) + .build(); - UserPrincipal userPrincipal = UserPrincipal.createUser(user); + tokenRepository.save(token); + + return AuthRes.builder() + .accessToken(tokenMapping.getAccessToken()) + .refreshToken(token.getRefreshToken()) + .build(); + } + + private AuthRes getShopOwnerAuthRes(ShopOwner shopOwner) { + UserPrincipal userPrincipal = UserPrincipal.createShopOwner(shopOwner); Authentication authentication = new UsernamePasswordAuthenticationToken( userPrincipal, null, @@ -280,6 +253,7 @@ public AuthRes appleSignIn(AppleSignInReq appleSignInReq) { ); TokenMapping tokenMapping = customTokenProviderService.createToken(authentication); + Token token = Token.builder() .refreshToken(tokenMapping.getRefreshToken()) .userEmail(tokenMapping.getUserEmail()) diff --git a/src/main/java/com/shallwe/domain/auth/application/CustomDefaultOAuth2UserService.java b/src/main/java/com/shallwe/domain/auth/application/CustomDefaultOAuth2UserService.java index bd8d50f9..9f5cbd2c 100644 --- a/src/main/java/com/shallwe/domain/auth/application/CustomDefaultOAuth2UserService.java +++ b/src/main/java/com/shallwe/domain/auth/application/CustomDefaultOAuth2UserService.java @@ -60,7 +60,6 @@ private User registerNewUser(OAuth2UserRequest oAuth2UserRequest, OAuth2UserInfo .providerId(oAuth2UserInfo.getId()) .name(oAuth2UserInfo.getName()) .email(oAuth2UserInfo.getEmail()) - .profileImgUrl(oAuth2UserInfo.getImageUrl()) .role(Role.USER) .birthDay(oAuth2UserInfo.getBirthDay()) .build(); @@ -69,10 +68,7 @@ private User registerNewUser(OAuth2UserRequest oAuth2UserRequest, OAuth2UserInfo } private User updateExistingUser(User user, OAuth2UserInfo oAuth2UserInfo) { - user.updateName(oAuth2UserInfo.getName()); - user.updateProfileImage(oAuth2UserInfo.getImageUrl()); - return userRepository.save(user); } diff --git a/src/main/java/com/shallwe/domain/auth/application/CustomTokenProviderService.java b/src/main/java/com/shallwe/domain/auth/application/CustomTokenProviderService.java index 275bfcfb..aa127d87 100644 --- a/src/main/java/com/shallwe/domain/auth/application/CustomTokenProviderService.java +++ b/src/main/java/com/shallwe/domain/auth/application/CustomTokenProviderService.java @@ -3,11 +3,10 @@ import java.security.Key; import java.util.Date; -import com.shallwe.global.config.security.OAuth2Config; +import com.shallwe.global.config.security.AuthConfig; import com.shallwe.global.config.security.token.UserPrincipal; import com.shallwe.domain.auth.dto.TokenMapping; -import com.shallwe.global.infrastructure.feign.apple.AppleClient; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -25,17 +24,16 @@ @RequiredArgsConstructor public class CustomTokenProviderService { - private final OAuth2Config oAuth2Config; + private final AuthConfig authConfig; private final CustomUserDetailsService customUserDetailsService; - private final AppleClient appleClient; public TokenMapping refreshToken(Authentication authentication, String refreshToken) { UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal(); Date now = new Date(); - Date accessTokenExpiresIn = new Date(now.getTime() + oAuth2Config.getAuth().getAccessTokenExpirationMsec()); + Date accessTokenExpiresIn = new Date(now.getTime() + authConfig.getAuth().getAccessTokenExpirationMsec()); - String secretKey = oAuth2Config.getAuth().getTokenSecret(); + String secretKey = authConfig.getAuth().getTokenSecret(); byte[] keyBytes = Decoders.BASE64.decode(secretKey); Key key = Keys.hmacShaKeyFor(keyBytes); @@ -58,10 +56,10 @@ public TokenMapping createToken(Authentication authentication) { Date now = new Date(); - Date accessTokenExpiresIn = new Date(now.getTime() + oAuth2Config.getAuth().getAccessTokenExpirationMsec()); - Date refreshTokenExpiresIn = new Date(now.getTime() + oAuth2Config.getAuth().getRefreshTokenExpirationMsec()); + Date accessTokenExpiresIn = new Date(now.getTime() + authConfig.getAuth().getAccessTokenExpirationMsec()); + Date refreshTokenExpiresIn = new Date(now.getTime() + authConfig.getAuth().getRefreshTokenExpirationMsec()); - String secretKey = oAuth2Config.getAuth().getTokenSecret(); + String secretKey = authConfig.getAuth().getTokenSecret(); byte[] keyBytes = Decoders.BASE64.decode(secretKey); Key key = Keys.hmacShaKeyFor(keyBytes); @@ -87,7 +85,7 @@ public TokenMapping createToken(Authentication authentication) { public Long getUserIdFromToken(String token) { Claims claims = Jwts.parserBuilder() - .setSigningKey(oAuth2Config.getAuth().getTokenSecret()) + .setSigningKey(authConfig.getAuth().getTokenSecret()) .build() .parseClaimsJws(token) .getBody(); @@ -110,7 +108,7 @@ public UsernamePasswordAuthenticationToken getAuthenticationByEmail(String email public Long getExpiration(String token) { // accessToken 남은 유효시간 - Date expiration = Jwts.parserBuilder().setSigningKey(oAuth2Config.getAuth().getTokenSecret()).build().parseClaimsJws(token).getBody().getExpiration(); + Date expiration = Jwts.parserBuilder().setSigningKey(authConfig.getAuth().getTokenSecret()).build().parseClaimsJws(token).getBody().getExpiration(); // 현재 시간 Long now = new Date().getTime(); //시간 계산 @@ -120,7 +118,7 @@ public Long getExpiration(String token) { public boolean validateToken(String token) { try { //log.info("bearerToken = {} \n oAuth2Config.getAuth()={}", token, oAuth2Config.getAuth().getTokenSecret()); - Jwts.parserBuilder().setSigningKey(oAuth2Config.getAuth().getTokenSecret()).build().parseClaimsJws(token); + Jwts.parserBuilder().setSigningKey(authConfig.getAuth().getTokenSecret()).build().parseClaimsJws(token); return true; } catch (io.jsonwebtoken.security.SecurityException ex) { log.error("잘못된 JWT 서명입니다."); diff --git a/src/main/java/com/shallwe/domain/auth/domain/AppleToken.java b/src/main/java/com/shallwe/domain/auth/domain/AppleToken.java new file mode 100644 index 00000000..a7c35bbc --- /dev/null +++ b/src/main/java/com/shallwe/domain/auth/domain/AppleToken.java @@ -0,0 +1,30 @@ +package com.shallwe.domain.auth.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "apple_token") +@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) +@Getter +public class AppleToken { + + @Id + @Column(name = "provider_id") + private String providerId; + + @Column(name = "refresh_token") + private String refreshToken; + + @Builder + public AppleToken(String providerId, String refreshToken) { + this.providerId = providerId; + this.refreshToken = refreshToken; + } + +} diff --git a/src/main/java/com/shallwe/domain/auth/domain/Token.java b/src/main/java/com/shallwe/domain/auth/domain/Token.java index 3cafcbd2..3c6595f9 100644 --- a/src/main/java/com/shallwe/domain/auth/domain/Token.java +++ b/src/main/java/com/shallwe/domain/auth/domain/Token.java @@ -9,16 +9,15 @@ import lombok.*; @NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor -@Builder @Entity @Getter public class Token extends BaseEntity { @Id + @Column(name = "user_email") private String userEmail; -// @Column(columnDefinition = "test") // 컬럼 타입을 "test"로 변경 + @Column(name = "refresh_token") private String refreshToken; public Token updateRefreshToken(String refreshToken) { @@ -26,4 +25,10 @@ public Token updateRefreshToken(String refreshToken) { return this; } -} + @Builder + public Token(String userEmail, String refreshToken) { + this.userEmail = userEmail; + this.refreshToken = refreshToken; + } + +} \ No newline at end of file diff --git a/src/main/java/com/shallwe/domain/auth/domain/repository/AppleTokenRepository.java b/src/main/java/com/shallwe/domain/auth/domain/repository/AppleTokenRepository.java new file mode 100644 index 00000000..2066b13e --- /dev/null +++ b/src/main/java/com/shallwe/domain/auth/domain/repository/AppleTokenRepository.java @@ -0,0 +1,7 @@ +package com.shallwe.domain.auth.domain.repository; + +import com.shallwe.domain.auth.domain.AppleToken; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AppleTokenRepository extends JpaRepository { +} diff --git a/src/main/java/com/shallwe/domain/auth/domain/repository/CustomAuthorizationRequestRepository.java b/src/main/java/com/shallwe/domain/auth/domain/repository/CustomAuthorizationRequestRepository.java index 5ffff743..604c2696 100644 --- a/src/main/java/com/shallwe/domain/auth/domain/repository/CustomAuthorizationRequestRepository.java +++ b/src/main/java/com/shallwe/domain/auth/domain/repository/CustomAuthorizationRequestRepository.java @@ -1,6 +1,6 @@ package com.shallwe.domain.auth.domain.repository; -import com.shallwe.global.config.security.util.CustomCookie; +import com.shallwe.global.utils.CustomCookie; import com.nimbusds.oauth2.sdk.util.StringUtils; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/com/shallwe/domain/auth/dto/MessageDTO.java b/src/main/java/com/shallwe/domain/auth/dto/MessageMapping.java similarity index 71% rename from src/main/java/com/shallwe/domain/auth/dto/MessageDTO.java rename to src/main/java/com/shallwe/domain/auth/dto/MessageMapping.java index c1283203..7a5539fe 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/MessageDTO.java +++ b/src/main/java/com/shallwe/domain/auth/dto/MessageMapping.java @@ -4,13 +4,13 @@ import lombok.Data; @Data -public class MessageDTO { +public class MessageMapping { private String to; private String content; @Builder - public MessageDTO(String to, String content) { + public MessageMapping(String to, String content) { this.to = to; this.content = content; } diff --git a/src/main/java/com/shallwe/domain/auth/dto/AppleSignInReq.java b/src/main/java/com/shallwe/domain/auth/dto/request/AppleSignInReq.java similarity index 60% rename from src/main/java/com/shallwe/domain/auth/dto/AppleSignInReq.java rename to src/main/java/com/shallwe/domain/auth/dto/request/AppleSignInReq.java index a1ef5593..195b5bb1 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/AppleSignInReq.java +++ b/src/main/java/com/shallwe/domain/auth/dto/request/AppleSignInReq.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.request; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -6,7 +6,7 @@ @Data public class AppleSignInReq { - @Schema(type = "string", description = "애플 로그인을 위한 IdentityToken") + @Schema(type = "String", description = "애플 로그인을 위한 IdentityToken") private String identityToken; -} +} \ No newline at end of file diff --git a/src/main/java/com/shallwe/domain/auth/dto/request/AppleSignUpReq.java b/src/main/java/com/shallwe/domain/auth/dto/request/AppleSignUpReq.java new file mode 100644 index 00000000..e503f366 --- /dev/null +++ b/src/main/java/com/shallwe/domain/auth/dto/request/AppleSignUpReq.java @@ -0,0 +1,24 @@ +package com.shallwe.domain.auth.dto.request; + + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import lombok.Data; + +@Data +public class AppleSignUpReq { + + @Schema(type = "String", description = "애플 로그인을 위한 IdentityToken") + private String identityToken; + + @Schema(type = "String", description = "애플 로그인을 위한 AuthorizationCode") + private String authorizationCode; + + @Schema( type = "string", example = "string", description="사용자 이름 입니다.") + private String name; + + @Schema( type = "string", example = "string@aa.bb", description="계정 이메일 입니다.") + @Email + private String email; + +} \ No newline at end of file diff --git a/src/main/java/com/shallwe/domain/auth/dto/request/AppleTokenReq.java b/src/main/java/com/shallwe/domain/auth/dto/request/AppleTokenReq.java new file mode 100644 index 00000000..9379c19a --- /dev/null +++ b/src/main/java/com/shallwe/domain/auth/dto/request/AppleTokenReq.java @@ -0,0 +1,36 @@ +package com.shallwe.domain.auth.dto.request; + +import lombok.Data; + +public class AppleTokenReq { + + @Data + public static class Request { + private String code; + private String client_id; + private String client_secret; + private String grant_type; + private String refresh_token; + + public static Request of(String code, String clientId, String clientSecret, String grantType, String refreshToken) { + Request request = new Request(); + request.code = code; + request.client_id = clientId; + request.client_secret = clientSecret; + request.grant_type = grantType; + request.refresh_token = refreshToken; + return request; + } + } + + @Data + public static class Response { + private String access_token; + private String expires_in; + private String id_token; + private String refresh_token; + private String token_type; + private String error; + } + +} \ No newline at end of file diff --git a/src/main/java/com/shallwe/domain/auth/dto/NaverCloudSmsReq.java b/src/main/java/com/shallwe/domain/auth/dto/request/NaverCloudSmsReq.java similarity index 71% rename from src/main/java/com/shallwe/domain/auth/dto/NaverCloudSmsReq.java rename to src/main/java/com/shallwe/domain/auth/dto/request/NaverCloudSmsReq.java index 93c22d4f..98b91a07 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/NaverCloudSmsReq.java +++ b/src/main/java/com/shallwe/domain/auth/dto/request/NaverCloudSmsReq.java @@ -1,5 +1,6 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.request; +import com.shallwe.domain.auth.dto.MessageMapping; import lombok.Builder; import lombok.Data; @@ -13,10 +14,10 @@ public class NaverCloudSmsReq { private String countryCode; private String from; private String content; - List messages; + List messages; @Builder - public NaverCloudSmsReq(String type, String contentType, String countryCode, String from, String content, List messages) { + public NaverCloudSmsReq(String type, String contentType, String countryCode, String from, String content, List messages) { this.type = type; this.contentType = contentType; this.countryCode = countryCode; diff --git a/src/main/java/com/shallwe/domain/auth/dto/RefreshTokenReq.java b/src/main/java/com/shallwe/domain/auth/dto/request/RefreshTokenReq.java similarity index 90% rename from src/main/java/com/shallwe/domain/auth/dto/RefreshTokenReq.java rename to src/main/java/com/shallwe/domain/auth/dto/request/RefreshTokenReq.java index b11b6044..8277dbbf 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/RefreshTokenReq.java +++ b/src/main/java/com/shallwe/domain/auth/dto/request/RefreshTokenReq.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.request; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/src/main/java/com/shallwe/domain/auth/dto/ShopOwnerChangePasswordReq.java b/src/main/java/com/shallwe/domain/auth/dto/request/ShopOwnerChangePasswordReq.java similarity index 90% rename from src/main/java/com/shallwe/domain/auth/dto/ShopOwnerChangePasswordReq.java rename to src/main/java/com/shallwe/domain/auth/dto/request/ShopOwnerChangePasswordReq.java index 45343fbb..fb93aab5 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/ShopOwnerChangePasswordReq.java +++ b/src/main/java/com/shallwe/domain/auth/dto/request/ShopOwnerChangePasswordReq.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.request; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/src/main/java/com/shallwe/domain/auth/dto/ShopOwnerSignInReq.java b/src/main/java/com/shallwe/domain/auth/dto/request/ShopOwnerSignInReq.java similarity index 89% rename from src/main/java/com/shallwe/domain/auth/dto/ShopOwnerSignInReq.java rename to src/main/java/com/shallwe/domain/auth/dto/request/ShopOwnerSignInReq.java index c61ffebe..2e9c67e7 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/ShopOwnerSignInReq.java +++ b/src/main/java/com/shallwe/domain/auth/dto/request/ShopOwnerSignInReq.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.request; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/src/main/java/com/shallwe/domain/auth/dto/ShopOwnerSignUpReq.java b/src/main/java/com/shallwe/domain/auth/dto/request/ShopOwnerSignUpReq.java similarity index 93% rename from src/main/java/com/shallwe/domain/auth/dto/ShopOwnerSignUpReq.java rename to src/main/java/com/shallwe/domain/auth/dto/request/ShopOwnerSignUpReq.java index 15d34a8c..935f7117 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/ShopOwnerSignUpReq.java +++ b/src/main/java/com/shallwe/domain/auth/dto/request/ShopOwnerSignUpReq.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.request; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/src/main/java/com/shallwe/domain/auth/dto/SignInReq.java b/src/main/java/com/shallwe/domain/auth/dto/request/SignInReq.java similarity index 90% rename from src/main/java/com/shallwe/domain/auth/dto/SignInReq.java rename to src/main/java/com/shallwe/domain/auth/dto/request/SignInReq.java index 337d12bc..4fbebb72 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/SignInReq.java +++ b/src/main/java/com/shallwe/domain/auth/dto/request/SignInReq.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.request; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Email; diff --git a/src/main/java/com/shallwe/domain/auth/dto/SignUpReq.java b/src/main/java/com/shallwe/domain/auth/dto/request/SignUpReq.java similarity index 56% rename from src/main/java/com/shallwe/domain/auth/dto/SignUpReq.java rename to src/main/java/com/shallwe/domain/auth/dto/request/SignUpReq.java index a14d847c..0713ba86 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/SignUpReq.java +++ b/src/main/java/com/shallwe/domain/auth/dto/request/SignUpReq.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.request; import com.shallwe.domain.user.domain.Provider; import io.swagger.v3.oas.annotations.media.Schema; @@ -11,17 +11,14 @@ public class SignUpReq { @Schema( type = "string", example = "123123", description="카카오/애플 고유 유저 ID 입니다.") private String providerId; - @Schema( type = "string", example = "KAKAO / APPLE / GOOGLE ", description="카카오/애플/구글 로그인 제공자 입니다.") + @Schema( type = "string", example = "KAKAO / GOOGLE ", description="카카오/애플/구글 로그인 제공자 입니다.") private Provider provider; - @Schema( type = "string", example = "string", description="카카오톡 닉네임 입니다.") - private String nickname; + @Schema( type = "string", example = "string", description="사용자 이름 입니다.") + private String name; @Schema( type = "string", example = "string@aa.bb", description="계정 이메일 입니다.") @Email private String email; - @Schema( type = "string", example = "http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_640x640.jpg", description="프로필 사진 URL 입니다.") - private String profileImgUrl; - } diff --git a/src/main/java/com/shallwe/domain/auth/dto/ValidVerificationCodeReq.java b/src/main/java/com/shallwe/domain/auth/dto/request/ValidVerificationCodeReq.java similarity index 75% rename from src/main/java/com/shallwe/domain/auth/dto/ValidVerificationCodeReq.java rename to src/main/java/com/shallwe/domain/auth/dto/request/ValidVerificationCodeReq.java index f17276d5..88a64dad 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/ValidVerificationCodeReq.java +++ b/src/main/java/com/shallwe/domain/auth/dto/request/ValidVerificationCodeReq.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.request; import lombok.Data; diff --git a/src/main/java/com/shallwe/global/infrastructure/feign/apple/dto/ApplePublicKeyRes.java b/src/main/java/com/shallwe/domain/auth/dto/response/ApplePublicKeyRes.java similarity index 90% rename from src/main/java/com/shallwe/global/infrastructure/feign/apple/dto/ApplePublicKeyRes.java rename to src/main/java/com/shallwe/domain/auth/dto/response/ApplePublicKeyRes.java index 1344036a..e3996724 100644 --- a/src/main/java/com/shallwe/global/infrastructure/feign/apple/dto/ApplePublicKeyRes.java +++ b/src/main/java/com/shallwe/domain/auth/dto/response/ApplePublicKeyRes.java @@ -1,4 +1,4 @@ -package com.shallwe.global.infrastructure.feign.apple.dto; +package com.shallwe.domain.auth.dto.response; import lombok.Data; diff --git a/src/main/java/com/shallwe/domain/auth/dto/AppleSignInRes.java b/src/main/java/com/shallwe/domain/auth/dto/response/AppleSignInRes.java similarity index 68% rename from src/main/java/com/shallwe/domain/auth/dto/AppleSignInRes.java rename to src/main/java/com/shallwe/domain/auth/dto/response/AppleSignInRes.java index 89f5d94d..78501615 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/AppleSignInRes.java +++ b/src/main/java/com/shallwe/domain/auth/dto/response/AppleSignInRes.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.response; import lombok.Data; diff --git a/src/main/java/com/shallwe/domain/auth/dto/AuthRes.java b/src/main/java/com/shallwe/domain/auth/dto/response/AuthRes.java similarity index 95% rename from src/main/java/com/shallwe/domain/auth/dto/AuthRes.java rename to src/main/java/com/shallwe/domain/auth/dto/response/AuthRes.java index 199f02c1..c19add92 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/AuthRes.java +++ b/src/main/java/com/shallwe/domain/auth/dto/response/AuthRes.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.response; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; diff --git a/src/main/java/com/shallwe/domain/auth/dto/NaverCloudSmsRes.java b/src/main/java/com/shallwe/domain/auth/dto/response/NaverCloudSmsRes.java similarity index 83% rename from src/main/java/com/shallwe/domain/auth/dto/NaverCloudSmsRes.java rename to src/main/java/com/shallwe/domain/auth/dto/response/NaverCloudSmsRes.java index bfa4fad8..2575c6af 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/NaverCloudSmsRes.java +++ b/src/main/java/com/shallwe/domain/auth/dto/response/NaverCloudSmsRes.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.response; import lombok.Data; diff --git a/src/main/java/com/shallwe/domain/auth/dto/SmsResponseDto.java b/src/main/java/com/shallwe/domain/auth/dto/response/SmsResponseDto.java similarity index 83% rename from src/main/java/com/shallwe/domain/auth/dto/SmsResponseDto.java rename to src/main/java/com/shallwe/domain/auth/dto/response/SmsResponseDto.java index 01e1b544..53450681 100644 --- a/src/main/java/com/shallwe/domain/auth/dto/SmsResponseDto.java +++ b/src/main/java/com/shallwe/domain/auth/dto/response/SmsResponseDto.java @@ -1,4 +1,4 @@ -package com.shallwe.domain.auth.dto; +package com.shallwe.domain.auth.dto.response; import lombok.Data; diff --git a/src/main/java/com/shallwe/domain/auth/exception/AlreadyExistsProviderIdException.java b/src/main/java/com/shallwe/domain/auth/exception/AlreadyExistsProviderIdException.java new file mode 100644 index 00000000..44be3b75 --- /dev/null +++ b/src/main/java/com/shallwe/domain/auth/exception/AlreadyExistsProviderIdException.java @@ -0,0 +1,9 @@ +package com.shallwe.domain.auth.exception; + +public class AlreadyExistsProviderIdException extends RuntimeException { + + public AlreadyExistsProviderIdException() { + super("이미 존재하는 providerId 입니다."); + } + +} diff --git a/src/main/java/com/shallwe/domain/auth/presentation/AuthController.java b/src/main/java/com/shallwe/domain/auth/presentation/AuthController.java index 89a72023..5cc6ee3c 100644 --- a/src/main/java/com/shallwe/domain/auth/presentation/AuthController.java +++ b/src/main/java/com/shallwe/domain/auth/presentation/AuthController.java @@ -1,13 +1,14 @@ package com.shallwe.domain.auth.presentation; -import com.shallwe.domain.auth.dto.ShopOwnerChangePasswordReq; +import com.shallwe.domain.auth.dto.request.*; +import com.shallwe.domain.auth.dto.response.AuthRes; +import com.shallwe.domain.auth.dto.response.SmsResponseDto; import com.shallwe.global.infrastructure.sms.NaverSmsClient; import com.shallwe.global.infrastructure.sms.dto.NaverVerifySmsReq; import com.shallwe.global.payload.ResponseCustom; import jakarta.validation.Valid; -import com.shallwe.domain.auth.dto.*; import com.shallwe.global.payload.ErrorResponse; import com.shallwe.global.config.security.token.CurrentUser; import com.shallwe.global.config.security.token.UserPrincipal; @@ -47,6 +48,18 @@ public ResponseCustom signUp( return ResponseCustom.OK(authService.signUp(signUpReq)); } + @Operation(summary = "애플 유저 회원가입", description = "애플 유저 회원가입을 수행합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "애플 회원가입 성공", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = AuthRes.class) ) } ), + @ApiResponse(responseCode = "400", description = "애플 회원가입 실패", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class) ) } ), + }) + @PostMapping(value="/sign-up/apple") + public ResponseCustom signUp( + @Parameter(description = "AppleSignUpReq Schema를 확인해주세요.", required = true) @RequestBody AppleSignUpReq appleSignUpReq + ) { + return ResponseCustom.OK(authService.appleSignUp(appleSignUpReq)); + } + @Operation(summary = "유저 로그인", description = "유저 로그인을 수행합니다.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "로그인 성공", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = AuthRes.class) ) } ), diff --git a/src/main/java/com/shallwe/domain/reservation/domain/repository/ReservationQuerydslRepositoryImpl.java b/src/main/java/com/shallwe/domain/reservation/domain/repository/ReservationQuerydslRepositoryImpl.java index aae90eb3..29e83395 100644 --- a/src/main/java/com/shallwe/domain/reservation/domain/repository/ReservationQuerydslRepositoryImpl.java +++ b/src/main/java/com/shallwe/domain/reservation/domain/repository/ReservationQuerydslRepositoryImpl.java @@ -42,7 +42,6 @@ public List findReservationsByPhoneNumberAndReservationSta user.age, user.phoneNumber, user.email, - user.profileImgUrl, user.gender, user.status ), @@ -79,7 +78,6 @@ public List findReservationsBySenderAndReservationStatusIn(Us user.age, user.phoneNumber, user.email, - user.profileImgUrl, user.gender, user.status ), diff --git a/src/main/java/com/shallwe/domain/user/domain/User.java b/src/main/java/com/shallwe/domain/user/domain/User.java index 2cc38808..9a1b8204 100644 --- a/src/main/java/com/shallwe/domain/user/domain/User.java +++ b/src/main/java/com/shallwe/domain/user/domain/User.java @@ -31,8 +31,6 @@ public class User extends BaseEntity { private String password; - private String profileImgUrl; - @Enumerated(EnumType.STRING) private Gender gender; @@ -50,10 +48,6 @@ public void updateName(String name){ this.name = name; } - public void updateProfileImage(String imageUrl){ - this.profileImgUrl = imageUrl; - } - public void updateMarketingConsent(boolean status) { this.marketingConsent = status; } @@ -71,14 +65,13 @@ public void updatePhoneNumber(String phoneNumber) { } @Builder - public User(String name, String birthDay, Integer age, String phoneNumber, String email, String password, String profileImgUrl, Gender gender, Boolean marketingConsent, Provider provider, String providerId, Role role) { + public User(String name, String birthDay, Integer age, String phoneNumber, String email, String password, Gender gender, Boolean marketingConsent, Provider provider, String providerId, Role role) { this.name = name; this.birthDay = birthDay; this.age = age; this.phoneNumber = phoneNumber; this.email = email; this.password = password; - this.profileImgUrl = profileImgUrl; this.gender = gender; this.marketingConsent = marketingConsent; this.provider = provider; diff --git a/src/main/java/com/shallwe/domain/user/domain/repository/UserRepository.java b/src/main/java/com/shallwe/domain/user/domain/repository/UserRepository.java index 9a433dc0..797eb64d 100644 --- a/src/main/java/com/shallwe/domain/user/domain/repository/UserRepository.java +++ b/src/main/java/com/shallwe/domain/user/domain/repository/UserRepository.java @@ -15,6 +15,8 @@ public interface UserRepository extends JpaRepository { Boolean existsByEmailAndStatus(String email, Status status); + Boolean existsByProviderId(String providerId); + Optional findByPhoneNumberAndStatus(String phoneNumber, Status status); Optional findByEmailAndStatus(String email, Status status); diff --git a/src/main/java/com/shallwe/domain/user/dto/UserDetailRes.java b/src/main/java/com/shallwe/domain/user/dto/UserDetailRes.java index 88f6f788..db7a5f68 100644 --- a/src/main/java/com/shallwe/domain/user/dto/UserDetailRes.java +++ b/src/main/java/com/shallwe/domain/user/dto/UserDetailRes.java @@ -28,7 +28,6 @@ public static UserDetailRes toDto(User user) { .age(user.getAge()) .email(user.getEmail()) .phoneNumber(user.getPhoneNumber()) - .profileImgUrl(user.getProfileImgUrl()) .gender(user.getGender()) .status(user.getStatus()) .build(); @@ -36,14 +35,13 @@ public static UserDetailRes toDto(User user) { @Builder @QueryProjection - public UserDetailRes(Long id, String name, String birthDay, Integer age, String phoneNumber, String email, String profileImgUrl, Gender gender, Status status) { + public UserDetailRes(Long id, String name, String birthDay, Integer age, String phoneNumber, String email, Gender gender, Status status) { this.id = id; this.name = name; this.birthDay = birthDay; this.age = age; this.phoneNumber = phoneNumber; this.email = email; - this.profileImgUrl = profileImgUrl; this.gender = gender; this.status = status; } diff --git a/src/main/java/com/shallwe/global/config/FeignConfig.java b/src/main/java/com/shallwe/global/config/FeignConfig.java deleted file mode 100644 index 016e2021..00000000 --- a/src/main/java/com/shallwe/global/config/FeignConfig.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.shallwe.global.config; - -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.context.annotation.Configuration; - -@Configuration -@EnableFeignClients(basePackages = "com.shallwe.global.infrastructure.feign") -public class FeignConfig { -} diff --git a/src/main/java/com/shallwe/global/config/resttemplate/RestTemplateClientHttpRequestInterceptor.java b/src/main/java/com/shallwe/global/config/resttemplate/RestTemplateClientHttpRequestInterceptor.java deleted file mode 100644 index 16744d8f..00000000 --- a/src/main/java/com/shallwe/global/config/resttemplate/RestTemplateClientHttpRequestInterceptor.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.shallwe.global.config.resttemplate; - -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.http.client.ClientHttpResponse; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; - -@Slf4j -public class RestTemplateClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { - - @NonNull - @Override - public ClientHttpResponse intercept(@NonNull final HttpRequest request, - final byte @NonNull [] body, @NonNull ClientHttpRequestExecution execution) - throws IOException { - final ClientHttpResponse response = execution.execute(request, body); - - loggingResponse(response); - loggingRequest(request, body); - return execution.execute(request, body); - } - - private void loggingRequest(final HttpRequest request, byte[] body) { - log.info("===========================request begin================================================"); - log.info("Headers : {}", request.getHeaders()); - log.info("Request Method : {}", request.getMethod()); - log.info("Request URI : {}", request.getURI()); - log.info("Request Body : {}", - body.length == 0 ? null : new String(body, StandardCharsets.UTF_8)); - log.info("==========================request end================================================"); - } - - private void loggingResponse(ClientHttpResponse response) throws IOException { - final String body = getBody(response); - - log.info("============================response begin=========================================="); - log.info("Headers : {}", response.getHeaders()); - log.info("Status code : {}", response.getStatusCode()); - log.info("Response Body : {}", body); - log.info("============================response end============================================"); - } - - private String getBody(@NonNull final ClientHttpResponse response) throws IOException { - try(BufferedReader br = new BufferedReader(new InputStreamReader(response.getBody()))) { - return br.readLine(); - } - } - -} diff --git a/src/main/java/com/shallwe/global/config/resttemplate/RestTemplateConfig.java b/src/main/java/com/shallwe/global/config/resttemplate/RestTemplateConfig.java deleted file mode 100644 index 621cc561..00000000 --- a/src/main/java/com/shallwe/global/config/resttemplate/RestTemplateConfig.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.shallwe.global.config.resttemplate; - -import lombok.RequiredArgsConstructor; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; - -import java.time.Duration; - -@Configuration -@RequiredArgsConstructor -public class RestTemplateConfig { - - private final RestTemplateBuilder restTemplateBuilder; - - @Bean - public RestTemplate naverSmsTemplate() { - return restTemplateBuilder - .rootUri("https://sens.apigw.ntruss.com/sms/v2/services") - .additionalInterceptors(new RestTemplateClientHttpRequestInterceptor()) - .errorHandler(new RestTemplateErrorHandler()) - .setConnectTimeout(Duration.ofMinutes(3)) - .build(); - } - -} diff --git a/src/main/java/com/shallwe/global/config/resttemplate/RestTemplateErrorHandler.java b/src/main/java/com/shallwe/global/config/resttemplate/RestTemplateErrorHandler.java deleted file mode 100644 index 2c07e5ad..00000000 --- a/src/main/java/com/shallwe/global/config/resttemplate/RestTemplateErrorHandler.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.shallwe.global.config.resttemplate; - -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.web.client.ResponseErrorHandler; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -@Slf4j -public class RestTemplateErrorHandler implements ResponseErrorHandler { - - @Override - public boolean hasError(@NonNull final ClientHttpResponse response) throws IOException { - final HttpStatus statusCode = (HttpStatus) response.getStatusCode(); - - return !statusCode.is2xxSuccessful(); - } - - @Override - public void handleError(@NonNull final ClientHttpResponse response) throws IOException { - final String error = getErrorAsString(response); - - log.error("==========error response begin=========="); - log.error("Headers : {}", response.getHeaders()); - log.error("Response Status : {}", response.getStatusCode()); - log.error("Request Body : {}", error); - log.error("==========error response end=========="); - } - - private String getErrorAsString(@NonNull final ClientHttpResponse response) throws IOException { - try(BufferedReader br = new BufferedReader(new InputStreamReader(response.getBody()))) { - return br.readLine(); - } - } - -} diff --git a/src/main/java/com/shallwe/global/config/security/OAuth2Config.java b/src/main/java/com/shallwe/global/config/security/AuthConfig.java similarity index 77% rename from src/main/java/com/shallwe/global/config/security/OAuth2Config.java rename to src/main/java/com/shallwe/global/config/security/AuthConfig.java index e90dec27..d53f951e 100644 --- a/src/main/java/com/shallwe/global/config/security/OAuth2Config.java +++ b/src/main/java/com/shallwe/global/config/security/AuthConfig.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; +import lombok.Getter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @@ -10,9 +11,11 @@ @Configuration @ConfigurationProperties(prefix = "app") -public class OAuth2Config { +@Getter +public class AuthConfig { private final Auth auth = new Auth(); private final OAuth2 oauth2 = new OAuth2(); + private final AppleAuth appleAuth = new AppleAuth(); @Data public static class Auth { @@ -21,6 +24,15 @@ public static class Auth { private long refreshTokenExpirationMsec; } + @Data + public static class AppleAuth { + private String clientId; + private String teamId; + private String keyId; + private String privateKey; + } + + @Getter public static final class OAuth2 { private List authorizedRedirectUris = new ArrayList<>(); @@ -34,11 +46,4 @@ public OAuth2 authorizedRedirectUris(List authorizedRedirectUris) { } } - public Auth getAuth() { - return auth; - } - - public OAuth2 getOauth2() { - return oauth2; - } } diff --git a/src/main/java/com/shallwe/global/config/security/handler/CustomSimpleUrlAuthenticationFailureHandler.java b/src/main/java/com/shallwe/global/config/security/handler/CustomSimpleUrlAuthenticationFailureHandler.java index cda2f9e0..7968e54d 100644 --- a/src/main/java/com/shallwe/global/config/security/handler/CustomSimpleUrlAuthenticationFailureHandler.java +++ b/src/main/java/com/shallwe/global/config/security/handler/CustomSimpleUrlAuthenticationFailureHandler.java @@ -16,7 +16,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import com.shallwe.global.config.security.util.CustomCookie; +import com.shallwe.global.utils.CustomCookie; import com.shallwe.domain.auth.domain.repository.CustomAuthorizationRequestRepository; @RequiredArgsConstructor diff --git a/src/main/java/com/shallwe/global/config/security/handler/CustomSimpleUrlAuthenticationSuccessHandler.java b/src/main/java/com/shallwe/global/config/security/handler/CustomSimpleUrlAuthenticationSuccessHandler.java index 1a82ae71..49ebe19e 100644 --- a/src/main/java/com/shallwe/global/config/security/handler/CustomSimpleUrlAuthenticationSuccessHandler.java +++ b/src/main/java/com/shallwe/global/config/security/handler/CustomSimpleUrlAuthenticationSuccessHandler.java @@ -1,8 +1,8 @@ package com.shallwe.global.config.security.handler; import com.shallwe.global.DefaultAssert; -import com.shallwe.global.config.security.OAuth2Config; -import com.shallwe.global.config.security.util.CustomCookie; +import com.shallwe.global.config.security.AuthConfig; +import com.shallwe.global.utils.CustomCookie; import com.shallwe.domain.auth.domain.Token; import com.shallwe.domain.auth.dto.TokenMapping; import com.shallwe.domain.auth.domain.repository.CustomAuthorizationRequestRepository; @@ -22,7 +22,6 @@ import java.net.URI; import java.util.Optional; -import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -32,7 +31,7 @@ public class CustomSimpleUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{ private final CustomTokenProviderService customTokenProviderService; - private final OAuth2Config oAuth2Config; + private final AuthConfig authConfig; private final TokenRepository tokenRepository; private final CustomAuthorizationRequestRepository customAuthorizationRequestRepository; @@ -43,8 +42,8 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo String targetUrl = determineTargetUrl(request, response, authentication); TokenMapping token = customTokenProviderService.createToken(authentication); - CustomCookie.addCookie(response, "Authorization", "Bearer_" + token.getAccessToken(), (int) oAuth2Config.getAuth().getAccessTokenExpirationMsec()); - CustomCookie.addCookie(response, "Refresh_Token", "Bearer_" + token.getRefreshToken(), (int) oAuth2Config.getAuth().getRefreshTokenExpirationMsec()); + CustomCookie.addCookie(response, "Authorization", "Bearer_" + token.getAccessToken(), (int) authConfig.getAuth().getAccessTokenExpirationMsec()); + CustomCookie.addCookie(response, "Refresh_Token", "Bearer_" + token.getRefreshToken(), (int) authConfig.getAuth().getRefreshTokenExpirationMsec()); clearAuthenticationAttributes(request, response); getRedirectStrategy().sendRedirect(request, response, targetUrl); @@ -77,7 +76,7 @@ protected void clearAuthenticationAttributes(HttpServletRequest request, HttpSer private boolean isAuthorizedRedirectUri(String uri) { URI clientRedirectUri = URI.create(uri); - return oAuth2Config.getOauth2().getAuthorizedRedirectUris() + return authConfig.getOauth2().getAuthorizedRedirectUris() .stream() .anyMatch(authorizedRedirectUri -> { URI authorizedURI = URI.create(authorizedRedirectUri); diff --git a/src/main/java/com/shallwe/global/infrastructure/feign/apple/AppleClient.java b/src/main/java/com/shallwe/global/infrastructure/feign/apple/AppleClient.java deleted file mode 100644 index 55cb67ff..00000000 --- a/src/main/java/com/shallwe/global/infrastructure/feign/apple/AppleClient.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.shallwe.global.infrastructure.feign.apple; - -import com.shallwe.global.config.FeignConfig; -import com.shallwe.global.infrastructure.feign.apple.dto.ApplePublicKeyRes; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; - -@FeignClient(name = "appleClient", url = "https://appleid.apple.com/auth", configuration = FeignConfig.class) -public interface AppleClient { - - @GetMapping(value = "/keys") - ApplePublicKeyRes getAppleAuthPublicKey(); - -} diff --git a/src/main/java/com/shallwe/global/infrastructure/sms/NaverSmsClient.java b/src/main/java/com/shallwe/global/infrastructure/sms/NaverSmsClient.java index 24f1bfb4..b8902bcb 100644 --- a/src/main/java/com/shallwe/global/infrastructure/sms/NaverSmsClient.java +++ b/src/main/java/com/shallwe/global/infrastructure/sms/NaverSmsClient.java @@ -3,28 +3,24 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.shallwe.domain.auth.domain.VerificationCode; import com.shallwe.domain.auth.domain.repository.VerificationCodeRepository; -import com.shallwe.domain.auth.dto.MessageDTO; -import com.shallwe.domain.auth.dto.NaverCloudSmsReq; -import com.shallwe.domain.auth.dto.ValidVerificationCodeReq; +import com.shallwe.domain.auth.dto.MessageMapping; +import com.shallwe.domain.auth.dto.request.NaverCloudSmsReq; +import com.shallwe.domain.auth.dto.request.ValidVerificationCodeReq; import com.shallwe.global.infrastructure.sms.exception.InvalidPhoneNumberException; import com.shallwe.global.infrastructure.sms.exception.InvalidVerificationCodeException; import com.shallwe.global.infrastructure.sms.exception.TimeOutException; -import com.shallwe.domain.auth.dto.SmsResponseDto; +import com.shallwe.domain.auth.dto.response.SmsResponseDto; import com.shallwe.global.payload.Message; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.PropertySource; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.util.ArrayList; @@ -49,7 +45,6 @@ public class NaverSmsClient implements SmsClient { @Value("${sms.naver-cloud.sender-phone}") private String PHONE_NUMBER; - private final RestTemplate naverSmsTemplate; private final VerificationCodeRepository verificationCodeRepository; @Override @@ -58,27 +53,21 @@ public SmsResponseDto send(String receivePhoneNumber) throws Exception { String timestamp = String.valueOf(System.currentTimeMillis()); String signature = makeSignature(timestamp); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("x-ncp-apigw-timestamp", timestamp); - headers.set("x-ncp-iam-access-key", ACCESS_KEY); - headers.set("x-ncp-apigw-signature-v2", signature); - String code = generateRandomCode(); - MessageDTO messageDTO = MessageDTO.builder() + MessageMapping messageMapping = MessageMapping.builder() .to(receivePhoneNumber) .content("[Shall We] 인증번호 [" + code + "]를 입력해주세요.").build(); - List messages = new ArrayList<>(); - messages.add(messageDTO); + List messages = new ArrayList<>(); + messages.add(messageMapping); NaverCloudSmsReq naverCloudSmsReq = NaverCloudSmsReq.builder() .type("SMS") .contentType("COMM") .countryCode("82") .from(PHONE_NUMBER) - .content(messageDTO.getContent()) + .content(messageMapping.getContent()) .messages(messages) .build(); @@ -92,10 +81,20 @@ public SmsResponseDto send(String receivePhoneNumber) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); String body = objectMapper.writeValueAsString(naverCloudSmsReq); - HttpEntity httpBody = new HttpEntity<>(body, headers); - naverSmsTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory()); - return naverSmsTemplate.postForEntity("/" + SERVICE_ID + "/messages", httpBody, SmsResponseDto.class).getBody(); + RestClient restClient = RestClient.builder() + .requestFactory(new HttpComponentsClientHttpRequestFactory()) + .baseUrl("https://sens.apigw.ntruss.com/sms/v2/services") + .build(); + + return restClient.post().uri("/" + SERVICE_ID + "/messages") + .contentType(MediaType.APPLICATION_JSON) + .header("x-ncp-apigw-timestamp", timestamp) + .header("x-ncp-iam-access-key", ACCESS_KEY) + .header("x-ncp-apigw-signature-v2", signature) + .body(body) + .retrieve() + .body(SmsResponseDto.class); } @Transactional diff --git a/src/main/java/com/shallwe/global/infrastructure/sms/SmsClient.java b/src/main/java/com/shallwe/global/infrastructure/sms/SmsClient.java index 4a46a2c8..c6524ec9 100644 --- a/src/main/java/com/shallwe/global/infrastructure/sms/SmsClient.java +++ b/src/main/java/com/shallwe/global/infrastructure/sms/SmsClient.java @@ -1,7 +1,6 @@ package com.shallwe.global.infrastructure.sms; -import com.shallwe.domain.auth.dto.SmsResponseDto; -import com.shallwe.global.infrastructure.sms.dto.NaverVerifySmsReq; +import com.shallwe.domain.auth.dto.response.SmsResponseDto; public interface SmsClient { diff --git a/src/main/java/com/shallwe/global/utils/AppleJwtUtils.java b/src/main/java/com/shallwe/global/utils/AppleJwtUtils.java index e5ff278f..82ee7c14 100644 --- a/src/main/java/com/shallwe/global/utils/AppleJwtUtils.java +++ b/src/main/java/com/shallwe/global/utils/AppleJwtUtils.java @@ -1,34 +1,51 @@ package com.shallwe.global.utils; import com.fasterxml.jackson.databind.ObjectMapper; -import com.shallwe.global.infrastructure.feign.apple.AppleClient; -import com.shallwe.global.infrastructure.feign.apple.dto.ApplePublicKeyRes; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.MalformedJwtException; +import com.shallwe.domain.auth.dto.request.AppleTokenReq; +import com.shallwe.global.config.security.AuthConfig; +import com.shallwe.domain.auth.dto.response.ApplePublicKeyRes; +import io.jsonwebtoken.*; import io.jsonwebtoken.security.SignatureException; import lombok.RequiredArgsConstructor; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.springframework.http.MediaType; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClient; import java.math.BigInteger; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; +import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPublicKeySpec; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Base64; +import java.util.Date; import java.util.Map; +import static com.shallwe.global.config.security.AuthConfig.*; + @Component @RequiredArgsConstructor public class AppleJwtUtils { - private final AppleClient appleClient; + private final AuthConfig authConfig; public Claims getClaimsBy(String identityToken) { try { - ApplePublicKeyRes response = appleClient.getAppleAuthPublicKey(); + RestClient restClient = RestClient.builder() + .requestFactory(new HttpComponentsClientHttpRequestFactory()) + .baseUrl("https://appleid.apple.com/auth") + .build(); + + ApplePublicKeyRes response = restClient.get() + .uri("/keys") + .retrieve() + .body(ApplePublicKeyRes.class); + String headerOfIdentityToken = identityToken.substring(0, identityToken.indexOf(".")); Map header = new ObjectMapper().readValue(new String(Base64.getDecoder().decode(headerOfIdentityToken), "UTF-8"), Map.class); @@ -44,7 +61,7 @@ public Claims getClaimsBy(String identityToken) { KeyFactory keyFactory = KeyFactory.getInstance(key.getKty()); PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); - return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(identityToken).getBody(); + return Jwts.parserBuilder().setSigningKey(publicKey).build().parseClaimsJws(identityToken).getBody(); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException("Not found algorithm"); } catch (InvalidKeySpecException e) { @@ -60,4 +77,63 @@ public Claims getClaimsBy(String identityToken) { } } + public String getAppleToken(String code) { + RestClient restClient = RestClient.builder() + .baseUrl("https://appleid.apple.com/auth") + .requestFactory(new HttpComponentsClientHttpRequestFactory()) + .build(); + + AppleTokenReq.Request request = AppleTokenReq.Request.of( + code, + authConfig.getAppleAuth().getClientId(), + makeClientSecret(), + "authorization_code", + null + ); + + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("client_id", request.getClient_id()); + map.add("client_secret", request.getClient_secret()); + map.add("code", request.getCode()); + map.add("grant_type", request.getGrant_type()); + map.add("refresh_token", request.getRefresh_token()); + + AppleTokenReq.Response response = restClient.post() + .uri("/token") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .body(map) + .retrieve() + .body(AppleTokenReq.Response.class); + + return response.getRefresh_token(); + } + + public String makeClientSecret() { + AppleAuth appleAuth = authConfig.getAppleAuth(); + Date expirationDate = Date.from(LocalDateTime.now().plusDays(30).atZone(ZoneId.systemDefault()).toInstant()); + return Jwts.builder() + .setHeaderParam("kid", appleAuth.getKeyId()) + .setHeaderParam("alg", "ES256") + .setIssuer(appleAuth.getTeamId()) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(expirationDate) + .setAudience("https://appleid.apple.com") + .setSubject(appleAuth.getClientId()) + .signWith(getPrivateKey(), SignatureAlgorithm.ES256) + .compact(); + } + + private PrivateKey getPrivateKey() { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); + + try { + byte[] privateKeyBytes = Base64.getDecoder().decode(authConfig.getAppleAuth().getPrivateKey()); + PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(privateKeyBytes); + return converter.getPrivateKey(privateKeyInfo); + } catch (Exception e) { + throw new RuntimeException("Error converting private key from String", e); + } + } + } diff --git a/src/main/java/com/shallwe/global/config/security/util/CustomCookie.java b/src/main/java/com/shallwe/global/utils/CustomCookie.java similarity index 97% rename from src/main/java/com/shallwe/global/config/security/util/CustomCookie.java rename to src/main/java/com/shallwe/global/utils/CustomCookie.java index cee761e9..9063ac47 100644 --- a/src/main/java/com/shallwe/global/config/security/util/CustomCookie.java +++ b/src/main/java/com/shallwe/global/utils/CustomCookie.java @@ -1,4 +1,4 @@ -package com.shallwe.global.config.security.util; +package com.shallwe.global.utils; import java.util.Base64; import java.util.Optional;