diff --git a/Backend/docker-compose.yml b/Backend/docker-compose.yml index 9ad3a88..ec9cb4e 100644 --- a/Backend/docker-compose.yml +++ b/Backend/docker-compose.yml @@ -16,4 +16,9 @@ services: - ./db:/var/lib/mysql container_name: chat-db ports: - - "3306:3306" \ No newline at end of file + - "3306:3306" + memcached: + container_name: solsol-memcached + image: memcached:alpine + ports: + - 11211:11211 \ No newline at end of file diff --git a/Backend/pom.xml b/Backend/pom.xml index 31e4fbe..f3561a3 100644 --- a/Backend/pom.xml +++ b/Backend/pom.xml @@ -115,6 +115,23 @@ 2.5 provided + + com.google.code.simple-spring-memcached + spring-cache + 4.1.3 + + + com.google.code.simple-spring-memcached + simple-spring-memcached + 4.1.3 + + + + com.google.code.simple-spring-memcached + xmemcached-provider + 4.1.3 + + diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/SolsolApplication.java b/Backend/src/main/java/kimjang/toolkit/solsol/SolsolApplication.java index 0ac311c..2018084 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/SolsolApplication.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/SolsolApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +@EnableCaching @SpringBootApplication public class SolsolApplication { public static void main(String[] args) { diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/config/SecurityConfig.java b/Backend/src/main/java/kimjang/toolkit/solsol/config/SecurityConfig.java index bdf4c42..c5e2a68 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/config/SecurityConfig.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/config/SecurityConfig.java @@ -2,13 +2,10 @@ import kimjang.toolkit.solsol.config.filter.*; import kimjang.toolkit.solsol.config.provider.JwtAuthenticationProvider; -import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.ProviderManager; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; @@ -152,8 +149,4 @@ public PasswordEncoder passwordEncoder(){ // BCrypt 알고리즘을 이용해서 비밀번호를 해싱하자 return new BCryptPasswordEncoder(); } - - - - } diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/config/cache/CacheConfig.java b/Backend/src/main/java/kimjang/toolkit/solsol/config/cache/CacheConfig.java new file mode 100644 index 0000000..dc48c49 --- /dev/null +++ b/Backend/src/main/java/kimjang/toolkit/solsol/config/cache/CacheConfig.java @@ -0,0 +1,79 @@ +package kimjang.toolkit.solsol.config.cache; + +import com.google.code.ssm.Cache; +import com.google.code.ssm.CacheFactory; +import com.google.code.ssm.config.AddressProvider; +import com.google.code.ssm.config.DefaultAddressProvider; +import com.google.code.ssm.providers.CacheConfiguration; +import com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl; +import com.google.code.ssm.spring.ExtendedSSMCacheManager; +import com.google.code.ssm.spring.SSMCache; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; + +@Slf4j +@Configuration +public class CacheConfig extends CachingConfigurerSupport { + + @Value("${spring.cache.address}") + private String address; + + @Value("${spring.cache.operationTimeout}") + private String operationTimeout; + + @Value("${spring.cache.cacheName}") + private String cacheName; + + @Value("${spring.cache.expiration}") + private String expiration; + + @Bean + public CacheManager cacheManager() { + + CacheFactory cacheFactory = getCacheFactory(); + + Cache object = null; + + try { + object = cacheFactory.getObject(); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + SSMCache ssmCache = new SSMCache(object, Integer.parseInt(expiration), true); + + ArrayList ssmCaches = new ArrayList<>(); + ssmCaches.add(0, ssmCache); + + ExtendedSSMCacheManager ssmCacheManager = new ExtendedSSMCacheManager(); + ssmCacheManager.setCaches(ssmCaches); + + return ssmCacheManager; + } + + private CacheFactory getCacheFactory() { + CacheConfiguration cacheConfiguration = new CacheConfiguration(); + cacheConfiguration.setConsistentHashing(true); + cacheConfiguration.setUseBinaryProtocol(true); + cacheConfiguration.setOperationTimeout(Integer.getInteger(operationTimeout)); + cacheConfiguration.setUseNameAsKeyPrefix(true); + cacheConfiguration.setKeyPrefixSeparator(":"); + + MemcacheClientFactoryImpl cacheClientFactory = new MemcacheClientFactoryImpl(); + + AddressProvider addressProvider = new DefaultAddressProvider(address); + + CacheFactory cacheFactory = new CacheFactory(); + cacheFactory.setCacheName(cacheName); + cacheFactory.setCacheClientFactory(cacheClientFactory); + cacheFactory.setAddressProvider(addressProvider); + cacheFactory.setConfiguration(cacheConfiguration); + return cacheFactory; + } +} \ No newline at end of file diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/config/cache/TokenHasher.java b/Backend/src/main/java/kimjang/toolkit/solsol/config/cache/TokenHasher.java new file mode 100644 index 0000000..18e64ac --- /dev/null +++ b/Backend/src/main/java/kimjang/toolkit/solsol/config/cache/TokenHasher.java @@ -0,0 +1,27 @@ +package kimjang.toolkit.solsol.config.cache; + +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +@Component +public class TokenHasher{ + public String encode(String token){ + MessageDigest digest = null; + try { + digest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + byte[] encodedhash = digest.digest(token.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(encodedhash); + } + + public boolean matches(String rawToken, String hashedToken) { + String hashedRawToken = encode(rawToken); + return hashedRawToken.equals(hashedToken); + } +} diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/config/handler/StompHandler.java b/Backend/src/main/java/kimjang/toolkit/solsol/config/handler/StompHandler.java index 222a28c..17ac705 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/config/handler/StompHandler.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/config/handler/StompHandler.java @@ -1,7 +1,7 @@ package kimjang.toolkit.solsol.config.handler; -import kimjang.toolkit.solsol.room.service.ChatRoomSubscribeService; +import kimjang.toolkit.solsol.domain.room.service.ChatRoomSubscribeService; import kimjang.toolkit.solsol.config.container.SocketSessionContainer; import kimjang.toolkit.solsol.config.jwt.SecurityConstants; import kimjang.toolkit.solsol.config.provider.JwtAuthenticationProvider; @@ -9,12 +9,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.support.ChannelInterceptor; -import org.springframework.security.authentication.BadCredentialsException; -import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/config/provider/CustomUserNamePwdAuthenticationProvider.java b/Backend/src/main/java/kimjang/toolkit/solsol/config/provider/CustomUserNamePwdAuthenticationProvider.java index e3e4313..e6015d1 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/config/provider/CustomUserNamePwdAuthenticationProvider.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/config/provider/CustomUserNamePwdAuthenticationProvider.java @@ -1,8 +1,8 @@ package kimjang.toolkit.solsol.config.provider; -import kimjang.toolkit.solsol.user.entities.Authority; -import kimjang.toolkit.solsol.user.entities.User; -import kimjang.toolkit.solsol.user.reposiotry.UserRepository; +import kimjang.toolkit.solsol.domain.user.entities.Authority; +import kimjang.toolkit.solsol.domain.user.entities.User; +import kimjang.toolkit.solsol.domain.user.reposiotry.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/domain/cache/RefreshToken.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/cache/RefreshToken.java new file mode 100644 index 0000000..c6a7a07 --- /dev/null +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/cache/RefreshToken.java @@ -0,0 +1,28 @@ +package kimjang.toolkit.solsol.domain.cache; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RefreshToken { + @Id + @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") + @Column(name = "token_id") // 컬럼의 이름을 바꿀 수 있다. + private Long id; + + private String email; + + @Column(name = "refresh_token") + private String refreshToken; + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } +} diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/domain/cache/RefreshTokenCacheService.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/cache/RefreshTokenCacheService.java new file mode 100644 index 0000000..77a0be4 --- /dev/null +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/cache/RefreshTokenCacheService.java @@ -0,0 +1,55 @@ +package kimjang.toolkit.solsol.domain.cache; + +import kimjang.toolkit.solsol.exception.UnauthorizedException; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class RefreshTokenCacheService { + + private final RefreshTokenRepository refreshTokenRepository; + + /** + * CachePut은 캐시가 있더라도 메서드를 실행한 결과를 Cache에 저장 + * 따라서 무조건 새로운 token을 저장하고 싶은 경우 적절 + * 한 메일에 한 refresh token만 가능! + * + * @param email + * @param hashedRefreshToken : hashing된 refresh token + * @return + */ + @CachePut(value= "refresh", key="#email") + public String saveToken(String email, String hashedRefreshToken){ + Optional token = refreshTokenRepository.findByEmail(email); + if(token.isPresent()){ // 예전에 로그인해서 refresh token이 남아 있는 경우 업데이트 + token.get().setRefreshToken(hashedRefreshToken); + return refreshTokenRepository.save(token.get()).getRefreshToken(); + } else{ // 처음 refresh token 만들어서 저장하는 경우 + RefreshToken newToken = RefreshToken.builder() + .email(email) + .refreshToken(hashedRefreshToken) + .build(); + return refreshTokenRepository.save(newToken).getRefreshToken(); + } + } + + @CacheEvict(value = "refresh", key = "#email") + public void deleteCache(String email){} + + /** + * 재발급 용도로 저장된 token 검색 + * @param email + * @return + */ + @Cacheable(value= "refresh") + public String searchToken(String email){ + return refreshTokenRepository.findByEmail(email) + .orElseThrow(()-> new UnauthorizedException("인증되지 않았습니다. 다시 시도해주세요.")).getRefreshToken(); + } +} diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/domain/cache/RefreshTokenRepository.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/cache/RefreshTokenRepository.java new file mode 100644 index 0000000..5f3dc36 --- /dev/null +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/cache/RefreshTokenRepository.java @@ -0,0 +1,11 @@ +package kimjang.toolkit.solsol.domain.cache; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface RefreshTokenRepository extends JpaRepository { + Optional findByEmail(String email); +} diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/message/ChatMessage.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/ChatMessage.java similarity index 85% rename from Backend/src/main/java/kimjang/toolkit/solsol/message/ChatMessage.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/message/ChatMessage.java index cf2e979..b836d1b 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/message/ChatMessage.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/ChatMessage.java @@ -1,9 +1,9 @@ -package kimjang.toolkit.solsol.message; +package kimjang.toolkit.solsol.domain.message; import jakarta.persistence.*; -import kimjang.toolkit.solsol.user.entities.User; -import kimjang.toolkit.solsol.message.dto.SendChatMessageDto; -import kimjang.toolkit.solsol.room.entity.ChatRoom; +import kimjang.toolkit.solsol.domain.message.dto.SendChatMessageDto; +import kimjang.toolkit.solsol.domain.room.entity.ChatRoom; +import kimjang.toolkit.solsol.domain.user.entities.User; import lombok.*; import java.time.LocalDateTime; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/message/Greeting.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/Greeting.java similarity index 80% rename from Backend/src/main/java/kimjang/toolkit/solsol/message/Greeting.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/message/Greeting.java index ce71e2a..62dd4d5 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/message/Greeting.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/Greeting.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.message; +package kimjang.toolkit.solsol.domain.message; public class Greeting { diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/message/GreetingController.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/GreetingController.java similarity index 77% rename from Backend/src/main/java/kimjang/toolkit/solsol/message/GreetingController.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/message/GreetingController.java index 2afccad..00dc0ad 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/message/GreetingController.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/GreetingController.java @@ -1,12 +1,8 @@ -package kimjang.toolkit.solsol.message; +package kimjang.toolkit.solsol.domain.message; -import org.springframework.http.ResponseEntity; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.util.HtmlUtils; @Controller public class GreetingController { diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/message/HelloMessage.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/HelloMessage.java similarity index 84% rename from Backend/src/main/java/kimjang/toolkit/solsol/message/HelloMessage.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/message/HelloMessage.java index 605b2c5..b4a3efb 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/message/HelloMessage.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/HelloMessage.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.message; +package kimjang.toolkit.solsol.domain.message; public class HelloMessage { diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/message/controllor/ChatController.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/controllor/ChatController.java similarity index 84% rename from Backend/src/main/java/kimjang/toolkit/solsol/message/controllor/ChatController.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/message/controllor/ChatController.java index 97a1c0c..1bcc93b 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/message/controllor/ChatController.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/controllor/ChatController.java @@ -1,19 +1,15 @@ -package kimjang.toolkit.solsol.message.controllor; +package kimjang.toolkit.solsol.domain.message.controllor; -import kimjang.toolkit.solsol.message.dto.PastChatsDto; -import kimjang.toolkit.solsol.message.dto.ReqPastChatsDto; -import kimjang.toolkit.solsol.message.service.ChatService; +import kimjang.toolkit.solsol.domain.message.dto.ReqPastChatsDto; +import kimjang.toolkit.solsol.domain.message.service.ChatService; +import kimjang.toolkit.solsol.domain.message.dto.PastChatsDto; import lombok.RequiredArgsConstructor; -import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.time.LocalDateTime; -import java.util.Date; - @RestController @RequiredArgsConstructor public class ChatController { diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/message/dto/GetChatProj.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/GetChatProj.java similarity index 74% rename from Backend/src/main/java/kimjang/toolkit/solsol/message/dto/GetChatProj.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/GetChatProj.java index 1514538..3d8c228 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/message/dto/GetChatProj.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/GetChatProj.java @@ -1,6 +1,6 @@ -package kimjang.toolkit.solsol.message.dto; +package kimjang.toolkit.solsol.domain.message.dto; -import kimjang.toolkit.solsol.user.dto.UserDto; +import kimjang.toolkit.solsol.domain.user.dto.UserDto; import java.time.LocalDateTime; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/message/dto/PastChatsDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/PastChatsDto.java similarity index 94% rename from Backend/src/main/java/kimjang/toolkit/solsol/message/dto/PastChatsDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/PastChatsDto.java index 87e864b..ab1ded8 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/message/dto/PastChatsDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/PastChatsDto.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.message.dto; +package kimjang.toolkit.solsol.domain.message.dto; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/message/dto/ReqPastChatsDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/ReqPastChatsDto.java similarity index 94% rename from Backend/src/main/java/kimjang/toolkit/solsol/message/dto/ReqPastChatsDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/ReqPastChatsDto.java index 507b83e..c56aa28 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/message/dto/ReqPastChatsDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/ReqPastChatsDto.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.message.dto; +package kimjang.toolkit.solsol.domain.message.dto; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/message/dto/SendChatMessageDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/SendChatMessageDto.java similarity index 94% rename from Backend/src/main/java/kimjang/toolkit/solsol/message/dto/SendChatMessageDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/SendChatMessageDto.java index edac1ff..97e6620 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/message/dto/SendChatMessageDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/dto/SendChatMessageDto.java @@ -1,9 +1,9 @@ -package kimjang.toolkit.solsol.message.dto; +package kimjang.toolkit.solsol.domain.message.dto; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.Nulls; -import kimjang.toolkit.solsol.user.dto.UserDto; +import kimjang.toolkit.solsol.domain.user.dto.UserDto; import lombok.*; import java.time.LocalDateTime; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/message/repository/ChatRepository.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/repository/ChatRepository.java similarity index 88% rename from Backend/src/main/java/kimjang/toolkit/solsol/message/repository/ChatRepository.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/message/repository/ChatRepository.java index 3c48083..73ed57e 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/message/repository/ChatRepository.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/repository/ChatRepository.java @@ -1,7 +1,7 @@ -package kimjang.toolkit.solsol.message.repository; +package kimjang.toolkit.solsol.domain.message.repository; -import kimjang.toolkit.solsol.message.ChatMessage; -import kimjang.toolkit.solsol.message.dto.SendChatMessageDto; +import kimjang.toolkit.solsol.domain.message.ChatMessage; +import kimjang.toolkit.solsol.domain.message.dto.SendChatMessageDto; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; @@ -10,7 +10,6 @@ import org.springframework.stereotype.Repository; import java.util.List; -import java.util.Optional; @Repository public interface ChatRepository extends JpaRepository { @@ -29,7 +28,7 @@ public interface ChatRepository extends JpaRepository { * @Param roomExitTime : 사용자가 최근 방에서 나간 시간 */ @Query(value="SELECT " + - " new kimjang.toolkit.solsol.message.dto.SendChatMessageDto(" + + " new kimjang.toolkit.solsol.domain.message.dto.SendChatMessageDto(" + " :roomId, cm.content, cm.createDate, u.id, u.email, u.name )" + // 채팅 내용 " FROM ChatMessage cm " + // 채팅 " join User u ON u.id = cm.user.id " + // 유저 @@ -46,7 +45,7 @@ public interface ChatRepository extends JpaRepository { // 유저가 속한 채팅방에 가장 최근 채팅만 쿼리 @Query(value="SELECT " + - " new kimjang.toolkit.solsol.message.dto.SendChatMessageDto(" + + " new kimjang.toolkit.solsol.domain.message.dto.SendChatMessageDto(" + " :roomId, cm.content, cm.createDate, u.id, u.email, u.name )" + // 채팅 내용 " FROM ChatMessage cm " + // 채팅 " join User u ON u.id = cm.user.id " + // 유저 diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/message/service/ChatService.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/service/ChatService.java similarity index 74% rename from Backend/src/main/java/kimjang/toolkit/solsol/message/service/ChatService.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/message/service/ChatService.java index 1c45a1f..a233b3c 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/message/service/ChatService.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/message/service/ChatService.java @@ -1,14 +1,14 @@ -package kimjang.toolkit.solsol.message.service; +package kimjang.toolkit.solsol.domain.message.service; -import kimjang.toolkit.solsol.user.entities.User; -import kimjang.toolkit.solsol.user.reposiotry.UserRepository; -import kimjang.toolkit.solsol.message.ChatMessage; -import kimjang.toolkit.solsol.message.dto.PastChatsDto; -import kimjang.toolkit.solsol.message.dto.ReqPastChatsDto; -import kimjang.toolkit.solsol.message.dto.SendChatMessageDto; -import kimjang.toolkit.solsol.message.repository.ChatRepository; -import kimjang.toolkit.solsol.room.entity.ChatRoom; -import kimjang.toolkit.solsol.room.repository.ChatRoomRepository; +import kimjang.toolkit.solsol.domain.message.dto.ReqPastChatsDto; +import kimjang.toolkit.solsol.domain.message.repository.ChatRepository; +import kimjang.toolkit.solsol.domain.room.entity.ChatRoom; +import kimjang.toolkit.solsol.domain.room.repository.ChatRoomRepository; +import kimjang.toolkit.solsol.domain.user.entities.User; +import kimjang.toolkit.solsol.domain.user.reposiotry.UserRepository; +import kimjang.toolkit.solsol.domain.message.ChatMessage; +import kimjang.toolkit.solsol.domain.message.dto.PastChatsDto; +import kimjang.toolkit.solsol.domain.message.dto.SendChatMessageDto; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Slice; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/ChatRoomController.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/ChatRoomController.java similarity index 83% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/ChatRoomController.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/ChatRoomController.java index 840d4f5..1950aa7 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/ChatRoomController.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/ChatRoomController.java @@ -1,10 +1,13 @@ -package kimjang.toolkit.solsol.room; +package kimjang.toolkit.solsol.domain.room; -import kimjang.toolkit.solsol.room.dto.*; -import kimjang.toolkit.solsol.message.dto.SendChatMessageDto; -import kimjang.toolkit.solsol.room.service.ChatRoomService; -import kimjang.toolkit.solsol.room.service.ChatRoomStompService; -import kimjang.toolkit.solsol.message.service.ChatService; +import kimjang.toolkit.solsol.domain.room.dto.ChatRoomDto; +import kimjang.toolkit.solsol.domain.room.dto.CreateChatRoomDto; +import kimjang.toolkit.solsol.domain.room.dto.InviteChatRoomDto; +import kimjang.toolkit.solsol.domain.room.dto.LeaveRoomDto; +import kimjang.toolkit.solsol.domain.message.dto.SendChatMessageDto; +import kimjang.toolkit.solsol.domain.room.service.ChatRoomService; +import kimjang.toolkit.solsol.domain.room.service.ChatRoomStompService; +import kimjang.toolkit.solsol.domain.message.service.ChatService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/ChatRoomDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/ChatRoomDto.java similarity index 92% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/dto/ChatRoomDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/ChatRoomDto.java index acbea4f..8bf8522 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/ChatRoomDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/ChatRoomDto.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.room.dto; +package kimjang.toolkit.solsol.domain.room.dto; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/CreateChatRoomDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/CreateChatRoomDto.java similarity index 78% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/dto/CreateChatRoomDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/CreateChatRoomDto.java index 7a163a3..093a430 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/CreateChatRoomDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/CreateChatRoomDto.java @@ -1,6 +1,5 @@ -package kimjang.toolkit.solsol.room.dto; +package kimjang.toolkit.solsol.domain.room.dto; -import kimjang.toolkit.solsol.user.dto.UserDto; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/CreateRoomReqDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/CreateRoomReqDto.java similarity index 86% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/dto/CreateRoomReqDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/CreateRoomReqDto.java index e51b470..00cc039 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/CreateRoomReqDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/CreateRoomReqDto.java @@ -1,6 +1,6 @@ -package kimjang.toolkit.solsol.room.dto; +package kimjang.toolkit.solsol.domain.room.dto; -import kimjang.toolkit.solsol.user.dto.UserDto; +import kimjang.toolkit.solsol.domain.user.dto.UserDto; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/InviteChatRoomDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/InviteChatRoomDto.java similarity index 86% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/dto/InviteChatRoomDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/InviteChatRoomDto.java index 96ade9d..ad98f66 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/InviteChatRoomDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/InviteChatRoomDto.java @@ -1,6 +1,6 @@ -package kimjang.toolkit.solsol.room.dto; +package kimjang.toolkit.solsol.domain.room.dto; -import kimjang.toolkit.solsol.user.dto.UserDto; +import kimjang.toolkit.solsol.domain.user.dto.UserDto; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/LastChatDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/LastChatDto.java similarity index 85% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/dto/LastChatDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/LastChatDto.java index a7dfb45..6528b51 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/LastChatDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/LastChatDto.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.room.dto; +package kimjang.toolkit.solsol.domain.room.dto; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/LeaveRoomDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/LeaveRoomDto.java similarity index 68% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/dto/LeaveRoomDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/LeaveRoomDto.java index 6dcefca..d0a77f7 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/LeaveRoomDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/LeaveRoomDto.java @@ -1,6 +1,6 @@ -package kimjang.toolkit.solsol.room.dto; +package kimjang.toolkit.solsol.domain.room.dto; -import kimjang.toolkit.solsol.user.dto.UserDto; +import kimjang.toolkit.solsol.domain.user.dto.UserDto; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/ReqChatRoomMetaDTO.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/ReqChatRoomMetaDTO.java similarity index 68% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/dto/ReqChatRoomMetaDTO.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/ReqChatRoomMetaDTO.java index a8fe308..bab8ecc 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/dto/ReqChatRoomMetaDTO.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/dto/ReqChatRoomMetaDTO.java @@ -1,6 +1,6 @@ -package kimjang.toolkit.solsol.room.dto; +package kimjang.toolkit.solsol.domain.room.dto; -import kimjang.toolkit.solsol.user.dto.UserDto; +import kimjang.toolkit.solsol.domain.user.dto.UserDto; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/entity/ChatRoom.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/entity/ChatRoom.java similarity index 95% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/entity/ChatRoom.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/entity/ChatRoom.java index 98eee37..a0027c0 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/entity/ChatRoom.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/entity/ChatRoom.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.room.entity; +package kimjang.toolkit.solsol.domain.room.entity; import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.persistence.*; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/entity/ChatRoomCustomerRelationship.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/entity/ChatRoomCustomerRelationship.java similarity index 87% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/entity/ChatRoomCustomerRelationship.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/entity/ChatRoomCustomerRelationship.java index 6a2a9e1..3ed5d9a 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/entity/ChatRoomCustomerRelationship.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/entity/ChatRoomCustomerRelationship.java @@ -1,7 +1,7 @@ -package kimjang.toolkit.solsol.room.entity; +package kimjang.toolkit.solsol.domain.room.entity; import jakarta.persistence.*; -import kimjang.toolkit.solsol.user.entities.User; +import kimjang.toolkit.solsol.domain.user.entities.User; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/repository/ChatRoomCustormerRelationshipRepository.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/repository/ChatRoomCustormerRelationshipRepository.java similarity index 78% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/repository/ChatRoomCustormerRelationshipRepository.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/repository/ChatRoomCustormerRelationshipRepository.java index b4b5cab..07cbb26 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/repository/ChatRoomCustormerRelationshipRepository.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/repository/ChatRoomCustormerRelationshipRepository.java @@ -1,6 +1,6 @@ -package kimjang.toolkit.solsol.room.repository; +package kimjang.toolkit.solsol.domain.room.repository; -import kimjang.toolkit.solsol.room.entity.ChatRoomCustomerRelationship; +import kimjang.toolkit.solsol.domain.room.entity.ChatRoomCustomerRelationship; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/repository/ChatRoomRepository.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/repository/ChatRoomRepository.java similarity index 84% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/repository/ChatRoomRepository.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/repository/ChatRoomRepository.java index a2a2257..2ac7249 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/repository/ChatRoomRepository.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/repository/ChatRoomRepository.java @@ -1,7 +1,7 @@ -package kimjang.toolkit.solsol.room.repository; +package kimjang.toolkit.solsol.domain.room.repository; -import kimjang.toolkit.solsol.room.dto.ChatRoomDto; -import kimjang.toolkit.solsol.room.entity.ChatRoom; +import kimjang.toolkit.solsol.domain.room.dto.ChatRoomDto; +import kimjang.toolkit.solsol.domain.room.entity.ChatRoom; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -10,7 +10,7 @@ public interface ChatRoomRepository extends JpaRepository { - @Query("SELECT new kimjang.toolkit.solsol.room.dto.ChatRoomDto( " + + @Query("SELECT new kimjang.toolkit.solsol.domain.room.dto.ChatRoomDto( " + " cr.id, ccr.roomName, cr.memberCnt, ccr.roomExitTime, " + " (SELECT cm.content FROM ChatMessage cm WHERE cm.chatRoom.id = cr.id AND " + // 채팅 내용 가져오기 " cm.createDate =(SELECT MAX(ccm.createDate) FROM ChatMessage ccm WHERE ccm.chatRoom.id = cm.chatRoom.id)) " + // 채팅방에서 가장 최근 채팅 날짜 diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/service/ChatRoomService.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/ChatRoomService.java similarity index 84% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/service/ChatRoomService.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/ChatRoomService.java index 317cd12..b2f454c 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/service/ChatRoomService.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/ChatRoomService.java @@ -1,18 +1,18 @@ -package kimjang.toolkit.solsol.room.service; - -import kimjang.toolkit.solsol.room.dto.InviteChatRoomDto; -import kimjang.toolkit.solsol.room.dto.LeaveRoomDto; -import kimjang.toolkit.solsol.user.entities.User; -import kimjang.toolkit.solsol.user.reposiotry.UserRepository; -import kimjang.toolkit.solsol.user.dto.UserDto; -import kimjang.toolkit.solsol.message.ChatMessage; -import kimjang.toolkit.solsol.message.repository.ChatRepository; -import kimjang.toolkit.solsol.room.dto.ChatRoomDto; -import kimjang.toolkit.solsol.room.entity.ChatRoom; -import kimjang.toolkit.solsol.room.entity.ChatRoomCustomerRelationship; -import kimjang.toolkit.solsol.room.dto.CreateChatRoomDto; -import kimjang.toolkit.solsol.room.repository.ChatRoomCustormerRelationshipRepository; -import kimjang.toolkit.solsol.room.repository.ChatRoomRepository; +package kimjang.toolkit.solsol.domain.room.service; + +import kimjang.toolkit.solsol.domain.room.dto.ChatRoomDto; +import kimjang.toolkit.solsol.domain.room.dto.InviteChatRoomDto; +import kimjang.toolkit.solsol.domain.room.entity.ChatRoomCustomerRelationship; +import kimjang.toolkit.solsol.domain.room.repository.ChatRoomRepository; +import kimjang.toolkit.solsol.domain.user.entities.User; +import kimjang.toolkit.solsol.domain.room.dto.LeaveRoomDto; +import kimjang.toolkit.solsol.domain.user.reposiotry.UserRepository; +import kimjang.toolkit.solsol.domain.user.dto.UserDto; +import kimjang.toolkit.solsol.domain.message.ChatMessage; +import kimjang.toolkit.solsol.domain.message.repository.ChatRepository; +import kimjang.toolkit.solsol.domain.room.entity.ChatRoom; +import kimjang.toolkit.solsol.domain.room.dto.CreateChatRoomDto; +import kimjang.toolkit.solsol.domain.room.repository.ChatRoomCustormerRelationshipRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/service/ChatRoomStompService.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/ChatRoomStompService.java similarity index 77% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/service/ChatRoomStompService.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/ChatRoomStompService.java index 88635bb..4f953d0 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/service/ChatRoomStompService.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/ChatRoomStompService.java @@ -1,19 +1,14 @@ -package kimjang.toolkit.solsol.room.service; +package kimjang.toolkit.solsol.domain.room.service; -import kimjang.toolkit.solsol.room.dto.CreateChatRoomDto; -import kimjang.toolkit.solsol.room.dto.CreateRoomReqDto; -import kimjang.toolkit.solsol.room.dto.InviteChatRoomDto; +import kimjang.toolkit.solsol.domain.room.dto.InviteChatRoomDto; +import kimjang.toolkit.solsol.domain.room.dto.CreateRoomReqDto; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.messaging.simp.SimpMessagingTemplate; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; - -import static kimjang.toolkit.solsol.room.service.CreateRoomName.withParticipationsName; @Slf4j @Service diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/service/ChatRoomSubscribeService.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/ChatRoomSubscribeService.java similarity index 75% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/service/ChatRoomSubscribeService.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/ChatRoomSubscribeService.java index 92c6fc1..1cde53e 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/service/ChatRoomSubscribeService.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/ChatRoomSubscribeService.java @@ -1,7 +1,7 @@ -package kimjang.toolkit.solsol.room.service; +package kimjang.toolkit.solsol.domain.room.service; -import kimjang.toolkit.solsol.room.entity.ChatRoomCustomerRelationship; -import kimjang.toolkit.solsol.room.repository.ChatRoomCustormerRelationshipRepository; +import kimjang.toolkit.solsol.domain.room.entity.ChatRoomCustomerRelationship; +import kimjang.toolkit.solsol.domain.room.repository.ChatRoomCustormerRelationshipRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/room/service/CreateRoomName.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/CreateRoomName.java similarity index 89% rename from Backend/src/main/java/kimjang/toolkit/solsol/room/service/CreateRoomName.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/CreateRoomName.java index 67152a8..0d03a0e 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/room/service/CreateRoomName.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/room/service/CreateRoomName.java @@ -1,9 +1,6 @@ -package kimjang.toolkit.solsol.room.service; +package kimjang.toolkit.solsol.domain.room.service; -import kimjang.toolkit.solsol.user.dto.UserDto; -import kimjang.toolkit.solsol.room.dto.CreateChatRoomDto; - -import java.util.List; +import kimjang.toolkit.solsol.domain.room.dto.CreateChatRoomDto; public class CreateRoomName { diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/controller/UserController.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/controller/UserController.java similarity index 83% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/controller/UserController.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/controller/UserController.java index 324e08b..44df1bc 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/controller/UserController.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/controller/UserController.java @@ -1,9 +1,8 @@ -package kimjang.toolkit.solsol.user.controller; +package kimjang.toolkit.solsol.domain.user.controller; -import jakarta.servlet.http.HttpServletResponse; -import kimjang.toolkit.solsol.user.dto.*; -import kimjang.toolkit.solsol.user.service.FriendService; -import kimjang.toolkit.solsol.user.service.UserService; +import kimjang.toolkit.solsol.domain.user.dto.*; +import kimjang.toolkit.solsol.domain.user.service.FriendService; +import kimjang.toolkit.solsol.domain.user.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -11,11 +10,8 @@ import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; -import java.net.http.HttpResponse; import java.util.List; -import static kimjang.toolkit.solsol.config.jwt.SecurityConstants.JWT_REFRESH_HEADER; - @Slf4j @RestController @RequiredArgsConstructor @@ -68,7 +64,7 @@ public ResponseEntity getUserDetail(){ } @PostMapping("/refresh-token") - public LoginSuccessDto getAccessTokenByRefreshToken(@RequestBody RefreshDto refreshDto){ + public IssuedTokens getAccessTokenByRefreshToken(@RequestBody RefreshDto refreshDto){ return userService.getAccessToken(refreshDto); } } diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/AddFriendsDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/AddFriendsDto.java similarity index 82% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/dto/AddFriendsDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/AddFriendsDto.java index 585c41f..57ed5bf 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/AddFriendsDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/AddFriendsDto.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.user.dto; +package kimjang.toolkit.solsol.domain.user.dto; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/AuthorityInfo.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/AuthorityInfo.java new file mode 100644 index 0000000..58596ca --- /dev/null +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/AuthorityInfo.java @@ -0,0 +1,15 @@ +package kimjang.toolkit.solsol.domain.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AuthorityInfo { + private String email; + private String authorities; +} diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/CreateUserDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/CreateUserDto.java similarity index 92% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/dto/CreateUserDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/CreateUserDto.java index c511fa1..9b3561d 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/CreateUserDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/CreateUserDto.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.user.dto; +package kimjang.toolkit.solsol.domain.user.dto; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/IssuedTokens.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/IssuedTokens.java new file mode 100644 index 0000000..7d7c669 --- /dev/null +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/IssuedTokens.java @@ -0,0 +1,15 @@ +package kimjang.toolkit.solsol.domain.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class IssuedTokens { + private String accessToken; + private String refreshToken; +} diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/LoginDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/LoginDto.java similarity index 81% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/dto/LoginDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/LoginDto.java index d40624c..e17de22 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/LoginDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/LoginDto.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.user.dto; +package kimjang.toolkit.solsol.domain.user.dto; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/LoginSuccessDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/LoginSuccessDto.java similarity index 92% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/dto/LoginSuccessDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/LoginSuccessDto.java index fa58d63..d66ebab 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/LoginSuccessDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/LoginSuccessDto.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.user.dto; +package kimjang.toolkit.solsol.domain.user.dto; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/RefreshDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/RefreshDto.java new file mode 100644 index 0000000..0c819dc --- /dev/null +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/RefreshDto.java @@ -0,0 +1,12 @@ +package kimjang.toolkit.solsol.domain.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class RefreshDto { + private String refreshToken; +} diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/UserDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/UserDto.java similarity index 88% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/dto/UserDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/UserDto.java index 4aa28fe..a1ce41d 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/UserDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/UserDto.java @@ -1,8 +1,8 @@ -package kimjang.toolkit.solsol.user.dto; +package kimjang.toolkit.solsol.domain.user.dto; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; -import kimjang.toolkit.solsol.user.entities.User; +import kimjang.toolkit.solsol.domain.user.entities.User; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/UserProfileDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/UserProfileDto.java similarity index 91% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/dto/UserProfileDto.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/UserProfileDto.java index c6af763..8b49c93 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/UserProfileDto.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/dto/UserProfileDto.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.user.dto; +package kimjang.toolkit.solsol.domain.user.dto; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/entities/Authority.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/entities/Authority.java similarity index 92% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/entities/Authority.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/entities/Authority.java index f111299..b0e861d 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/entities/Authority.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/entities/Authority.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.user.entities; +package kimjang.toolkit.solsol.domain.user.entities; import jakarta.persistence.*; import lombok.AllArgsConstructor; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/entities/Friend.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/entities/Friend.java similarity index 94% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/entities/Friend.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/entities/Friend.java index 8b734c1..49884b3 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/entities/Friend.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/entities/Friend.java @@ -1,4 +1,4 @@ -package kimjang.toolkit.solsol.user.entities; +package kimjang.toolkit.solsol.domain.user.entities; import jakarta.persistence.*; import lombok.AllArgsConstructor; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/entities/User.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/entities/User.java similarity index 94% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/entities/User.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/entities/User.java index 32f28ad..f992882 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/entities/User.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/entities/User.java @@ -1,7 +1,7 @@ -package kimjang.toolkit.solsol.user.entities; +package kimjang.toolkit.solsol.domain.user.entities; import jakarta.persistence.*; -import kimjang.toolkit.solsol.user.dto.CreateUserDto; +import kimjang.toolkit.solsol.domain.user.dto.CreateUserDto; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/reposiotry/FriendRepository.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/reposiotry/FriendRepository.java similarity index 70% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/reposiotry/FriendRepository.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/reposiotry/FriendRepository.java index c42659a..246421c 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/reposiotry/FriendRepository.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/reposiotry/FriendRepository.java @@ -1,7 +1,7 @@ -package kimjang.toolkit.solsol.user.reposiotry; +package kimjang.toolkit.solsol.domain.user.reposiotry; -import kimjang.toolkit.solsol.user.entities.Friend; -import kimjang.toolkit.solsol.user.dto.UserProfileDto; +import kimjang.toolkit.solsol.domain.user.dto.UserProfileDto; +import kimjang.toolkit.solsol.domain.user.entities.Friend; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -10,7 +10,7 @@ public interface FriendRepository extends JpaRepository { - @Query(value = "SELECT distinct new kimjang.toolkit.solsol.user.dto.UserProfileDto( " + + @Query(value = "SELECT distinct new kimjang.toolkit.solsol.domain.user.dto.UserProfileDto( " + " f.subUser.id, f.subUser.email, f.subUser.name, f.subUser.imgUrl ) FROM Friend f " + " JOIN User u ON f.user = u" + " WHERE u.email = :email") // 이메일이 email인 유저가 main인 f만 존재~ diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/reposiotry/UserRepository.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/reposiotry/UserRepository.java similarity index 72% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/reposiotry/UserRepository.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/reposiotry/UserRepository.java index 9c8cef6..612d03f 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/reposiotry/UserRepository.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/reposiotry/UserRepository.java @@ -1,7 +1,7 @@ -package kimjang.toolkit.solsol.user.reposiotry; +package kimjang.toolkit.solsol.domain.user.reposiotry; -import kimjang.toolkit.solsol.user.entities.User; -import kimjang.toolkit.solsol.user.dto.UserProfileDto; +import kimjang.toolkit.solsol.domain.user.dto.UserProfileDto; +import kimjang.toolkit.solsol.domain.user.entities.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -15,7 +15,7 @@ public interface UserRepository extends JpaRepository { @Query(value = "select u from User u join FETCH u.authorities a WHERE u.email = :email") Optional findByEmail(@Param("email") String email); - @Query(value = "select new kimjang.toolkit.solsol.user.dto.UserProfileDto(" + + @Query(value = "select new kimjang.toolkit.solsol.domain.user.dto.UserProfileDto(" + " u.id, u.email, u.name, u.imgUrl ) from User u WHERE u.email = :email") UserProfileDto findProfileByEmail(@Param("email") String email); diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/service/FriendService.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/service/FriendService.java similarity index 78% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/service/FriendService.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/service/FriendService.java index 94ddd95..a82d402 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/service/FriendService.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/service/FriendService.java @@ -1,11 +1,11 @@ -package kimjang.toolkit.solsol.user.service; +package kimjang.toolkit.solsol.domain.user.service; -import kimjang.toolkit.solsol.user.entities.Friend; -import kimjang.toolkit.solsol.user.entities.User; -import kimjang.toolkit.solsol.user.reposiotry.FriendRepository; -import kimjang.toolkit.solsol.user.reposiotry.UserRepository; -import kimjang.toolkit.solsol.user.dto.AddFriendsDto; -import kimjang.toolkit.solsol.user.dto.UserProfileDto; +import kimjang.toolkit.solsol.domain.user.dto.AddFriendsDto; +import kimjang.toolkit.solsol.domain.user.dto.UserProfileDto; +import kimjang.toolkit.solsol.domain.user.entities.Friend; +import kimjang.toolkit.solsol.domain.user.entities.User; +import kimjang.toolkit.solsol.domain.user.reposiotry.FriendRepository; +import kimjang.toolkit.solsol.domain.user.reposiotry.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/service/JwtIssuer.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/service/JwtIssuer.java new file mode 100644 index 0000000..8d252ff --- /dev/null +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/service/JwtIssuer.java @@ -0,0 +1,143 @@ +package kimjang.toolkit.solsol.domain.user.service; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; +import kimjang.toolkit.solsol.config.cache.TokenHasher; +import kimjang.toolkit.solsol.config.jwt.JwtInvalidException; +import kimjang.toolkit.solsol.config.jwt.SecurityConstants; +import kimjang.toolkit.solsol.domain.cache.RefreshToken; +import kimjang.toolkit.solsol.domain.cache.RefreshTokenCacheService; +import kimjang.toolkit.solsol.domain.user.dto.AuthorityInfo; +import kimjang.toolkit.solsol.domain.user.dto.IssuedTokens; +import kimjang.toolkit.solsol.domain.user.dto.RefreshDto; +import kimjang.toolkit.solsol.domain.user.entities.Authority; +import kimjang.toolkit.solsol.exception.UnauthorizedException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; +import java.util.Set; + +@Slf4j +@Component +public class JwtIssuer { + private final SecretKey key = Keys.hmacShaKeyFor(SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8)); + private final Long ONE_SECONDS = 1000L; + private final Long ONE_MINUTE = 60 * ONE_SECONDS; + private final long accessExpireMin; + private final long refreshExpireMin; + private final RefreshTokenCacheService refreshTokenCacheService; + private final TokenHasher tokenHasher; + + public JwtIssuer(@Value("${jwt.access-expire-min:10}") long accessExpireMin, + @Value("${jwt.refresh-expire-min:60}") long refreshExpireMin, + RefreshTokenCacheService refreshTokenCacheService, + TokenHasher tokenHasher){ + this.accessExpireMin = accessExpireMin; + this.refreshExpireMin = refreshExpireMin; + this.refreshTokenCacheService = refreshTokenCacheService; + this.tokenHasher = tokenHasher; + } + + /** + * refresh token 재발급 시 호출되는 메서드! + * 요청된 refresh token을 바탕으로 email을 찾고 + * 새로운 tokens를 발급하고 캐시와 DB에 저장! + * @param refreshDto + * @return + */ + public IssuedTokens getAccessTokenByRefreshToken(RefreshDto refreshDto) { + try { + AuthorityInfo authorityInfo = getAuthorityInfo(refreshDto); + // cache hit 저장된 토큰 그대로 가져옴 + String savedRefreshToken = refreshTokenCacheService.searchToken(authorityInfo.getEmail()); + // 저장된 refresh token과 같은지 확인 같을 때 재발급 가능! + hashMatchingTokens(refreshDto.getRefreshToken(), savedRefreshToken); + + // 기존 토큰 제거하기 + refreshTokenCacheService.deleteCache(authorityInfo.getEmail()); + // 새로운 토큰 재발급 + IssuedTokens issuedTokens = getAccessTokenByAuthorityInfo(authorityInfo); + // 새로운 토큰 저장하기 + saveIssuedToken(authorityInfo.getEmail(), issuedTokens.getRefreshToken()); + return issuedTokens; + + } catch (SignatureException signatureException) { + throw new JwtInvalidException("signature key is different", signatureException); + } catch (ExpiredJwtException expiredJwtException) { + throw new JwtInvalidException("expired token", expiredJwtException); + } catch (MalformedJwtException malformedJwtException) { + throw new JwtInvalidException("malformed token", malformedJwtException); + } catch (IllegalArgumentException illegalArgumentException) { + throw new JwtInvalidException("using illegal argument like null", illegalArgumentException); + } + } + + private void hashMatchingTokens(String refreshToken, String savedRefreshToken){ + // hash match check + if(!tokenHasher.matches(refreshToken, savedRefreshToken)){ + throw new UnauthorizedException("refresh token is already used"); + } + log.info("존재하는 token과 같은 token을 가지고 있습니다."); + } + + private AuthorityInfo getAuthorityInfo(RefreshDto refreshDto) { + Claims claims = Jwts.parser() + .verifyWith(key) // 서버가 저장하고 있는 키로 payload 암호 풀기 + .build() + .parseSignedClaims(refreshDto.getRefreshToken()) + .getPayload(); + String email = (String) claims.get("email"); + String authorities = (String) claims.get("authorities"); + + return AuthorityInfo.builder() + .email(email) + .authorities(authorities).build(); + } + + public IssuedTokens getAccessTokenByAuthorityInfo(AuthorityInfo authorityInfo) { + String accessToken = getAccessToken(authorityInfo); + String refreshToken = getRefreshToken(authorityInfo); + return IssuedTokens.builder() + .accessToken(accessToken) + .refreshToken(refreshToken) + .build(); + } + + public String getAccessToken(AuthorityInfo authorityInfo) { + log.info("유통기한 : {}",accessExpireMin*ONE_MINUTE); + return getToken(authorityInfo.getEmail(), authorityInfo.getAuthorities(), accessExpireMin*ONE_MINUTE); + } + public String getRefreshToken(AuthorityInfo authorityInfo) { + return getToken(authorityInfo.getEmail(), authorityInfo.getAuthorities(), refreshExpireMin*ONE_MINUTE); + } + + private String getToken(String email, String authorities, Long expireTime) { + long now = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + return Jwts.builder().issuer("Sol-Kimjang").subject("JWT Token") + // 비밀번호는 절대 안된다. + .claim("email", email) + .claim("authorities", authorities) + .claim("create_date", LocalDateTime.now().toString()) // 생성 시간을 둬서 여러번 생성되는 것을 방지하기 + .issuedAt(new Date(now)) + // 만료시간, ms 단위, 만료 시간을 넘어선 토큰이 들어오면 만료됐다고 예외가 발생한다. + .expiration(new Date(now + expireTime)) + // 디지털 서명 작성 + .signWith(key).compact(); + } + + public void saveIssuedToken(String email, String refreshToken) { + String hashedRefreshToken = tokenHasher.encode(refreshToken); + refreshTokenCacheService.saveToken(email, hashedRefreshToken); + } +} diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/service/UserService.java b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/service/UserService.java similarity index 61% rename from Backend/src/main/java/kimjang/toolkit/solsol/user/service/UserService.java rename to Backend/src/main/java/kimjang/toolkit/solsol/domain/user/service/UserService.java index 955a895..666be81 100644 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/service/UserService.java +++ b/Backend/src/main/java/kimjang/toolkit/solsol/domain/user/service/UserService.java @@ -1,9 +1,10 @@ -package kimjang.toolkit.solsol.user.service; +package kimjang.toolkit.solsol.domain.user.service; +import kimjang.toolkit.solsol.domain.user.dto.*; +import kimjang.toolkit.solsol.domain.user.entities.Authority; import kimjang.toolkit.solsol.exception.UnauthorizedException; -import kimjang.toolkit.solsol.user.entities.User; -import kimjang.toolkit.solsol.user.dto.*; -import kimjang.toolkit.solsol.user.reposiotry.UserRepository; +import kimjang.toolkit.solsol.domain.user.entities.User; +import kimjang.toolkit.solsol.domain.user.reposiotry.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.BadCredentialsException; @@ -14,7 +15,9 @@ import org.springframework.transaction.annotation.Transactional; import javax.naming.AuthenticationException; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; @Slf4j @Service @@ -23,6 +26,7 @@ public class UserService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; private final JwtIssuer jwtIssuer; + @Transactional public UserDto registerUser(CreateUserDto createUserDto){ String hashPwd = passwordEncoder.encode(createUserDto.getPwd()); @@ -44,6 +48,12 @@ public UserDto findByEmail(String email){ return UserDto.toDto(user); } + /** + * 유저가 처음으로 로그인할 때 생성되는 로그인 결과 값 LoginSuccessDto + * + * @param loginDto + * @return + */ public LoginSuccessDto userLoginAndSaveRefreshToken(LoginDto loginDto){ try{ User user = userRepository.findByEmail(loginDto.getEmail()) @@ -53,22 +63,39 @@ public LoginSuccessDto userLoginAndSaveRefreshToken(LoginDto loginDto){ throw new BadCredentialsException("bad credential: using unmatched password"); } log.info("email : "+loginDto.getEmail()); - String accessToken = jwtIssuer.getAccessToken(user.getEmail(), user.getAuthorities()); - String refreshToken = jwtIssuer.getRefreshToken(user.getEmail(), user.getAuthorities()); + String authorities = populateAuthorities(user.getAuthorities()); + AuthorityInfo authorityInfo = AuthorityInfo.builder().email(user.getEmail()) + .authorities(authorities).build(); + // 새로운 tokens 재발급 + IssuedTokens tokens = jwtIssuer.getAccessTokenByAuthorityInfo(authorityInfo); + // 재발급한 tokens Cache와 DB에 저장 + jwtIssuer.saveIssuedToken(user.getEmail(), tokens.getRefreshToken()); return LoginSuccessDto.builder() .id(user.getId()) .email(user.getEmail()) .name(user.getName()) .imgUrl(user.getImgUrl()) - .accessToken(accessToken) - .refreshToken(refreshToken) + .accessToken(tokens.getAccessToken()) + .refreshToken(tokens.getRefreshToken()) .build(); } catch (AuthenticationException e){ throw new RuntimeException(e.getMessage(), e); } } - public LoginSuccessDto getAccessToken(RefreshDto refreshDto) { + /** + * 기존 refresh token으로 새로운 로그인 결과 값 LoginSuccessDto을 생성 + * @param refreshDto + * @return + */ + public IssuedTokens getAccessToken(RefreshDto refreshDto) { + /** + * 캐시나 DB에 존재하는 refresh token인지 확인 + * - 없으면 401 에러 + * - 있으면 기존꺼 지우고 새로운 값으로 업데이트 (Cacheput) + * - refresh token의 만료시간까지 사용되지 않으면 제거 + */ + return jwtIssuer.getAccessTokenByRefreshToken(refreshDto); } @@ -81,4 +108,12 @@ public UserProfileDto getUserDetail() { } return userRepository.findProfileByEmail(userEmail); } + + private String populateAuthorities(Set collection) { + Set authoritiesSet = new HashSet<>(); + for (Authority authority : collection) { + authoritiesSet.add(authority.getName()); + } + return String.join(",", authoritiesSet); + } } diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/RefreshDto.java b/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/RefreshDto.java deleted file mode 100644 index f996bae..0000000 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/dto/RefreshDto.java +++ /dev/null @@ -1,8 +0,0 @@ -package kimjang.toolkit.solsol.user.dto; - -import lombok.Getter; - -@Getter -public class RefreshDto { - private String refreshToken; -} diff --git a/Backend/src/main/java/kimjang/toolkit/solsol/user/service/JwtIssuer.java b/Backend/src/main/java/kimjang/toolkit/solsol/user/service/JwtIssuer.java deleted file mode 100644 index dc299a4..0000000 --- a/Backend/src/main/java/kimjang/toolkit/solsol/user/service/JwtIssuer.java +++ /dev/null @@ -1,104 +0,0 @@ -package kimjang.toolkit.solsol.user.service; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.MalformedJwtException; -import io.jsonwebtoken.security.Keys; -import io.jsonwebtoken.security.SignatureException; -import kimjang.toolkit.solsol.config.jwt.JwtInvalidException; -import kimjang.toolkit.solsol.config.jwt.SecurityConstants; -import kimjang.toolkit.solsol.user.entities.Authority; -import kimjang.toolkit.solsol.user.dto.LoginSuccessDto; -import kimjang.toolkit.solsol.user.dto.RefreshDto; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import javax.crypto.SecretKey; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; - -@Slf4j -@Component -public class JwtIssuer { - private final SecretKey key = Keys.hmacShaKeyFor(SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8)); - private final Long ONE_SECONDS = 1000L; - private final Long ONE_MINUTE = 60 * ONE_SECONDS; - private final long accessExpireMin; - private final long refreshExpireMin; - public JwtIssuer(@Value("${jwt.access-expire-min:10}") long accessExpireMin, - @Value("${jwt.refresh-expire-min:60}") long refreshExpireMin){ - this.accessExpireMin = accessExpireMin; - this.refreshExpireMin = refreshExpireMin; - } - - private String getToken(String email, String authorities, Long expireTime) { - log.info("authories : {}",authorities); - long now = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); - return Jwts.builder().issuer("Sol-Kimjang").subject("JWT Token") - // 비밀번호는 절대 안된다. - .claim("email", email) - .claim("authorities", authorities) - .issuedAt(new Date(now)) - // 만료시간, ms 단위, 만료 시간을 넘어선 토큰이 들어오면 만료됐다고 예외가 발생한다. - .expiration(new Date(now + expireTime)) - // 디지털 서명 작성 - .signWith(key).compact(); - } - - private String populateAuthorities(Set collection) { - Set authoritiesSet = new HashSet<>(); - for (Authority authority : collection) { - authoritiesSet.add(authority.getName()); - } - return String.join(",", authoritiesSet); - } - - public String getAccessToken(String email, Set authorities) { - log.info("유통기한 : {}",accessExpireMin*ONE_MINUTE); - return getToken(email, populateAuthorities(authorities), accessExpireMin*ONE_MINUTE); - } - public String getRefreshToken(String email, Set authorities) { - return getToken(email, populateAuthorities(authorities), refreshExpireMin*ONE_MINUTE); - } - public String getAccessToken(String email, String authorities) { - log.info("유통기한 : {}",accessExpireMin*ONE_MINUTE); - return getToken(email, authorities, accessExpireMin*ONE_MINUTE); - } - public String getRefreshToken(String email, String authorities) { - return getToken(email, authorities, refreshExpireMin*ONE_MINUTE); - } - - public LoginSuccessDto getAccessTokenByRefreshToken(RefreshDto refreshDto) { - try { - Claims claims = Jwts.parser() - .verifyWith(key) // 서버가 저장하고 있는 키로 payload 암호 풀기 - .build() - .parseSignedClaims(refreshDto.getRefreshToken()) - .getPayload(); - String email = (String) claims.get("email"); - String authorities = (String) claims.get("authorities"); - - String accessToken = getAccessToken(email, authorities); - String refreshToken = getRefreshToken(email, authorities); - return LoginSuccessDto.builder() - .accessToken(accessToken) - .refreshToken(refreshToken) - .build(); - } catch (SignatureException signatureException) { - throw new JwtInvalidException("signature key is different", signatureException); - } catch (ExpiredJwtException expiredJwtException) { - throw new JwtInvalidException("expired token", expiredJwtException); - } catch (MalformedJwtException malformedJwtException) { - throw new JwtInvalidException("malformed token", malformedJwtException); - } catch (IllegalArgumentException illegalArgumentException) { - throw new JwtInvalidException("using illegal argument like null", illegalArgumentException); - } - - } -} diff --git a/Backend/src/main/resources/application-dev.properties b/Backend/src/main/resources/application-dev.properties index 3dead11..8e925ae 100644 --- a/Backend/src/main/resources/application-dev.properties +++ b/Backend/src/main/resources/application-dev.properties @@ -15,8 +15,14 @@ spring.datasource.username=root spring.datasource.password=sol-root spring.jpa.hibernate.ddl-auto= update spring.datasource.url=jdbc:mysql://localhost:3306/chat -spring.jpa.show-sql=true -spring.jpa.properties.hibernate.format_sql= true +spring.jpa.show-sql=false +spring.jpa.properties.hibernate.format_sql= false # swagger -#springdoc.swagger-ui.path=/swagger-ui.html \ No newline at end of file +#springdoc.swagger-ui.path=/swagger-ui.html + +# memcached +spring.cache.operationTimeout: 1500 +spring.cache.cacheName: refresh +spring.cache.expiration: 600 +spring.cache.address: localhost:11211 diff --git a/Backend/src/main/resources/application-prod.properties b/Backend/src/main/resources/application-prod.properties index 97a2fbd..a6235d3 100644 --- a/Backend/src/main/resources/application-prod.properties +++ b/Backend/src/main/resources/application-prod.properties @@ -21,3 +21,9 @@ spring.jpa.show-sql=true ## aws RDS certification spring.datasource.username=chat_rds spring.datasource.password=chat-rds-root + +# memcached +spring.cache.operationTimeout: 1500 +spring.cache.cacheName: refresh +spring.cache.expiration: 600 +spring.cache.address: kimjang-token-cache-cgl9vt.serverless.apn2.cache.amazonaws.com:11212 diff --git a/Backend/src/test/java/kimjang/toolkit/solsol/chat/CreateChatTest.java b/Backend/src/test/java/kimjang/toolkit/solsol/chat/CreateChatTest.java index 0cd6305..903f15b 100644 --- a/Backend/src/test/java/kimjang/toolkit/solsol/chat/CreateChatTest.java +++ b/Backend/src/test/java/kimjang/toolkit/solsol/chat/CreateChatTest.java @@ -2,10 +2,10 @@ import kimjang.toolkit.solsol.SolsolApplication; -import kimjang.toolkit.solsol.user.dto.UserDto; -import kimjang.toolkit.solsol.message.ChatMessage; -import kimjang.toolkit.solsol.message.dto.SendChatMessageDto; -import kimjang.toolkit.solsol.message.service.ChatService; +import kimjang.toolkit.solsol.domain.user.dto.UserDto; +import kimjang.toolkit.solsol.domain.message.ChatMessage; +import kimjang.toolkit.solsol.domain.message.dto.SendChatMessageDto; +import kimjang.toolkit.solsol.domain.message.service.ChatService; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; diff --git a/Backend/src/test/java/kimjang/toolkit/solsol/chat/GetPastChatsTest.java b/Backend/src/test/java/kimjang/toolkit/solsol/chat/GetPastChatsTest.java index c233b7f..a97fae2 100644 --- a/Backend/src/test/java/kimjang/toolkit/solsol/chat/GetPastChatsTest.java +++ b/Backend/src/test/java/kimjang/toolkit/solsol/chat/GetPastChatsTest.java @@ -1,17 +1,15 @@ package kimjang.toolkit.solsol.chat; -import kimjang.toolkit.solsol.message.dto.PastChatsDto; -import kimjang.toolkit.solsol.message.dto.ReqPastChatsDto; -import kimjang.toolkit.solsol.message.service.ChatService; +import kimjang.toolkit.solsol.domain.message.dto.PastChatsDto; +import kimjang.toolkit.solsol.domain.message.dto.ReqPastChatsDto; +import kimjang.toolkit.solsol.domain.message.service.ChatService; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.PageRequest; -import java.time.LocalDateTime; - @SpringBootTest public class GetPastChatsTest { @Autowired diff --git a/Backend/src/test/java/kimjang/toolkit/solsol/chat/LoadPastChatsTest.java b/Backend/src/test/java/kimjang/toolkit/solsol/chat/LoadPastChatsTest.java index e622927..659d68d 100644 --- a/Backend/src/test/java/kimjang/toolkit/solsol/chat/LoadPastChatsTest.java +++ b/Backend/src/test/java/kimjang/toolkit/solsol/chat/LoadPastChatsTest.java @@ -1,8 +1,8 @@ package kimjang.toolkit.solsol.chat; -import kimjang.toolkit.solsol.message.dto.PastChatsDto; -import kimjang.toolkit.solsol.message.dto.ReqPastChatsDto; -import kimjang.toolkit.solsol.message.service.ChatService; +import kimjang.toolkit.solsol.domain.message.dto.PastChatsDto; +import kimjang.toolkit.solsol.domain.message.dto.ReqPastChatsDto; +import kimjang.toolkit.solsol.domain.message.service.ChatService; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; diff --git a/Backend/src/test/java/kimjang/toolkit/solsol/integrationTest/InviteIntegrationTests.java b/Backend/src/test/java/kimjang/toolkit/solsol/integrationTest/InviteIntegrationTests.java index 0799a52..adfcd61 100644 --- a/Backend/src/test/java/kimjang/toolkit/solsol/integrationTest/InviteIntegrationTests.java +++ b/Backend/src/test/java/kimjang/toolkit/solsol/integrationTest/InviteIntegrationTests.java @@ -3,14 +3,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import kimjang.toolkit.solsol.message.ChatMessage; -import kimjang.toolkit.solsol.message.dto.SendChatMessageDto; -import kimjang.toolkit.solsol.room.ChatRoomController; -import kimjang.toolkit.solsol.room.dto.CreateChatRoomDto; -import kimjang.toolkit.solsol.room.dto.CreateRoomReqDto; -import kimjang.toolkit.solsol.room.dto.InviteChatRoomDto; -import kimjang.toolkit.solsol.room.service.ChatRoomService; -import kimjang.toolkit.solsol.user.dto.UserDto; +import kimjang.toolkit.solsol.domain.room.ChatRoomController; +import kimjang.toolkit.solsol.domain.room.dto.CreateChatRoomDto; +import kimjang.toolkit.solsol.domain.room.dto.CreateRoomReqDto; +import kimjang.toolkit.solsol.domain.room.dto.InviteChatRoomDto; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/Backend/src/test/java/kimjang/toolkit/solsol/integrationTest/MessageIntegrationTests.java b/Backend/src/test/java/kimjang/toolkit/solsol/integrationTest/MessageIntegrationTests.java index 4343657..6131993 100644 --- a/Backend/src/test/java/kimjang/toolkit/solsol/integrationTest/MessageIntegrationTests.java +++ b/Backend/src/test/java/kimjang/toolkit/solsol/integrationTest/MessageIntegrationTests.java @@ -3,8 +3,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import kimjang.toolkit.solsol.user.dto.UserDto; -import kimjang.toolkit.solsol.message.dto.SendChatMessageDto; +import kimjang.toolkit.solsol.domain.user.dto.UserDto; +import kimjang.toolkit.solsol.domain.message.dto.SendChatMessageDto; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; diff --git a/Backend/src/test/java/kimjang/toolkit/solsol/jwt/ReIssueTokenTest.java b/Backend/src/test/java/kimjang/toolkit/solsol/jwt/ReIssueTokenTest.java new file mode 100644 index 0000000..589fbff --- /dev/null +++ b/Backend/src/test/java/kimjang/toolkit/solsol/jwt/ReIssueTokenTest.java @@ -0,0 +1,137 @@ +package kimjang.toolkit.solsol.jwt; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import kimjang.toolkit.solsol.config.cache.TokenHasher; +import kimjang.toolkit.solsol.config.jwt.SecurityConstants; +import kimjang.toolkit.solsol.domain.cache.RefreshTokenCacheService; +import kimjang.toolkit.solsol.domain.user.dto.AuthorityInfo; +import kimjang.toolkit.solsol.domain.user.dto.IssuedTokens; +import kimjang.toolkit.solsol.domain.user.dto.RefreshDto; +import kimjang.toolkit.solsol.domain.user.service.JwtIssuer; +import kimjang.toolkit.solsol.exception.UnauthorizedException; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.transaction.annotation.Transactional; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; + +@Slf4j +@SpringBootTest +@Transactional +public class ReIssueTokenTest { + private final SecretKey key = Keys.hmacShaKeyFor(SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8)); + @Autowired + private TokenHasher tokenHasher; + + @Autowired + private RefreshTokenCacheService refreshTokenCacheService; + + @Autowired + private JwtIssuer jwtIssuer; + + @Autowired + private PasswordEncoder bCryptPasswordEncoder; + + private IssuedTokens issuedTokens; + private final String email = "solsol@gmail.com"; + private final AuthorityInfo authorityInfo1 = AuthorityInfo.builder() + .email(email).authorities("ROLE_USER").build(); + private final AuthorityInfo authorityInfo2 = AuthorityInfo.builder() + .email("email").authorities("ROLE_USER").build(); + + @Test + public void when_reissue_then_not_equal_made_time(){ + // 토큰 생성 + issuedTokens = jwtIssuer.getAccessTokenByAuthorityInfo(authorityInfo1); + // 토큰 저장 + jwtIssuer.saveIssuedToken(email, issuedTokens.getRefreshToken()); + String time1 = getCreatedTime(issuedTokens.getRefreshToken()); + log.info("초기 refresh token 생성 시간 : {}",time1); + // 처음 생성 + IssuedTokens newIssuedTokens = jwtIssuer.getAccessTokenByRefreshToken(new RefreshDto(issuedTokens.getRefreshToken())); + String time2 = getCreatedTime(newIssuedTokens.getRefreshToken()); + log.info("다음 refresh token 생성 시간 : {}",time2); + Assertions.assertNotEquals(time1, time2); + } + + @Test + public void when_reissue_then_not_match(){ + // 토큰 생성 + issuedTokens = jwtIssuer.getAccessTokenByAuthorityInfo(authorityInfo1); + // 토큰 저장 + jwtIssuer.saveIssuedToken(email, issuedTokens.getRefreshToken()); + log.info("초기 토큰 : {}", issuedTokens.getRefreshToken()); + // 해싱된 refresh token 불러오기 + String savedRefreshToken = refreshTokenCacheService.searchToken(authorityInfo1.getEmail()); + + String time1 = getCreatedTime(issuedTokens.getRefreshToken()); + log.info("초기 refresh token 생성 시간 : {}",time1); + // 처음 생성 + IssuedTokens newIssuedTokens = jwtIssuer.getAccessTokenByRefreshToken(new RefreshDto(issuedTokens.getRefreshToken())); + String time2 = getCreatedTime(newIssuedTokens.getRefreshToken()); + log.info("다음 토큰 : {}", newIssuedTokens.getRefreshToken()); + log.info("다음 refresh token 생성 시간 : {}",time2); + + Assertions.assertNotEquals(time1, time2); + // 새로운 refresh token 가져오고, 해싱된 토큰 비교. 다른 값을 해싱했기 때문에 match하지 말아야한다. + Assertions.assertTrue(!tokenHasher.matches(newIssuedTokens.getRefreshToken(), savedRefreshToken)); + } + + @Test + public void when_string_then_not_match_sha_256(){ + // 토큰 생성 + String test1 = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTb2wtS2ltamFuZyIsInN1YiI6IkpXVCBUb2tlbiIsImVtYWlsIjoic29sc29sQGdtYWlsLmNvbSIsImF1dGhvcml0aWVzIjoiUk9MRV9VU0VSIiwiY3JlYXRlX2RhdGUiOiIyMDI0LTA4LTAyVDE3OjMxOjQxLjMzMzkwNCIsImlhdCI6MTcyMjU4NzUwMSwiZXhwIjoxNzIyNTg3NjIxfQ.RYVguGifzq4jQ4fCHfNGTGfFlMLn-VnmMy_BAKPLsEE"; + String test2 = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTb2wtS2ltamFuZyIsInN1YiI6IkpXVCBUb2tlbiIsImVtYWlsIjoic29sc29sQGdtYWlsLmNvbSIsImF1dGhvcml0aWVzIjoiUk9MRV9VU0VSIiwiY3JlYXRlX2RhdGUiOiIyMDI0LTA4LTAyVDE3OjMxOjQxLjcwMDE3MyIsImlhdCI6MTcyMjU4NzUwMSwiZXhwIjoxNzIyNTg3NjIxfQ.J_HxlIgceLJTjbU58_KIgfeb8gRiOfmHPBaWnQyXc7w"; + + String hashed = tokenHasher.encode(test1); + // 새로운 refresh token 가져오고, 해싱된 토큰 비교. 다른 값을 해싱했기 때문에 match하지 말아야한다. + Assertions.assertFalse(tokenHasher.matches(test2, hashed)); + } + +// @Test +// public void when_string_then_not_match_bCrypt(){ +// // 토큰 생성 +// String test1 = jwtIssuer.getRefreshToken(authorityInfo1); +// log.info("test1 : {}",test1); +// String test2 = jwtIssuer.getRefreshToken(authorityInfo2);; +// log.info("test2 : {}",test2); +// +// String hashed = bCryptPasswordEncoder.encode(test1); +// // 새로운 refresh token 가져오고, 해싱된 토큰 비교. 다른 값을 해싱했기 때문에 match하지 말아야한다. +// Assertions.assertFalse(bCryptPasswordEncoder.matches(test2, hashed)); +// } + + + @Test + public void when_reissue_then_invoke_exception(){ + // 토큰 생성 + issuedTokens = jwtIssuer.getAccessTokenByAuthorityInfo(authorityInfo1); + // 토큰 저장 + jwtIssuer.saveIssuedToken(email, issuedTokens.getRefreshToken()); + String time1 = getCreatedTime(issuedTokens.getRefreshToken()); + log.info("초기 refresh token 생성 시간 : {}",time1); + // 처음 생성 + IssuedTokens newIssuedTokens = jwtIssuer.getAccessTokenByRefreshToken(new RefreshDto(issuedTokens.getRefreshToken())); + String time2 = getCreatedTime(newIssuedTokens.getRefreshToken()); + log.info("다음 refresh token 생성 시간 : {}",time2); + + Assertions.assertThrows(UnauthorizedException.class, + () -> jwtIssuer.getAccessTokenByRefreshToken(new RefreshDto(issuedTokens.getRefreshToken()))); + } + + private String getCreatedTime(String refreshToken) { + Claims claims = Jwts.parser() + .verifyWith(key) // 서버가 저장하고 있는 키로 payload 암호 풀기 + .build() + .parseSignedClaims(refreshToken) + .getPayload(); + return (String) claims.get("create_date"); + } +} diff --git a/Backend/src/test/java/kimjang/toolkit/solsol/jwt/RefreshTokenCacheServiceTest.java b/Backend/src/test/java/kimjang/toolkit/solsol/jwt/RefreshTokenCacheServiceTest.java new file mode 100644 index 0000000..5ea8629 --- /dev/null +++ b/Backend/src/test/java/kimjang/toolkit/solsol/jwt/RefreshTokenCacheServiceTest.java @@ -0,0 +1,120 @@ +package kimjang.toolkit.solsol.jwt; + +import kimjang.toolkit.solsol.SolsolApplication; +import kimjang.toolkit.solsol.domain.cache.RefreshToken; +import kimjang.toolkit.solsol.domain.cache.RefreshTokenCacheService; +import kimjang.toolkit.solsol.domain.cache.RefreshTokenRepository; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Duration; +import java.time.LocalDateTime; + +@Slf4j +@SpringBootTest(classes = SolsolApplication.class) +@ActiveProfiles(value = "dev") +public class RefreshTokenCacheServiceTest { + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private RefreshTokenCacheService refreshTokenCacheService; + + @Autowired + private RefreshTokenRepository refreshTokenRepository; + + @Test + public void checkCacheableToken(){ + String email = "solsol@gmail.com"; + String refreshToken = "1234"; + String hashedRefreshToken = passwordEncoder.encode(refreshToken); + + // email에 맞는 refresh token 캐시에 저장 + refreshTokenCacheService.saveToken(email, hashedRefreshToken); + // 캐시에 있는 refresh token 얻어오기 + String savedRefreshToken = refreshTokenCacheService.searchToken(email); + Assertions.assertTrue(passwordEncoder.matches(refreshToken, savedRefreshToken)); + } + + + + private final int testCount = 10000; + @Test + public void countTimeDBAndCache(){ + // set up + String email = "solsol@gmail.com"; + String refreshToken = "1234"; + String hashedRefreshToken = passwordEncoder.encode(refreshToken); + RefreshToken tokenEntity = RefreshToken.builder() + .email(email) + .refreshToken(hashedRefreshToken) + .build(); + refreshTokenRepository.save(tokenEntity); + // email에 맞는 refresh token 캐시에 저장 + refreshTokenCacheService.saveToken(email, hashedRefreshToken); + + LocalDateTime start = LocalDateTime.now(); + for(int i=0; i