From 9c459883a172f89028d7509cf0f362590898451a Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 27 Feb 2025 15:51:15 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/application/AuthService.java | 23 +++++++- .../auth/presentation/AuthController.java | 7 +++ .../presentation/GuestTokenInterceptor.java | 27 +++++++++ .../presentation/dto/GuestTokenResponse.java | 4 ++ .../crypto/application/CryptoService.java | 55 +++++++++++++++++++ .../user/application/UserService.java | 6 ++ .../java/com/swyp8team2/user/domain/Role.java | 5 ++ .../java/com/swyp8team2/user/domain/User.java | 17 +++++- 8 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/swyp8team2/auth/presentation/GuestTokenInterceptor.java create mode 100644 src/main/java/com/swyp8team2/auth/presentation/dto/GuestTokenResponse.java create mode 100644 src/main/java/com/swyp8team2/crypto/application/CryptoService.java create mode 100644 src/main/java/com/swyp8team2/user/domain/Role.java diff --git a/src/main/java/com/swyp8team2/auth/application/AuthService.java b/src/main/java/com/swyp8team2/auth/application/AuthService.java index 9b3898f9..f04588d3 100644 --- a/src/main/java/com/swyp8team2/auth/application/AuthService.java +++ b/src/main/java/com/swyp8team2/auth/application/AuthService.java @@ -7,19 +7,35 @@ import com.swyp8team2.auth.domain.Provider; import com.swyp8team2.auth.domain.SocialAccount; import com.swyp8team2.auth.domain.SocialAccountRepository; +import com.swyp8team2.crypto.application.CryptoService; import com.swyp8team2.user.application.UserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.security.NoSuchAlgorithmException; + @Service -@RequiredArgsConstructor public class AuthService { private final JwtService jwtService; private final OAuthService oAuthService; private final SocialAccountRepository socialAccountRepository; private final UserService userService; + private final CryptoService cryptoService; + + public AuthService( + JwtService jwtService, + OAuthService oAuthService, + SocialAccountRepository socialAccountRepository, + UserService userService + ) throws Exception { + this.jwtService = jwtService; + this.oAuthService = oAuthService; + this.socialAccountRepository = socialAccountRepository; + this.userService = userService; + this.cryptoService = new CryptoService(); + } @Transactional public TokenPair oauthSignIn(String code, String redirectUri) { @@ -39,4 +55,9 @@ private SocialAccount createUser(OAuthUserInfo oAuthUserInfo) { public TokenPair reissue(String refreshToken) { return jwtService.reissue(refreshToken); } + + public String guestLogin() { + Long guestId = userService.createGuest(); + return cryptoService.encrypt(String.valueOf(guestId)); + } } diff --git a/src/main/java/com/swyp8team2/auth/presentation/AuthController.java b/src/main/java/com/swyp8team2/auth/presentation/AuthController.java index e7e2e908..32d2c508 100644 --- a/src/main/java/com/swyp8team2/auth/presentation/AuthController.java +++ b/src/main/java/com/swyp8team2/auth/presentation/AuthController.java @@ -3,6 +3,7 @@ import com.swyp8team2.auth.application.AuthService; import com.swyp8team2.auth.application.jwt.TokenPair; +import com.swyp8team2.auth.presentation.dto.GuestTokenResponse; import com.swyp8team2.auth.presentation.dto.OAuthSignInRequest; import com.swyp8team2.auth.presentation.dto.TokenResponse; import com.swyp8team2.common.exception.BadRequestException; @@ -53,4 +54,10 @@ public ResponseEntity reissue( response.addCookie(cookie); return ResponseEntity.ok(new TokenResponse(tokenPair.accessToken())); } + + @PostMapping("/guest/login") + public ResponseEntity guestLogin() { + String guestToken = authService.guestLogin(); + return ResponseEntity.ok(new GuestTokenResponse(guestToken)); + } } diff --git a/src/main/java/com/swyp8team2/auth/presentation/GuestTokenInterceptor.java b/src/main/java/com/swyp8team2/auth/presentation/GuestTokenInterceptor.java new file mode 100644 index 00000000..6334f37e --- /dev/null +++ b/src/main/java/com/swyp8team2/auth/presentation/GuestTokenInterceptor.java @@ -0,0 +1,27 @@ +package com.swyp8team2.auth.presentation; + +import com.swyp8team2.common.presentation.CustomHeader; +import com.swyp8team2.crypto.application.CryptoService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.util.Objects; + +@Component +@RequiredArgsConstructor +public class GuestTokenInterceptor implements HandlerInterceptor { + + private final CryptoService cryptoService; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { +// String token = request.getHeader(CustomHeader.GUEST_ID); +// if (Objects.isNull(token)) { +// return true; +// } + return true; + } +} diff --git a/src/main/java/com/swyp8team2/auth/presentation/dto/GuestTokenResponse.java b/src/main/java/com/swyp8team2/auth/presentation/dto/GuestTokenResponse.java new file mode 100644 index 00000000..ee76d2f6 --- /dev/null +++ b/src/main/java/com/swyp8team2/auth/presentation/dto/GuestTokenResponse.java @@ -0,0 +1,4 @@ +package com.swyp8team2.auth.presentation.dto; + +public record GuestTokenResponse(String guestToken) { +} diff --git a/src/main/java/com/swyp8team2/crypto/application/CryptoService.java b/src/main/java/com/swyp8team2/crypto/application/CryptoService.java new file mode 100644 index 00000000..25fddd7b --- /dev/null +++ b/src/main/java/com/swyp8team2/crypto/application/CryptoService.java @@ -0,0 +1,55 @@ +package com.swyp8team2.crypto.application; + +import com.swyp8team2.common.exception.BadRequestException; +import com.swyp8team2.common.exception.ErrorCode; +import com.swyp8team2.common.exception.InternalServerException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import java.util.Base64; + +@Slf4j +@Service +public class CryptoService { + + private static final String ALGORITHM = "AES"; + private final SecretKey secretKey; + + public CryptoService() throws Exception { + KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM); + keyGenerator.init(256); + this.secretKey = keyGenerator.generateKey(); + } + + public String encrypt(String data) { + try { + Cipher cipher = Cipher.getInstance(ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + byte[] encryptedBytes = cipher.doFinal(data.getBytes()); + return Base64.getEncoder().encodeToString(encryptedBytes); + } catch (Exception e) { + log.error("encrypt error {}", e.getMessage()); + throw new InternalServerException(ErrorCode.INTERNAL_SERVER_ERROR); + } + } + + public String decrypt(String encryptedData) { + try { + Cipher cipher = Cipher.getInstance(ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData)); + return new String(decryptedBytes); + } catch (IllegalBlockSizeException | BadPaddingException e) { + log.debug("decrypt error {}", e.getMessage()); + throw new BadRequestException(ErrorCode.INVALID_TOKEN); + } catch (Exception e) { + log.error("decrypt error {}", e.getMessage()); + throw new InternalServerException(ErrorCode.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/src/main/java/com/swyp8team2/user/application/UserService.java b/src/main/java/com/swyp8team2/user/application/UserService.java index 64e5e1c1..b089de58 100644 --- a/src/main/java/com/swyp8team2/user/application/UserService.java +++ b/src/main/java/com/swyp8team2/user/application/UserService.java @@ -42,6 +42,12 @@ private String getNickname(String nickname) { }); } + @Transactional + public Long createGuest() { + User user = userRepository.save(User.createGuest()); + return user.getId(); + } + public UserInfoResponse findById(Long userId) { User user = userRepository.findById(userId) .orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND)); diff --git a/src/main/java/com/swyp8team2/user/domain/Role.java b/src/main/java/com/swyp8team2/user/domain/Role.java new file mode 100644 index 00000000..3792a4c5 --- /dev/null +++ b/src/main/java/com/swyp8team2/user/domain/Role.java @@ -0,0 +1,5 @@ +package com.swyp8team2.user.domain; + +public enum Role { + GUEST, USER +} diff --git a/src/main/java/com/swyp8team2/user/domain/User.java b/src/main/java/com/swyp8team2/user/domain/User.java index d63e1bd3..a9767fbf 100644 --- a/src/main/java/com/swyp8team2/user/domain/User.java +++ b/src/main/java/com/swyp8team2/user/domain/User.java @@ -29,14 +29,27 @@ public class User { private String seq; - public User(Long id, String nickname, String profileUrl, String seq) { + public Role role; + + public User(Long id, String nickname, String profileUrl, String seq, Role role) { this.id = id; this.nickname = nickname; this.profileUrl = profileUrl; this.seq = seq; + this.role = role; } public static User create(String nickname, String profileUrl) { - return new User(null, nickname, profileUrl, UUID.randomUUID().toString()); + return new User(null, nickname, profileUrl, UUID.randomUUID().toString(), Role.USER); + } + + public static User createGuest() { + return new User( + null, + "guest_" + System.currentTimeMillis(), + "https://image.photopic.site/images-dev/resized_202502240006030.png", + UUID.randomUUID().toString(), + Role.GUEST + ); } } From c2d42e5521d17238cc6ea53d505fbc1a85701d50 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 27 Feb 2025 21:52:33 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/application/AuthService.java | 14 +---- .../com/swyp8team2/auth/domain/UserInfo.java | 7 ++- .../auth/presentation/AuthController.java | 2 +- .../presentation/GuestTokenInterceptor.java | 27 -------- .../presentation/filter/GuestAuthFilter.java | 61 +++++++++++++++++++ .../presentation/filter/JwtAuthFilter.java | 3 +- .../common/config/SecurityConfig.java | 25 +++++++- .../common/exception/ErrorCode.java | 1 + 8 files changed, 93 insertions(+), 47 deletions(-) delete mode 100644 src/main/java/com/swyp8team2/auth/presentation/GuestTokenInterceptor.java create mode 100644 src/main/java/com/swyp8team2/auth/presentation/filter/GuestAuthFilter.java diff --git a/src/main/java/com/swyp8team2/auth/application/AuthService.java b/src/main/java/com/swyp8team2/auth/application/AuthService.java index f04588d3..470816d7 100644 --- a/src/main/java/com/swyp8team2/auth/application/AuthService.java +++ b/src/main/java/com/swyp8team2/auth/application/AuthService.java @@ -16,6 +16,7 @@ import java.security.NoSuchAlgorithmException; @Service +@RequiredArgsConstructor public class AuthService { private final JwtService jwtService; @@ -24,19 +25,6 @@ public class AuthService { private final UserService userService; private final CryptoService cryptoService; - public AuthService( - JwtService jwtService, - OAuthService oAuthService, - SocialAccountRepository socialAccountRepository, - UserService userService - ) throws Exception { - this.jwtService = jwtService; - this.oAuthService = oAuthService; - this.socialAccountRepository = socialAccountRepository; - this.userService = userService; - this.cryptoService = new CryptoService(); - } - @Transactional public TokenPair oauthSignIn(String code, String redirectUri) { OAuthUserInfo oAuthUserInfo = oAuthService.getUserInfo(code, redirectUri); diff --git a/src/main/java/com/swyp8team2/auth/domain/UserInfo.java b/src/main/java/com/swyp8team2/auth/domain/UserInfo.java index bd836070..84ac2c45 100644 --- a/src/main/java/com/swyp8team2/auth/domain/UserInfo.java +++ b/src/main/java/com/swyp8team2/auth/domain/UserInfo.java @@ -1,14 +1,17 @@ package com.swyp8team2.auth.domain; +import com.swyp8team2.user.domain.Role; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.Collections; +import java.util.List; import static com.swyp8team2.common.util.Validator.validateNull; -public record UserInfo(long userId) implements UserDetails { +public record UserInfo(long userId, Role role) implements UserDetails { public UserInfo { validateNull(userId); @@ -16,7 +19,7 @@ public record UserInfo(long userId) implements UserDetails { @Override public Collection getAuthorities() { - return Collections.emptyList(); + return List.of(new SimpleGrantedAuthority("ROLE_" + role.name())); } @Override diff --git a/src/main/java/com/swyp8team2/auth/presentation/AuthController.java b/src/main/java/com/swyp8team2/auth/presentation/AuthController.java index 32d2c508..b14bd9c2 100644 --- a/src/main/java/com/swyp8team2/auth/presentation/AuthController.java +++ b/src/main/java/com/swyp8team2/auth/presentation/AuthController.java @@ -55,7 +55,7 @@ public ResponseEntity reissue( return ResponseEntity.ok(new TokenResponse(tokenPair.accessToken())); } - @PostMapping("/guest/login") + @PostMapping("/guest/token") public ResponseEntity guestLogin() { String guestToken = authService.guestLogin(); return ResponseEntity.ok(new GuestTokenResponse(guestToken)); diff --git a/src/main/java/com/swyp8team2/auth/presentation/GuestTokenInterceptor.java b/src/main/java/com/swyp8team2/auth/presentation/GuestTokenInterceptor.java deleted file mode 100644 index 6334f37e..00000000 --- a/src/main/java/com/swyp8team2/auth/presentation/GuestTokenInterceptor.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.swyp8team2.auth.presentation; - -import com.swyp8team2.common.presentation.CustomHeader; -import com.swyp8team2.crypto.application.CryptoService; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; - -import java.util.Objects; - -@Component -@RequiredArgsConstructor -public class GuestTokenInterceptor implements HandlerInterceptor { - - private final CryptoService cryptoService; - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { -// String token = request.getHeader(CustomHeader.GUEST_ID); -// if (Objects.isNull(token)) { -// return true; -// } - return true; - } -} diff --git a/src/main/java/com/swyp8team2/auth/presentation/filter/GuestAuthFilter.java b/src/main/java/com/swyp8team2/auth/presentation/filter/GuestAuthFilter.java new file mode 100644 index 00000000..4b678645 --- /dev/null +++ b/src/main/java/com/swyp8team2/auth/presentation/filter/GuestAuthFilter.java @@ -0,0 +1,61 @@ +package com.swyp8team2.auth.presentation.filter; + +import com.swyp8team2.auth.domain.UserInfo; +import com.swyp8team2.common.exception.ApplicationException; +import com.swyp8team2.common.exception.BadRequestException; +import com.swyp8team2.common.exception.ErrorCode; +import com.swyp8team2.common.presentation.CustomHeader; +import com.swyp8team2.crypto.application.CryptoService; +import com.swyp8team2.user.domain.Role; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.Objects; + +import static com.swyp8team2.auth.presentation.filter.JwtAuthenticationEntryPoint.EXCEPTION_KEY; + +@Slf4j +@RequiredArgsConstructor +public class GuestAuthFilter extends OncePerRequestFilter { + + private final CryptoService cryptoService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + try { + AntPathMatcher matcher = new AntPathMatcher(); + if (!matcher.match("/posts/{postId}/votes/guest", request.getRequestURI())) { + return; + } + String token = request.getHeader(CustomHeader.GUEST_ID); + if (Objects.isNull(token)) { + throw new BadRequestException(ErrorCode.INVALID_GUEST_HEADER); + } + String guestId = cryptoService.decrypt(token); + Authentication authentication = getAuthentication(Long.parseLong(guestId)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (ApplicationException e) { + request.setAttribute(EXCEPTION_KEY, e); + } finally { + doFilter(request, response, filterChain); + } + } + + private Authentication getAuthentication(long userId) { + UserInfo userInfo = new UserInfo(userId, Role.GUEST); + return new UsernamePasswordAuthenticationToken(userInfo, null, userInfo.getAuthorities()); + } +} diff --git a/src/main/java/com/swyp8team2/auth/presentation/filter/JwtAuthFilter.java b/src/main/java/com/swyp8team2/auth/presentation/filter/JwtAuthFilter.java index d77b476f..b7f16f08 100644 --- a/src/main/java/com/swyp8team2/auth/presentation/filter/JwtAuthFilter.java +++ b/src/main/java/com/swyp8team2/auth/presentation/filter/JwtAuthFilter.java @@ -4,6 +4,7 @@ import com.swyp8team2.auth.application.jwt.JwtProvider; import com.swyp8team2.auth.domain.UserInfo; import com.swyp8team2.common.exception.ApplicationException; +import com.swyp8team2.user.domain.Role; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -44,7 +45,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } private Authentication getAuthentication(long userId) { - UserInfo userInfo = new UserInfo(userId); + UserInfo userInfo = new UserInfo(userId, Role.USER); return new UsernamePasswordAuthenticationToken(userInfo, null, userInfo.getAuthorities()); } } diff --git a/src/main/java/com/swyp8team2/common/config/SecurityConfig.java b/src/main/java/com/swyp8team2/common/config/SecurityConfig.java index 4e94ba55..e5de05a9 100644 --- a/src/main/java/com/swyp8team2/common/config/SecurityConfig.java +++ b/src/main/java/com/swyp8team2/common/config/SecurityConfig.java @@ -1,9 +1,12 @@ package com.swyp8team2.common.config; import com.swyp8team2.auth.application.jwt.JwtProvider; +import com.swyp8team2.auth.presentation.filter.GuestAuthFilter; import com.swyp8team2.auth.presentation.filter.HeaderTokenExtractor; import com.swyp8team2.auth.presentation.filter.JwtAuthFilter; import com.swyp8team2.auth.presentation.filter.JwtAuthenticationEntryPoint; +import com.swyp8team2.crypto.application.CryptoService; +import com.swyp8team2.user.domain.Role; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.security.servlet.PathRequest; @@ -32,11 +35,14 @@ public class SecurityConfig { private final HandlerExceptionResolver handlerExceptionResolver; + private final CryptoService cryptoService; public SecurityConfig( - @Qualifier("handlerExceptionResolver") HandlerExceptionResolver handlerExceptionResolver + @Qualifier("handlerExceptionResolver") HandlerExceptionResolver handlerExceptionResolver, + CryptoService cryptoService ) { this.handlerExceptionResolver = handlerExceptionResolver; + this.cryptoService = cryptoService; } @Bean @@ -84,11 +90,17 @@ public SecurityFilterChain securityFilterChain( .authorizeHttpRequests(authorize -> authorize .requestMatchers(getWhiteList(introspect)).permitAll() + .requestMatchers(getGuestTokenRequestList(introspect)) + .hasAnyRole(Role.USER.name(), Role.GUEST.name()) .anyRequest().authenticated()) .addFilterBefore( new JwtAuthFilter(jwtProvider, new HeaderTokenExtractor()), UsernamePasswordAuthenticationFilter.class) + .addFilterAfter( + new GuestAuthFilter(cryptoService), + JwtAuthFilter.class + ) .exceptionHandling(exception -> exception.authenticationEntryPoint( new JwtAuthenticationEntryPoint(handlerExceptionResolver))); @@ -99,11 +111,18 @@ public static MvcRequestMatcher[] getWhiteList(HandlerMappingIntrospector intros MvcRequestMatcher.Builder mvc = new MvcRequestMatcher.Builder(introspect); return new MvcRequestMatcher[]{ mvc.pattern("/auth/reissue"), - mvc.pattern("/guest"), + mvc.pattern("/auth/guest/token"), mvc.pattern(HttpMethod.GET, "/posts/{sharedUrl}"), mvc.pattern(HttpMethod.GET, "/posts/{postId}/comments"), - mvc.pattern("/posts/{postId}/votes/guest/**"), +// mvc.pattern("/posts/{postId}/votes/guest/**"), mvc.pattern("/auth/oauth2/**") }; } + + public static MvcRequestMatcher[] getGuestTokenRequestList(HandlerMappingIntrospector introspect) { + MvcRequestMatcher.Builder mvc = new MvcRequestMatcher.Builder(introspect); + return new MvcRequestMatcher[]{ + mvc.pattern("/posts/{postId}/votes/guest"), + }; + } } diff --git a/src/main/java/com/swyp8team2/common/exception/ErrorCode.java b/src/main/java/com/swyp8team2/common/exception/ErrorCode.java index 3a1f52d5..bbc5ea04 100644 --- a/src/main/java/com/swyp8team2/common/exception/ErrorCode.java +++ b/src/main/java/com/swyp8team2/common/exception/ErrorCode.java @@ -20,6 +20,7 @@ public enum ErrorCode { INVALID_POST_IMAGE_COUNT("게시글 이미지 개수 오류"), NOT_POST_AUTHOR("게시글 작성자가 아님"), POST_ALREADY_CLOSED("이미 마감된 게시글"), + INVALID_GUEST_HEADER("잘못된 게스트 토큰 헤더"), //401 EXPIRED_TOKEN("토큰 만료"), From e81d3a167b24f6cb6c617b6a43539a1fa16c6d1c Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 27 Feb 2025 21:52:50 +0900 Subject: [PATCH 03/11] =?UTF-8?q?refactor:=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ApplicationControllerAdvice.java | 37 ++++++------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/swyp8team2/common/exception/ApplicationControllerAdvice.java b/src/main/java/com/swyp8team2/common/exception/ApplicationControllerAdvice.java index 2796149d..3d2fc530 100644 --- a/src/main/java/com/swyp8team2/common/exception/ApplicationControllerAdvice.java +++ b/src/main/java/com/swyp8team2/common/exception/ApplicationControllerAdvice.java @@ -3,6 +3,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingRequestHeaderException; @@ -33,16 +34,21 @@ public ResponseEntity handle(UnauthorizedException e) { .body(response); } - @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity handle(MethodArgumentNotValidException e) { - log.debug("MethodArgumentNotValidException {}", e.getMessage()); + @ExceptionHandler({ + MethodArgumentNotValidException.class, + HttpMessageNotReadableException.class, + MissingRequestHeaderException.class, + HandlerMethodValidationException.class + }) + public ResponseEntity invalidArgument(Exception e) { + log.debug("invalidArgument: {}", e.getMessage()); return ResponseEntity.badRequest() .body(new ErrorResponse(ErrorCode.INVALID_ARGUMENT)); } - @ExceptionHandler(HttpRequestMethodNotSupportedException.class) - public ResponseEntity handle(HttpRequestMethodNotSupportedException e) { - log.debug("HttpRequestMethodNotSupportedException {}", e.getMessage()); + @ExceptionHandler({HttpRequestMethodNotSupportedException.class, MethodArgumentTypeMismatchException.class}) + public ResponseEntity notFound(HttpRequestMethodNotSupportedException e) { + log.debug("notFound: {}", e.getMessage()); return ResponseEntity.notFound().build(); } @@ -52,11 +58,6 @@ public ResponseEntity handle(NoResourceFoundException e) { return ResponseEntity.notFound().build(); } - @ExceptionHandler(HandlerMethodValidationException.class) - public ResponseEntity handle(HandlerMethodValidationException e) { - return ResponseEntity.badRequest() - .body(new ErrorResponse(ErrorCode.INVALID_ARGUMENT)); - } @ExceptionHandler(AuthenticationException.class) public ResponseEntity handle(AuthenticationException e) { @@ -70,20 +71,6 @@ public ResponseEntity handle(AccessDeniedException e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponse(ErrorCode.INVALID_TOKEN)); } - @ExceptionHandler(MissingRequestHeaderException.class) - public ResponseEntity handle(MissingRequestHeaderException e) { - log.debug("MissingRequestHeaderException {}", e.getMessage()); - return ResponseEntity.badRequest() - .body(new ErrorResponse(ErrorCode.INVALID_ARGUMENT)); - } - - @ExceptionHandler(MethodArgumentTypeMismatchException.class) - public ResponseEntity handle(MethodArgumentTypeMismatchException e) { - log.debug("MethodArgumentTypeMismatchException {}", e.getMessage()); - return ResponseEntity.badRequest() - .body(new ErrorResponse(ErrorCode.INVALID_ARGUMENT)); - } - @ExceptionHandler(Exception.class) public ResponseEntity handle(Exception e) { log.error("Exception", e); From beead95917beb97337563adbf55d2d38f0aa1018 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 27 Feb 2025 22:20:00 +0900 Subject: [PATCH 04/11] =?UTF-8?q?refactor:=20=ED=88=AC=ED=91=9C=20userSeq?= =?UTF-8?q?=20->=20userId=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/application/PostService.java | 4 ++-- .../vote/application/VoteService.java | 20 +++++++++---------- .../java/com/swyp8team2/vote/domain/Vote.java | 10 +++++----- .../vote/domain/VoteRepository.java | 4 ++-- .../vote/presentation/VoteController.java | 6 +++--- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/swyp8team2/post/application/PostService.java b/src/main/java/com/swyp8team2/post/application/PostService.java index b9279815..5b643b77 100644 --- a/src/main/java/com/swyp8team2/post/application/PostService.java +++ b/src/main/java/com/swyp8team2/post/application/PostService.java @@ -88,7 +88,7 @@ private PostImageResponse createVoteResponseDto(PostImage image, Long userId, Lo private Boolean getVoted(PostImage image, Long userId, Long postId) { User user = userRepository.findById(userId) .orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND)); - return voteRepository.findByUserSeqAndPostId(user.getSeq(), postId) + return voteRepository.findByUserIdAndPostId(user.getId(), postId) .map(vote -> vote.getPostImageId().equals(image.getId())) .orElse(false); } @@ -108,7 +108,7 @@ private SimplePostResponse createSimplePostResponse(Post post) { public CursorBasePaginatedResponse findVotedPosts(Long userId, Long cursor, int size) { User user = userRepository.findById(userId) .orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND)); - List postIds = voteRepository.findByUserSeq(user.getSeq()) + List postIds = voteRepository.findByUserId(user.getId()) .map(Vote::getPostId) .toList(); Slice postSlice = postRepository.findByIdIn(postIds, cursor, PageRequest.ofSize(size)); diff --git a/src/main/java/com/swyp8team2/vote/application/VoteService.java b/src/main/java/com/swyp8team2/vote/application/VoteService.java index 9860e8a7..e7f093d9 100644 --- a/src/main/java/com/swyp8team2/vote/application/VoteService.java +++ b/src/main/java/com/swyp8team2/vote/application/VoteService.java @@ -8,7 +8,6 @@ import com.swyp8team2.user.domain.UserRepository; import com.swyp8team2.vote.domain.Vote; import com.swyp8team2.vote.domain.VoteRepository; -import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,13 +25,13 @@ public class VoteService { public Long vote(Long voterId, Long postId, Long imageId) { User voter = userRepository.findById(voterId) .orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND)); - deleteVoteIfExisting(postId, voter.getSeq()); - Vote vote = createVote(postId, imageId, voter.getSeq()); + deleteVoteIfExisting(postId, voter.getId()); + Vote vote = createVote(postId, imageId, voter.getId()); return vote.getId(); } - private void deleteVoteIfExisting(Long postId, String userSeq) { - voteRepository.findByUserSeqAndPostId(userSeq, postId) + private void deleteVoteIfExisting(Long postId, Long userId) { + voteRepository.findByUserIdAndPostId(userId, postId) .ifPresent(vote -> { voteRepository.delete(vote); postRepository.findById(postId) @@ -41,18 +40,19 @@ private void deleteVoteIfExisting(Long postId, String userSeq) { }); } - private Vote createVote(Long postId, Long imageId, String userSeq) { + private Vote createVote(Long postId, Long imageId, Long userId) { Post post = postRepository.findById(postId) .orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND)); post.validateProgress(); - Vote vote = voteRepository.save(Vote.of(post.getId(), imageId, userSeq)); + Vote vote = voteRepository.save(Vote.of(post.getId(), imageId, userId)); post.vote(imageId); return vote; } - public Long guestVote(String guestId, Long postId, Long imageId) { - deleteVoteIfExisting(postId, guestId); - Vote vote = createVote(postId, imageId, guestId); + @Transactional + public Long guestVote(Long userId, Long postId, Long imageId) { + deleteVoteIfExisting(postId, userId); + Vote vote = createVote(postId, imageId, userId); return vote.getId(); } } diff --git a/src/main/java/com/swyp8team2/vote/domain/Vote.java b/src/main/java/com/swyp8team2/vote/domain/Vote.java index dde80117..91c483fe 100644 --- a/src/main/java/com/swyp8team2/vote/domain/Vote.java +++ b/src/main/java/com/swyp8team2/vote/domain/Vote.java @@ -23,16 +23,16 @@ public class Vote { private Long postImageId; - private String userSeq; + private Long userId; - public Vote(Long id, Long postId, Long postImageId, String userSeq) { + public Vote(Long id, Long postId, Long postImageId, Long userId) { this.id = id; this.postId = postId; this.postImageId = postImageId; - this.userSeq = userSeq; + this.userId = userId; } - public static Vote of(Long postId, Long postImageId, String userSeq) { - return new Vote(null, postId, postImageId, userSeq); + public static Vote of(Long postId, Long postImageId, Long userId) { + return new Vote(null, postId, postImageId, userId); } } diff --git a/src/main/java/com/swyp8team2/vote/domain/VoteRepository.java b/src/main/java/com/swyp8team2/vote/domain/VoteRepository.java index 786e845f..05c2ccf5 100644 --- a/src/main/java/com/swyp8team2/vote/domain/VoteRepository.java +++ b/src/main/java/com/swyp8team2/vote/domain/VoteRepository.java @@ -8,7 +8,7 @@ @Repository public interface VoteRepository extends JpaRepository { - Optional findByUserSeqAndPostId(String userSeq, Long postId); + Optional findByUserIdAndPostId(Long userId, Long postId); - Slice findByUserSeq(String seq); + Slice findByUserId(Long userId); } diff --git a/src/main/java/com/swyp8team2/vote/presentation/VoteController.java b/src/main/java/com/swyp8team2/vote/presentation/VoteController.java index 25025afd..51d0caea 100644 --- a/src/main/java/com/swyp8team2/vote/presentation/VoteController.java +++ b/src/main/java/com/swyp8team2/vote/presentation/VoteController.java @@ -37,10 +37,10 @@ public ResponseEntity vote( @PostMapping("/guest") public ResponseEntity guestVote( @PathVariable("postId") Long postId, - @RequestHeader(CustomHeader.GUEST_ID) String guestId, - @Valid @RequestBody VoteRequest request + @Valid @RequestBody VoteRequest request, + @AuthenticationPrincipal UserInfo userInfo ) { - voteService.guestVote(guestId, postId, request.imageId()); + voteService.guestVote(userInfo.userId(), postId, request.imageId()); return ResponseEntity.ok().build(); } From c09d4d6d33a6b8aa65b227af805f8c949c0058eb Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 27 Feb 2025 22:20:16 +0900 Subject: [PATCH 05/11] =?UTF-8?q?test:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EB=B0=9C=EA=B8=89=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/presentation/AuthControllerTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java b/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java index fc93bafb..676f8231 100644 --- a/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java +++ b/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java @@ -2,6 +2,7 @@ import com.swyp8team2.auth.application.AuthService; import com.swyp8team2.auth.application.jwt.TokenPair; +import com.swyp8team2.auth.presentation.dto.GuestTokenResponse; import com.swyp8team2.auth.presentation.dto.OAuthSignInRequest; import com.swyp8team2.auth.presentation.dto.TokenResponse; import com.swyp8team2.common.exception.BadRequestException; @@ -146,4 +147,23 @@ void reissue_refreshTokenMismatched() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(objectMapper.writeValueAsString(response))); } + + @Test + @DisplayName("게스트 토큰 발급") + void guestLogin() throws Exception { + //given + String guestToken = "guestToken"; + given(authService.guestLogin()) + .willReturn(guestToken); + + //when then + mockMvc.perform(post("/auth/guest/token")) + .andExpect(status().isOk()) + .andExpect(content().json(objectMapper.writeValueAsString(new GuestTokenResponse(guestToken)))) + .andDo(restDocs.document( + responseFields( + fieldWithPath("guestToken").description("게스트 토큰") + ) + )); + } } From 3cebe5a5a561607c60ecde7a5117d1df1094130e Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 27 Feb 2025 22:25:32 +0900 Subject: [PATCH 06/11] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/application/CommentServiceTest.java | 5 +++-- .../comment/domain/CommentRepositoryTest.java | 2 +- .../post/application/PostServiceTest.java | 2 +- .../com/swyp8team2/support/WithMockUserInfo.java | 2 ++ .../support/config/TestSecurityConfig.java | 8 ++++++-- .../security/TestSecurityContextFactory.java | 3 ++- .../vote/application/VoteServiceTest.java | 15 ++++++++------- .../vote/presentation/VoteControllerTest.java | 9 +++++---- 8 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/test/java/com/swyp8team2/comment/application/CommentServiceTest.java b/src/test/java/com/swyp8team2/comment/application/CommentServiceTest.java index 29b7f6f0..09d5e374 100644 --- a/src/test/java/com/swyp8team2/comment/application/CommentServiceTest.java +++ b/src/test/java/com/swyp8team2/comment/application/CommentServiceTest.java @@ -8,6 +8,7 @@ import com.swyp8team2.common.dto.CursorBasePaginatedResponse; import com.swyp8team2.common.exception.BadRequestException; import com.swyp8team2.common.exception.ErrorCode; +import com.swyp8team2.user.domain.Role; import com.swyp8team2.user.domain.User; import com.swyp8team2.user.domain.UserRepository; import org.junit.jupiter.api.DisplayName; @@ -48,7 +49,7 @@ void createComment() { // given Long postId = 1L; CreateCommentRequest request = new CreateCommentRequest("테스트 댓글"); - UserInfo userInfo = new UserInfo(100L); + UserInfo userInfo = new UserInfo(100L, Role.USER); Comment comment = new Comment(postId, userInfo.userId(), request.content()); // when @@ -69,7 +70,7 @@ void findComments() { Comment comment1 = new Comment(1L, postId, 100L, "첫 번째 댓글"); Comment comment2 = new Comment(2L, postId, 100L, "두 번째 댓글"); SliceImpl commentSlice = new SliceImpl<>(List.of(comment1, comment2), PageRequest.of(0, size), false); - User user = new User(100L, "닉네임","http://example.com/profile.png", "seq"); + User user = new User(100L, "닉네임","http://example.com/profile.png", "seq", Role.USER); // Mock 설정 given(commentRepository.findByPostId(eq(postId), eq(cursor), any(PageRequest.class))).willReturn(commentSlice); diff --git a/src/test/java/com/swyp8team2/comment/domain/CommentRepositoryTest.java b/src/test/java/com/swyp8team2/comment/domain/CommentRepositoryTest.java index aaa6f9ca..d8a44e7d 100644 --- a/src/test/java/com/swyp8team2/comment/domain/CommentRepositoryTest.java +++ b/src/test/java/com/swyp8team2/comment/domain/CommentRepositoryTest.java @@ -35,6 +35,6 @@ void select_CommentUser() { Slice result2 = commentRepository.findByPostId(1L, 1L, PageRequest.of(0, 10)); // then2 - assertThat(result2.getContent()).hasSize(2); + assertThat(result2.getContent()).hasSize(0); } } \ No newline at end of file diff --git a/src/test/java/com/swyp8team2/post/application/PostServiceTest.java b/src/test/java/com/swyp8team2/post/application/PostServiceTest.java index 53b927be..4f5118d9 100644 --- a/src/test/java/com/swyp8team2/post/application/PostServiceTest.java +++ b/src/test/java/com/swyp8team2/post/application/PostServiceTest.java @@ -196,7 +196,7 @@ void findVotedPosts() throws Exception { List posts = createPosts(user); for (int i = 0; i < 15; i++) { Post post = posts.get(i); - voteRepository.save(Vote.of(post.getId(), post.getImages().get(0).getId(), user.getSeq())); + voteRepository.save(Vote.of(post.getId(), post.getImages().get(0).getId(), user.getId())); } int size = 10; diff --git a/src/test/java/com/swyp8team2/support/WithMockUserInfo.java b/src/test/java/com/swyp8team2/support/WithMockUserInfo.java index bb769e56..5b42ff3e 100644 --- a/src/test/java/com/swyp8team2/support/WithMockUserInfo.java +++ b/src/test/java/com/swyp8team2/support/WithMockUserInfo.java @@ -1,6 +1,7 @@ package com.swyp8team2.support; import com.swyp8team2.support.security.TestSecurityContextFactory; +import com.swyp8team2.user.domain.Role; import org.springframework.security.test.context.support.WithSecurityContext; import java.lang.annotation.Retention; @@ -10,4 +11,5 @@ @WithSecurityContext(factory = TestSecurityContextFactory.class) public @interface WithMockUserInfo { long userId() default 1L; + Role role() default Role.USER; } diff --git a/src/test/java/com/swyp8team2/support/config/TestSecurityConfig.java b/src/test/java/com/swyp8team2/support/config/TestSecurityConfig.java index 163d7423..2fd6de4a 100644 --- a/src/test/java/com/swyp8team2/support/config/TestSecurityConfig.java +++ b/src/test/java/com/swyp8team2/support/config/TestSecurityConfig.java @@ -1,5 +1,6 @@ package com.swyp8team2.support.config; +import com.swyp8team2.user.domain.Role; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -9,6 +10,7 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; +import static com.swyp8team2.common.config.SecurityConfig.getGuestTokenRequestList; import static com.swyp8team2.common.config.SecurityConfig.getWhiteList; @TestConfiguration @@ -32,8 +34,10 @@ public SecurityFilterChain securityFilterChain( .authorizeHttpRequests(authorize -> authorize - .requestMatchers(getWhiteList(introspect)).permitAll() - .anyRequest().authenticated() +// .requestMatchers(getWhiteList(introspect)).permitAll() +// .requestMatchers(getGuestTokenRequestList(introspect)) +// .hasAnyRole(Role.USER.name(), Role.GUEST.name()) + .anyRequest().permitAll() ); return http.build(); } diff --git a/src/test/java/com/swyp8team2/support/security/TestSecurityContextFactory.java b/src/test/java/com/swyp8team2/support/security/TestSecurityContextFactory.java index 822963d0..e6a06d4a 100644 --- a/src/test/java/com/swyp8team2/support/security/TestSecurityContextFactory.java +++ b/src/test/java/com/swyp8team2/support/security/TestSecurityContextFactory.java @@ -2,6 +2,7 @@ import com.swyp8team2.auth.domain.UserInfo; import com.swyp8team2.support.WithMockUserInfo; +import com.swyp8team2.user.domain.Role; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; @@ -15,7 +16,7 @@ public class TestSecurityContextFactory implements WithSecurityContextFactory assertThat(vote.getUserSeq()).isEqualTo(user.getSeq()), + () -> assertThat(vote.getUserId()).isEqualTo(user.getId()), () -> assertThat(vote.getPostId()).isEqualTo(post.getId()), () -> assertThat(vote.getPostImageId()).isEqualTo(post.getImages().get(0).getId()), () -> assertThat(findPost.getImages().get(0).getVoteCount()).isEqualTo(1) @@ -83,7 +83,7 @@ void vote_change() { Vote vote = voteRepository.findById(voteId).get(); Post findPost = postRepository.findById(post.getId()).get(); assertAll( - () -> assertThat(vote.getUserSeq()).isEqualTo(user.getSeq()), + () -> assertThat(vote.getUserId()).isEqualTo(user.getId()), () -> assertThat(vote.getPostId()).isEqualTo(post.getId()), () -> assertThat(vote.getPostImageId()).isEqualTo(post.getImages().get(1).getId()), () -> assertThat(findPost.getImages().get(0).getVoteCount()).isEqualTo(0), @@ -120,8 +120,8 @@ void vote_alreadyClosed() { @DisplayName("게스트 투표하기") void guestVote() { // given - String guestId = "guestId"; User user = userRepository.save(createUser(1)); + Long guestId = user.getId() + 1L; ImageFile imageFile1 = imageFileRepository.save(createImageFile(1)); ImageFile imageFile2 = imageFileRepository.save(createImageFile(2)); Post post = postRepository.save(createPost(user.getId(), imageFile1, imageFile2, 1)); @@ -133,7 +133,7 @@ void guestVote() { Vote vote = voteRepository.findById(voteId).get(); Post findPost = postRepository.findById(post.getId()).get(); assertAll( - () -> assertThat(vote.getUserSeq()).isEqualTo(guestId), + () -> assertThat(vote.getUserId()).isEqualTo(guestId), () -> assertThat(vote.getPostId()).isEqualTo(post.getId()), () -> assertThat(vote.getPostImageId()).isEqualTo(post.getImages().get(0).getId()), () -> assertThat(findPost.getImages().get(0).getVoteCount()).isEqualTo(1) @@ -144,8 +144,8 @@ void guestVote() { @DisplayName("게스트 투표하기 - 다른 이미지로 투표 변경한 경우") void guestVote_change() { // given - String guestId = "guestId"; User user = userRepository.save(createUser(1)); + Long guestId = user.getId() + 1L; ImageFile imageFile1 = imageFileRepository.save(createImageFile(1)); ImageFile imageFile2 = imageFileRepository.save(createImageFile(2)); Post post = postRepository.save(createPost(user.getId(), imageFile1, imageFile2, 1)); @@ -158,7 +158,7 @@ void guestVote_change() { Vote vote = voteRepository.findById(voteId).get(); Post findPost = postRepository.findById(post.getId()).get(); assertAll( - () -> assertThat(vote.getUserSeq()).isEqualTo(guestId), + () -> assertThat(vote.getUserId()).isEqualTo(guestId), () -> assertThat(vote.getPostId()).isEqualTo(post.getId()), () -> assertThat(vote.getPostImageId()).isEqualTo(post.getImages().get(1).getId()), () -> assertThat(findPost.getImages().get(0).getVoteCount()).isEqualTo(0), @@ -171,6 +171,7 @@ void guestVote_change() { void guestVote_alreadyClosed() { // given User user = userRepository.save(createUser(1)); + Long guestId = user.getId() + 1L; ImageFile imageFile1 = imageFileRepository.save(createImageFile(1)); ImageFile imageFile2 = imageFileRepository.save(createImageFile(2)); Post post = postRepository.save(new Post( @@ -186,7 +187,7 @@ void guestVote_alreadyClosed() { )); // when - assertThatThrownBy(() -> voteService.guestVote("guestId", post.getId(), post.getImages().get(0).getId())) + assertThatThrownBy(() -> voteService.guestVote(guestId, post.getId(), post.getImages().get(0).getId())) .isInstanceOf(BadRequestException.class) .hasMessage(ErrorCode.POST_ALREADY_CLOSED.getMessage()); } diff --git a/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java b/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java index c28de442..129d98d2 100644 --- a/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java +++ b/src/test/java/com/swyp8team2/vote/presentation/VoteControllerTest.java @@ -3,6 +3,7 @@ import com.swyp8team2.common.presentation.CustomHeader; import com.swyp8team2.support.RestDocsTest; import com.swyp8team2.support.WithMockUserInfo; +import com.swyp8team2.user.domain.Role; import com.swyp8team2.vote.presentation.dto.ChangeVoteRequest; import com.swyp8team2.vote.presentation.dto.VoteRequest; import org.junit.jupiter.api.DisplayName; @@ -58,7 +59,7 @@ void vote() throws Exception { } @Test - @WithAnonymousUser + @WithMockUserInfo(role = Role.GUEST) @DisplayName("게스트 투표") void guestVote() throws Exception { //given @@ -68,7 +69,7 @@ void guestVote() throws Exception { mockMvc.perform(post("/posts/{postId}/votes/guest", "1") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) - .header(CustomHeader.GUEST_ID, UUID.randomUUID().toString())) + .header(CustomHeader.GUEST_ID, "guestToken")) .andExpect(status().isOk()) .andDo(restDocs.document( requestHeaders(guestHeader()), @@ -111,7 +112,7 @@ void changeVote() throws Exception { } @Test - @WithAnonymousUser + @WithMockUserInfo(role = Role.GUEST) @DisplayName("게스트 투표 변경") void guestChangeVote() throws Exception { //given @@ -121,7 +122,7 @@ void guestChangeVote() throws Exception { mockMvc.perform(patch("/posts/{postId}/votes/guest", "1") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) - .header(CustomHeader.GUEST_ID, UUID.randomUUID().toString())) + .header(CustomHeader.GUEST_ID, "guestToken")) .andExpect(status().isOk()) .andDo(restDocs.document( requestHeaders(guestHeader()), From 25bed71442c4d22aab8256bbf54c93e7d2e51072 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 27 Feb 2025 22:25:51 +0900 Subject: [PATCH 07/11] =?UTF-8?q?fix:=20=EB=8C=93=EA=B8=80=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/swyp8team2/comment/domain/CommentRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/swyp8team2/comment/domain/CommentRepository.java b/src/main/java/com/swyp8team2/comment/domain/CommentRepository.java index dc36a80b..54e0f7e6 100644 --- a/src/main/java/com/swyp8team2/comment/domain/CommentRepository.java +++ b/src/main/java/com/swyp8team2/comment/domain/CommentRepository.java @@ -14,7 +14,7 @@ public interface CommentRepository extends JpaRepository { SELECT c FROM Comment c WHERE c.postId = :postId - AND (:cursor is null or c.id > :cursor) + AND (:cursor is null or c.id < :cursor) ORDER BY c.createdAt DESC """) Slice findByPostId( From 8280d6f7fe17dc73d0db287d575253c7344533aa Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 27 Feb 2025 22:26:06 +0900 Subject: [PATCH 08/11] =?UTF-8?q?fix:=20=EB=82=B4=EA=B0=80=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=ED=95=9C=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20api=20url=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/swyp8team2/post/presentation/PostController.java | 2 +- .../com/swyp8team2/post/presentation/PostControllerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/swyp8team2/post/presentation/PostController.java b/src/main/java/com/swyp8team2/post/presentation/PostController.java index 80596126..3d4273a0 100644 --- a/src/main/java/com/swyp8team2/post/presentation/PostController.java +++ b/src/main/java/com/swyp8team2/post/presentation/PostController.java @@ -76,7 +76,7 @@ public ResponseEntity deletePost( return ResponseEntity.ok().build(); } - @GetMapping("/user") + @GetMapping("/user/me") public ResponseEntity> findMyPosts( @RequestParam(name = "cursor", required = false) @Min(0) Long cursor, @RequestParam(name = "size", required = false, defaultValue = "10") @Min(1) int size, diff --git a/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java b/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java index 6041d4d4..0d3701f1 100644 --- a/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java +++ b/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java @@ -206,7 +206,7 @@ void findMyPost() throws Exception { .willReturn(response); //when then - mockMvc.perform(get("/posts/user") + mockMvc.perform(get("/posts/user/me") .header(HttpHeaders.AUTHORIZATION, "Bearer token")) .andExpect(status().isOk()) .andExpect(content().json(objectMapper.writeValueAsString(response))) From 1212f2a2026a495a61663971f6c0caadfb958756 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 27 Feb 2025 22:34:38 +0900 Subject: [PATCH 09/11] =?UTF-8?q?feat:=20baseEntity=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/swyp8team2/auth/domain/SocialAccount.java | 3 ++- src/main/java/com/swyp8team2/user/domain/User.java | 5 ++++- src/main/java/com/swyp8team2/vote/domain/Vote.java | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/swyp8team2/auth/domain/SocialAccount.java b/src/main/java/com/swyp8team2/auth/domain/SocialAccount.java index ff12cb20..23f784e7 100644 --- a/src/main/java/com/swyp8team2/auth/domain/SocialAccount.java +++ b/src/main/java/com/swyp8team2/auth/domain/SocialAccount.java @@ -1,6 +1,7 @@ package com.swyp8team2.auth.domain; import com.swyp8team2.auth.application.oauth.dto.OAuthUserInfo; +import com.swyp8team2.common.domain.BaseEntity; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -16,7 +17,7 @@ @Getter @Entity @NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) -public class SocialAccount { +public class SocialAccount extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/com/swyp8team2/user/domain/User.java b/src/main/java/com/swyp8team2/user/domain/User.java index a9767fbf..85ad7041 100644 --- a/src/main/java/com/swyp8team2/user/domain/User.java +++ b/src/main/java/com/swyp8team2/user/domain/User.java @@ -1,6 +1,8 @@ package com.swyp8team2.user.domain; +import com.swyp8team2.common.domain.BaseEntity; import jakarta.persistence.Entity; +import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -17,7 +19,7 @@ @Entity @Table(name = "users") @NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) -public class User { +public class User extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -29,6 +31,7 @@ public class User { private String seq; + @Enumerated(jakarta.persistence.EnumType.STRING) public Role role; public User(Long id, String nickname, String profileUrl, String seq, Role role) { diff --git a/src/main/java/com/swyp8team2/vote/domain/Vote.java b/src/main/java/com/swyp8team2/vote/domain/Vote.java index 91c483fe..23bd38a9 100644 --- a/src/main/java/com/swyp8team2/vote/domain/Vote.java +++ b/src/main/java/com/swyp8team2/vote/domain/Vote.java @@ -1,5 +1,6 @@ package com.swyp8team2.vote.domain; +import com.swyp8team2.common.domain.BaseEntity; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -13,7 +14,7 @@ @Entity @Table(name = "user_votes") @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Vote { +public class Vote extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) From 19af103cb35944432fee3b0bf0fc0a2eca5aa49f Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 27 Feb 2025 22:34:48 +0900 Subject: [PATCH 10/11] =?UTF-8?q?feat:=20=EB=8C=93=EA=B8=80=20=EB=8D=94?= =?UTF-8?q?=EB=AF=B8=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/swyp8team2/common/dev/DataInitializer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/swyp8team2/common/dev/DataInitializer.java b/src/main/java/com/swyp8team2/common/dev/DataInitializer.java index c490ac30..9665d462 100644 --- a/src/main/java/com/swyp8team2/common/dev/DataInitializer.java +++ b/src/main/java/com/swyp8team2/common/dev/DataInitializer.java @@ -2,6 +2,8 @@ import com.swyp8team2.auth.application.jwt.JwtService; import com.swyp8team2.auth.application.jwt.TokenPair; +import com.swyp8team2.comment.domain.Comment; +import com.swyp8team2.comment.domain.CommentRepository; import com.swyp8team2.image.domain.ImageFile; import com.swyp8team2.image.domain.ImageFileRepository; import com.swyp8team2.image.presentation.dto.ImageFileDto; @@ -33,6 +35,7 @@ public class DataInitializer { private final PostRepository postRepository; private final JwtService jwtService; private final VoteService voteService; + private final CommentRepository commentRepository; @Transactional public void init() { @@ -52,12 +55,14 @@ public void init() { ImageFile imageFile2 = imageFileRepository.save(ImageFile.create(new ImageFileDto("202502240006030.png", "https://image.photopic.site/images-dev/202502240006030.png", "https://image.photopic.site/images-dev/resized_202502240006030.png"))); posts.add(postRepository.save(Post.create(user.getId(), "description" + j, List.of(PostImage.create("뽀또A", imageFile1.getId()), PostImage.create("뽀또B", imageFile2.getId())), "https://photopic.site/shareurl"))); } + } for (User user : users) { for (Post post : posts) { Random random = new Random(); int num = random.nextInt(2); voteService.vote(user.getId(), post.getId(), post.getImages().get(num).getId()); + commentRepository.save(new Comment(post.getId(), user.getId(), "댓글 내용" + random.nextInt(100))); } } } From 80de11da132072dd1221e8e202c341426e4fbc48 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Thu, 27 Feb 2025 22:41:47 +0900 Subject: [PATCH 11/11] =?UTF-8?q?test:=20=EC=95=94=ED=98=B8=ED=99=94=20?= =?UTF-8?q?=EB=B3=B5=ED=98=B8=ED=99=94=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crypto/application/CryptoServiceTest.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/test/java/com/swyp8team2/crypto/application/CryptoServiceTest.java diff --git a/src/test/java/com/swyp8team2/crypto/application/CryptoServiceTest.java b/src/test/java/com/swyp8team2/crypto/application/CryptoServiceTest.java new file mode 100644 index 00000000..3142366c --- /dev/null +++ b/src/test/java/com/swyp8team2/crypto/application/CryptoServiceTest.java @@ -0,0 +1,61 @@ +package com.swyp8team2.crypto.application; + +import com.swyp8team2.common.exception.BadRequestException; +import com.swyp8team2.common.exception.ErrorCode; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +class CryptoServiceTest { + + CryptoService cryptoService; + + @BeforeEach + void setUp() throws Exception { + cryptoService = new CryptoService(); + } + + @Test + @DisplayName("암호화 및 복호화") + void encryptAndDecrypt() { + // given + String plainText = "Hello, World!"; + + // when + String encryptedText = cryptoService.encrypt(plainText); + String decryptedText = cryptoService.decrypt(encryptedText); + + // then + assertThat(decryptedText).isEqualTo(plainText); + } + + @Test + @DisplayName("암호화 및 복호화 - 다른 키") + void encryptAndDecrypt_differentKey() throws Exception { + // given + String plainText = "Hello, World!"; + CryptoService differentCryptoService = new CryptoService(); + String encryptedText = differentCryptoService.encrypt(plainText); + + // when then + assertThatThrownBy(() -> cryptoService.decrypt(encryptedText)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.INVALID_TOKEN.getMessage()); + } + + @Test + @DisplayName("복호화 - 이상한 토큰") + void decrypt_invalidToken() { + // given + String invalid = "invalidToken"; + + // when then + assertThatThrownBy(() -> cryptoService.decrypt(invalid)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.INVALID_TOKEN.getMessage()); + } +}