diff --git a/codin-auth/README.md b/codin-auth/README.md new file mode 100644 index 00000000..e69de29b diff --git a/codin-auth/build.gradle b/codin-auth/build.gradle new file mode 100644 index 00000000..23dab9b0 --- /dev/null +++ b/codin-auth/build.gradle @@ -0,0 +1,7 @@ +plugins { + id 'java' +} + +dependencies { + //todo : 의존성 추가 +} \ No newline at end of file diff --git a/codin-common/README.md b/codin-common/README.md new file mode 100644 index 00000000..e69de29b diff --git a/codin-common/build.gradle b/codin-common/build.gradle new file mode 100644 index 00000000..67badb08 --- /dev/null +++ b/codin-common/build.gradle @@ -0,0 +1,55 @@ +plugins { + id 'java-library' + id 'org.springframework.boot' version '3.2.0' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'inu.codin' +version = '1.0.0' +sourceCompatibility = '17' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + // Spring Boot Core + api 'org.springframework.boot:spring-boot-starter-web' + + // Spring Data MongoDB (for BaseTimeEntity) + api 'org.springframework.boot:spring-boot-starter-data-mongodb' + + // Rate Limiting (for RateLimitInterceptor) + api 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0' + + // Configuration Properties + api 'org.springframework.boot:spring-boot-configuration-processor' + + // Jackson for JSON processing + api 'com.fasterxml.jackson.core:jackson-databind' + + // Lombok + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' +} + +tasks.named('test') { + useJUnitPlatform() +} + +// Disable Spring Boot plugin's jar task since this is a library +jar { + enabled = true + archiveClassifier = '' +} + +// Disable the bootJar task since this is a library +bootJar { + enabled = false +} \ No newline at end of file diff --git a/codin-common/src/main/java/inu/codin/common/config/CodinCommonAutoConfiguration.java b/codin-common/src/main/java/inu/codin/common/config/CodinCommonAutoConfiguration.java new file mode 100644 index 00000000..45584e55 --- /dev/null +++ b/codin-common/src/main/java/inu/codin/common/config/CodinCommonAutoConfiguration.java @@ -0,0 +1,41 @@ +package inu.codin.common.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import inu.codin.common.util.MultipartJackson2HttpMessageConverter; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; + +/** + * Codin Common 모듈의 자동 구성 클래스 + * + * 목적: + * - codin-common 모듈을 의존성으로 추가하기만 해도 + * MultipartJackson2HttpMessageConverter 빈이 자동으로 등록되도록 함 + * + * 특징: + * - Spring Boot 3 방식의 AutoConfiguration + * - 애플리케이션(@SpringBootApplication)에서 + * scanBasePackages를 수정하지 않아도 됨 + */ +@AutoConfiguration +public class CodinCommonAutoConfiguration { + + /** + * Multipart 요청(JSON + File)을 처리하기 위한 + * 커스텀 HttpMessageConverter 빈 등록 + * + * ObjectMapper는 Spring Boot가 기본으로 제공하는 + * 공용 Jackson ObjectMapper 빈을 주입받아 사용 + * + * → 이렇게 하면: + * - Jackson 설정(LocalDateTime, snake_case 등) + * - 기존 API JSON 직렬화 규칙 + * 을 그대로 재사용 가능 + */ + @Bean + public MultipartJackson2HttpMessageConverter multipartJackson2HttpMessageConverter( + ObjectMapper objectMapper + ) { + return new MultipartJackson2HttpMessageConverter(objectMapper); + } +} \ No newline at end of file diff --git a/codin-core/src/main/java/inu/codin/codin/common/dto/BaseTimeEntity.java b/codin-common/src/main/java/inu/codin/common/dto/BaseTimeEntity.java similarity index 96% rename from codin-core/src/main/java/inu/codin/codin/common/dto/BaseTimeEntity.java rename to codin-common/src/main/java/inu/codin/common/dto/BaseTimeEntity.java index 595a1377..1867ed45 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/dto/BaseTimeEntity.java +++ b/codin-common/src/main/java/inu/codin/common/dto/BaseTimeEntity.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.dto; +package inu.codin.common.dto; import lombok.Getter; import org.springframework.data.annotation.CreatedBy; diff --git a/codin-core/src/main/java/inu/codin/codin/common/dto/Department.java b/codin-common/src/main/java/inu/codin/common/dto/Department.java similarity index 97% rename from codin-core/src/main/java/inu/codin/codin/common/dto/Department.java rename to codin-common/src/main/java/inu/codin/common/dto/Department.java index 841b1ed4..926a2b9d 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/dto/Department.java +++ b/codin-common/src/main/java/inu/codin/common/dto/Department.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.dto; +package inu.codin.common.dto; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/codin-core/src/main/java/inu/codin/codin/common/exception/GlobalErrorCode.java b/codin-common/src/main/java/inu/codin/common/exception/GlobalErrorCode.java similarity index 76% rename from codin-core/src/main/java/inu/codin/codin/common/exception/GlobalErrorCode.java rename to codin-common/src/main/java/inu/codin/common/exception/GlobalErrorCode.java index 70c3f2f2..f1ef3fc4 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/exception/GlobalErrorCode.java +++ b/codin-common/src/main/java/inu/codin/common/exception/GlobalErrorCode.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.exception; +package inu.codin.common.exception; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/common/exception/GlobalException.java b/codin-common/src/main/java/inu/codin/common/exception/GlobalException.java similarity index 86% rename from codin-core/src/main/java/inu/codin/codin/common/exception/GlobalException.java rename to codin-common/src/main/java/inu/codin/common/exception/GlobalException.java index 6b2c2932..210c24c5 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/exception/GlobalException.java +++ b/codin-common/src/main/java/inu/codin/common/exception/GlobalException.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.exception; +package inu.codin.common.exception; import lombok.Getter; diff --git a/codin-core/src/main/java/inu/codin/codin/common/exception/NotFoundException.java b/codin-common/src/main/java/inu/codin/common/exception/NotFoundException.java similarity index 76% rename from codin-core/src/main/java/inu/codin/codin/common/exception/NotFoundException.java rename to codin-common/src/main/java/inu/codin/common/exception/NotFoundException.java index 6ea57e46..a97f0b44 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/exception/NotFoundException.java +++ b/codin-common/src/main/java/inu/codin/common/exception/NotFoundException.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.exception; +package inu.codin.common.exception; public class NotFoundException extends RuntimeException{ diff --git a/codin-core/src/main/java/inu/codin/codin/common/ratelimit/ClientIpUtil.java b/codin-common/src/main/java/inu/codin/common/ratelimit/ClientIpUtil.java similarity index 95% rename from codin-core/src/main/java/inu/codin/codin/common/ratelimit/ClientIpUtil.java rename to codin-common/src/main/java/inu/codin/common/ratelimit/ClientIpUtil.java index 5d80709e..dfe4d07b 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/ratelimit/ClientIpUtil.java +++ b/codin-common/src/main/java/inu/codin/common/ratelimit/ClientIpUtil.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.ratelimit; +package inu.codin.common.ratelimit; import jakarta.servlet.http.HttpServletRequest; diff --git a/codin-core/src/main/java/inu/codin/codin/common/ratelimit/RateLimitBucketConstants.java b/codin-common/src/main/java/inu/codin/common/ratelimit/RateLimitBucketConstants.java similarity index 93% rename from codin-core/src/main/java/inu/codin/codin/common/ratelimit/RateLimitBucketConstants.java rename to codin-common/src/main/java/inu/codin/common/ratelimit/RateLimitBucketConstants.java index 941decfb..4bba43d9 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/ratelimit/RateLimitBucketConstants.java +++ b/codin-common/src/main/java/inu/codin/common/ratelimit/RateLimitBucketConstants.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.ratelimit; +package inu.codin.common.ratelimit; import java.time.Duration; diff --git a/codin-core/src/main/java/inu/codin/codin/common/ratelimit/RateLimitInterceptor.java b/codin-common/src/main/java/inu/codin/common/ratelimit/RateLimitInterceptor.java similarity index 93% rename from codin-core/src/main/java/inu/codin/codin/common/ratelimit/RateLimitInterceptor.java rename to codin-common/src/main/java/inu/codin/common/ratelimit/RateLimitInterceptor.java index 0ce93b5d..c05fc6fc 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/ratelimit/RateLimitInterceptor.java +++ b/codin-common/src/main/java/inu/codin/common/ratelimit/RateLimitInterceptor.java @@ -1,6 +1,6 @@ -package inu.codin.codin.common.ratelimit; +package inu.codin.common.ratelimit; -import inu.codin.codin.common.response.RateLimitResponse; +import inu.codin.common.response.RateLimitResponse; import io.github.bucket4j.Bandwidth; import io.github.bucket4j.Bucket; import io.github.bucket4j.ConsumptionProbe; @@ -15,7 +15,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import static inu.codin.codin.common.ratelimit.RateLimitBucketConstants.*; +import static inu.codin.common.ratelimit.RateLimitBucketConstants.*; /** * Reference : https://velog.io/@whcksdud8/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Rate-limit-%ED%95%B8%EB%93%A4%EB%A7%81-%EB%B0%8F-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81 diff --git a/codin-core/src/main/java/inu/codin/codin/common/ratelimit/RateLimitService.java b/codin-common/src/main/java/inu/codin/common/ratelimit/RateLimitService.java similarity index 98% rename from codin-core/src/main/java/inu/codin/codin/common/ratelimit/RateLimitService.java rename to codin-common/src/main/java/inu/codin/common/ratelimit/RateLimitService.java index 17286444..33c3c832 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/ratelimit/RateLimitService.java +++ b/codin-common/src/main/java/inu/codin/common/ratelimit/RateLimitService.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.ratelimit; +package inu.codin.common.ratelimit; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/codin-core/src/main/java/inu/codin/codin/common/response/CommonResponse.java b/codin-common/src/main/java/inu/codin/common/response/CommonResponse.java similarity index 87% rename from codin-core/src/main/java/inu/codin/codin/common/response/CommonResponse.java rename to codin-common/src/main/java/inu/codin/common/response/CommonResponse.java index e35ef50b..1cb79d70 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/response/CommonResponse.java +++ b/codin-common/src/main/java/inu/codin/common/response/CommonResponse.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.response; +package inu.codin.common.response; import lombok.Getter; diff --git a/codin-core/src/main/java/inu/codin/codin/common/response/ExceptionResponse.java b/codin-common/src/main/java/inu/codin/common/response/ExceptionResponse.java similarity index 82% rename from codin-core/src/main/java/inu/codin/codin/common/response/ExceptionResponse.java rename to codin-common/src/main/java/inu/codin/common/response/ExceptionResponse.java index b9af8553..73a42a0e 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/response/ExceptionResponse.java +++ b/codin-common/src/main/java/inu/codin/common/response/ExceptionResponse.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.response; +package inu.codin.common.response; import lombok.Getter; diff --git a/codin-core/src/main/java/inu/codin/codin/common/response/ListResponse.java b/codin-common/src/main/java/inu/codin/common/response/ListResponse.java similarity index 88% rename from codin-core/src/main/java/inu/codin/codin/common/response/ListResponse.java rename to codin-common/src/main/java/inu/codin/common/response/ListResponse.java index 1f2b1c11..067e7823 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/response/ListResponse.java +++ b/codin-common/src/main/java/inu/codin/common/response/ListResponse.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.response; +package inu.codin.common.response; import lombok.Builder; import lombok.Getter; diff --git a/codin-core/src/main/java/inu/codin/codin/common/response/RateLimitResponse.java b/codin-common/src/main/java/inu/codin/common/response/RateLimitResponse.java similarity index 95% rename from codin-core/src/main/java/inu/codin/codin/common/response/RateLimitResponse.java rename to codin-common/src/main/java/inu/codin/common/response/RateLimitResponse.java index caf646ff..51a19a57 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/response/RateLimitResponse.java +++ b/codin-common/src/main/java/inu/codin/common/response/RateLimitResponse.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.response; +package inu.codin.common.response; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/common/response/SingleResponse.java b/codin-common/src/main/java/inu/codin/common/response/SingleResponse.java similarity index 86% rename from codin-core/src/main/java/inu/codin/codin/common/response/SingleResponse.java rename to codin-common/src/main/java/inu/codin/common/response/SingleResponse.java index 32955bcc..ab7ba32d 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/response/SingleResponse.java +++ b/codin-common/src/main/java/inu/codin/common/response/SingleResponse.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.response; +package inu.codin.common.response; import lombok.Builder; import lombok.Getter; diff --git a/codin-core/src/main/java/inu/codin/codin/common/util/MultipartJackson2HttpMessageConverter.java b/codin-common/src/main/java/inu/codin/common/util/MultipartJackson2HttpMessageConverter.java similarity index 96% rename from codin-core/src/main/java/inu/codin/codin/common/util/MultipartJackson2HttpMessageConverter.java rename to codin-common/src/main/java/inu/codin/common/util/MultipartJackson2HttpMessageConverter.java index be518234..1ebf7490 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/util/MultipartJackson2HttpMessageConverter.java +++ b/codin-common/src/main/java/inu/codin/common/util/MultipartJackson2HttpMessageConverter.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.util; +package inu.codin.common.util; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.MediaType; diff --git a/codin-core/src/main/java/inu/codin/codin/common/util/ObjectIdUtil.java b/codin-common/src/main/java/inu/codin/common/util/ObjectIdUtil.java similarity index 96% rename from codin-core/src/main/java/inu/codin/codin/common/util/ObjectIdUtil.java rename to codin-common/src/main/java/inu/codin/common/util/ObjectIdUtil.java index 869d9ad2..f319db29 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/util/ObjectIdUtil.java +++ b/codin-common/src/main/java/inu/codin/common/util/ObjectIdUtil.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.util; +package inu.codin.common.util; import org.bson.types.ObjectId; diff --git a/codin-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/codin-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..10297826 --- /dev/null +++ b/codin-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +inu.codin.common.config.CodinCommonAutoConfiguration \ No newline at end of file diff --git a/codin-core/Dockerfile b/codin-core/Dockerfile index 79e4ba45..cb6d7bc7 100644 --- a/codin-core/Dockerfile +++ b/codin-core/Dockerfile @@ -1,11 +1,27 @@ -FROM openjdk:17-jdk-slim -RUN apt-get update && apt-get install -y --no-install-recommends python3 python3-pip +FROM eclipse-temurin:17-jdk-jammy + +# 1. 기본 패키지 + Python + Chrome 의존성 설치 +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + python3 python3-pip \ + libglib2.0-0 libnss3 libgconf-2-4 libfontconfig1 \ + libxdamage1 libxkbcommon0 libxrandr2 xdg-utils \ + chromium-driver wget && \ + rm -rf /var/lib/apt/lists/* + +# 2. 파이썬 라이브러리 RUN pip3 install selenium pymongo webdriver-manager pandas openpyxl -RUN apt-get install -y libglib2.0 libnss3 libgconf-2-4 libfontconfig1 chromium-driver wget \ - && wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \ - && dpkg -i google-chrome-stable_current_amd64.deb && apt-get install -f -y + +# 3. Google Chrome 설치 (.deb를 apt가 설치하도록) +RUN wget -O /tmp/google-chrome.deb \ + https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \ + apt-get update && \ + apt-get install -y /tmp/google-chrome.deb && \ + rm -rf /var/lib/apt/lists/* /tmp/google-chrome.deb \ + ENV PATH="/usr/bin/google-chrome-stable:${PATH}" + WORKDIR /app COPY build/libs/codin-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8080 -ENTRYPOINT ["java", "-jar", "app.jar"] +ENTRYPOINT ["java", "-jar", "app.jar"] \ No newline at end of file diff --git a/codin-core/build.gradle b/codin-core/build.gradle index 5ddd651f..21d178dc 100644 --- a/codin-core/build.gradle +++ b/codin-core/build.gradle @@ -33,14 +33,18 @@ dependencyManagement { } } dependencies { + // Security - codin-security 공통 모듈 사용 + implementation project(':codin-security') + implementation project(':codin-common') + // FCM implementation 'com.google.firebase:firebase-admin:7.3.0' - // JWT Token - implementation 'io.jsonwebtoken:jjwt-api:0.11.2' - implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' - implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' - // Security - implementation 'org.springframework.boot:spring-boot-starter-security' + // JWT Token - codin-security에서 제공되므로 제거 가능 + // implementation 'io.jsonwebtoken:jjwt-api:0.11.2' + // implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' + // implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' + // Security - codin-security에서 제공되므로 제거 가능 + // implementation 'org.springframework.boot:spring-boot-starter-security' // Email implementation 'org.springframework.boot:spring-boot-starter-mail' // Thymeleaf(for email service) @@ -77,11 +81,18 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' + testImplementation 'org.testcontainers:junit-jupiter:1.19.7' + testImplementation 'org.testcontainers:mongodb:1.19.7' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } tasks.named('test') { + enabled = false useJUnitPlatform() + +} +tasks.withType(Test).configureEach { + enabled = false } processResources.dependsOn('copySecret') diff --git a/codin-core/src/main/java/inu/codin/codin/common/config/Bucket4jRateLimitApp.java b/codin-core/src/main/java/inu/codin/codin/common/config/Bucket4jRateLimitApp.java index 19fd425d..336622ae 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/config/Bucket4jRateLimitApp.java +++ b/codin-core/src/main/java/inu/codin/codin/common/config/Bucket4jRateLimitApp.java @@ -1,11 +1,5 @@ package inu.codin.codin.common.config; -import inu.codin.codin.common.ratelimit.RateLimitInterceptor; -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - // Lecture API - 좋아요 개수 Feign 요청으로 인한 RateLimiting 에러로 주석화 //@Configuration //@RequiredArgsConstructor diff --git a/codin-core/src/main/java/inu/codin/codin/common/config/SecurityConfig.java b/codin-core/src/main/java/inu/codin/codin/common/config/SecurityConfig.java index 5d3cafbb..b8e2b20b 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/config/SecurityConfig.java +++ b/codin-core/src/main/java/inu/codin/codin/common/config/SecurityConfig.java @@ -1,131 +1,103 @@ package inu.codin.codin.common.config; +/* + * IMPORTANT: 이 SecurityConfig는 임시로 주석 처리됨 + * + * Phase 1에서 codin-security 모듈로 Resource Server 기능이 분리됨 + * Phase 2에서 codin-auth 모듈로 Authorization Server 기능을 분리할 예정 + * + * 현재 상태: + * - JWT 검증 기능 -> codin-security 모듈로 이동 완료 + * - OAuth2 로그인 기능 -> codin-core에 잠시 남아있음 (Phase 2에서 codin-auth로 이동 예정) + * + * 임시 조치: 전체 설정을 주석 처리하여 컴파일 에러 방지 + * codin-core는 이제 codin-security 모듈에 의존하여 JWT 검증 기능 사용 + */ + +// TODO: Phase 2에서 codin-auth로 Authorization Server 기능 이동 + +/* + * 기존 SecurityConfig의 역할: + * 1. Authorization Server - OAuth2 로그인 처리 (Google, Apple) + * 2. Resource Server - JWT 토큰 검증 및 권한 체크 + * + * 분리 후: + * 1. Authorization Server -> codin-auth 모듈 (Phase 2에서 구현) + * 2. Resource Server -> codin-security 모듈 (Phase 1에서 완료) + */ -import inu.codin.codin.common.dto.PermitAllProperties; -import inu.codin.codin.common.dto.PublicApiProperties; -import inu.codin.codin.common.security.filter.ExceptionHandlerFilter; -import inu.codin.codin.common.security.filter.JwtAuthenticationFilter; -import inu.codin.codin.common.security.service.JwtService; import inu.codin.codin.common.security.service.oauth2.AppleOAuth2UserService; import inu.codin.codin.common.security.service.oauth2.CustomOAuth2UserService; -import inu.codin.codin.common.security.util.*; +import inu.codin.codin.common.security.util.CustomOAuth2AccessTokenResponseClient; +import inu.codin.codin.common.security.util.OAuth2AuthorizationRequestBasedOnCookieRepository; +import inu.codin.codin.common.security.util.OAuth2LoginFailureHandler; +import inu.codin.codin.common.security.util.OAuth2LoginSuccessHandler; import inu.codin.codin.common.util.CustomAuthorizationRequestResolver; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.access.hierarchicalroles.RoleHierarchy; -import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; -import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; -import org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.security.web.authentication.logout.LogoutFilter; -import org.springframework.web.context.request.RequestContextListener; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - -import java.util.List; -@Slf4j @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { - private final UserDetailsService userDetailsService; - private final JwtService jwtService; private final OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler; private final OAuth2LoginFailureHandler oAuth2LoginFailureHandler; private final CustomOAuth2UserService customOAuth2UserService; - private final PermitAllProperties permitAllProperties; - private final PublicApiProperties publicApiProperties; - private final AppleOAuth2UserService appleOAuth2UserService; private final ClientRegistrationRepository clientRegistrationRepository; private final CustomOAuth2AccessTokenResponseClient customOAuth2AccessTokenResponseClient; - private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint; - - @Value("${server.domain}") - private String BASEURL; - @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - + @org.springframework.core.annotation.Order(1) + public SecurityFilterChain oauth2Chain(HttpSecurity http) throws Exception { http - .cors(cors -> cors.configurationSource(corsConfigurationSource())) // cors 설정 - .csrf(CsrfConfigurer::disable) // csrf 비활성화 - .formLogin(FormLoginConfigurer::disable) // form login 비활성화 - .sessionManagement(sessionManagement -> sessionManagement - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 세션 사용하지 않음 - ) - // authorizeHttpRequests 메서드를 통해 요청에 대한 권한 설정 - .authorizeHttpRequests((authorizeHttpRequests) -> - authorizeHttpRequests - .requestMatchers(permitAllProperties.getUrls().toArray(new String[0])).permitAll() - .requestMatchers(publicApiProperties.getUrls().toArray(new String[0])).permitAll() - .requestMatchers(ADMIN_AUTH_PATHS).hasRole("ADMIN") - .requestMatchers(MANAGER_AUTH_PATHS).hasRole("MANAGER") - .requestMatchers(USER_AUTH_PATHS).hasRole("USER") - .anyRequest().hasRole("USER") - ) - //Security 내부 인증 실패 처리 => access_token 없는 경우 - .exceptionHandling(exception -> exception - .authenticationEntryPoint(customAuthenticationEntryPoint) + // 이 체인은 OAuth2 엔드포인트에만 적용 + .securityMatcher("/oauth2/**", "/login/oauth2/**") + + .csrf(CsrfConfigurer::disable) + .formLogin(FormLoginConfigurer::disable) + .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + + .authorizeHttpRequests(auth -> auth + .anyRequest().permitAll() ) - //oauth2 로그인 설정 추가 + .oauth2Login(oauth2 -> oauth2 - // Apple 클라이언트에 대해 커스텀 토큰 응답 클라이언트 적용 - .tokenEndpoint(token -> token - .accessTokenResponseClient(customOAuth2AccessTokenResponseClient) - ) - .authorizationEndpoint(authorization -> authorization - //쿠키를 사용해 OAuth의 정보를 가져오고 저장 - .authorizationRequestRepository(oAuth2AuthorizationRequestBasedOnCookieRepository()) - .authorizationRequestResolver(new CustomAuthorizationRequestResolver(clientRegistrationRepository)) - ) -// .redirectionEndpoint(redirection -> redirection -// .baseUri("/callback/apple") // apple 콜백 URI를 설정추가. -// ) - .userInfoEndpoint(userInfo -> userInfo - .userService(delegatingOAuth2UserService()) - ) + .tokenEndpoint(token -> token.accessTokenResponseClient(customOAuth2AccessTokenResponseClient)) + .authorizationEndpoint(authorization -> authorization + .authorizationRequestRepository(oAuth2AuthorizationRequestBasedOnCookieRepository()) + .authorizationRequestResolver(new CustomAuthorizationRequestResolver(clientRegistrationRepository)) + ) + .userInfoEndpoint(userInfo -> userInfo.userService(delegatingOAuth2UserService())) + .successHandler(oAuth2LoginSuccessHandler) + .failureHandler(oAuth2LoginFailureHandler) + ); - .successHandler(oAuth2LoginSuccessHandler) - .failureHandler(oAuth2LoginFailureHandler) - ) - // Swagger 접근 시 httpBasic 인증 사용 -// .httpBasic(Customizer.withDefaults()) - // JwtAuthenticationFilter 추가 - .addFilterBefore( - new JwtAuthenticationFilter(jwtService, permitAllProperties, publicApiProperties), - UsernamePasswordAuthenticationFilter.class - ) - // 예외 처리 필터 추가 - .addFilterBefore(new ExceptionHandlerFilter(), LogoutFilter.class); return http.build(); } + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } @Bean public OAuth2AuthorizationRequestBasedOnCookieRepository oAuth2AuthorizationRequestBasedOnCookieRepository() { @@ -135,94 +107,10 @@ public OAuth2AuthorizationRequestBasedOnCookieRepository oAuth2AuthorizationRequ private OAuth2UserService delegatingOAuth2UserService() { return userRequest -> { String registrationId = userRequest.getClientRegistration().getRegistrationId(); - log.info("OAuth2 registrationId: {}", registrationId); - if ("apple".equals(registrationId)) { - log.info("apple Login loadUser : {}", userRequest); - return appleOAuth2UserService.loadUser(userRequest); - } else if ("google".equals(registrationId)) { - log.info("google Login loadUser"); - return customOAuth2UserService.loadUser(userRequest); - } else { - throw new OAuth2AuthenticationException(new OAuth2Error("unsupported_provider"), - "지원되지 않는 공급자입니다: " + registrationId); - } + if ("apple".equals(registrationId)) return appleOAuth2UserService.loadUser(userRequest); + if ("google".equals(registrationId)) return customOAuth2UserService.loadUser(userRequest); + throw new OAuth2AuthenticationException(new OAuth2Error("unsupported_provider"), + "지원되지 않는 공급자입니다: " + registrationId); }; } - - - @Bean - public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { - AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); - authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); - return authenticationManagerBuilder.build(); - } - - @Bean - public RequestContextListener requestContextListener() { - return new RequestContextListener(); - } - - @Bean - public RoleHierarchy roleHierarchy() { - return RoleHierarchyImpl.fromHierarchy("ROLE_ADMIN > ROLE_MANAGER > ROLE_USER"); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Bean - public AuthorizationRequestRepository authorizationRequestRepository() { - return new HttpSessionOAuth2AuthorizationRequestRepository(); - } - /** - * CORS 설정 - */ - @Bean - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration config = new CorsConfiguration(); - config.setAllowCredentials(true); - config.setAllowedOrigins(List.of( - "http://localhost:3000", - "http://localhost:8080", - BASEURL, - "https://front-end-peach-two.vercel.app", - "https://front-end-dun-mu.vercel.app", - "https://e876-2406-5900-1080-882f-b519-f7cf-62b3-4ba4.ngrok-free.app", - "http://e876-2406-5900-1080-882f-b519-f7cf-62b3-4ba4.ngrok-free.app", - "https://appleid.apple.com" - )); - config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH")); - config.setAllowedHeaders(List.of( - "Authorization", - "Content-Type", - "X-Requested-With", - "Accept", - "Cache-Control", - "X-Refresh-Token" - )); - config.setExposedHeaders(List.of("Authorization")); - config.setMaxAge(3600L); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", config); - return source; - } - - - // User 권한 URL - private static final String[] USER_AUTH_PATHS = { - "/v3/api/test2", - "/v3/api/test3", - }; - - // Admin 권한 URL - private static final String[] ADMIN_AUTH_PATHS = { - "/v3/api/test4", - }; - - // Manager 권한 URL - private static final String[] MANAGER_AUTH_PATHS = { - "/v3/api/test5", - }; -} +} \ No newline at end of file diff --git a/codin-core/src/main/java/inu/codin/codin/common/config/WebConfig.java b/codin-core/src/main/java/inu/codin/codin/common/config/WebConfig.java index 27c8112c..128ea07e 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/config/WebConfig.java +++ b/codin-core/src/main/java/inu/codin/codin/common/config/WebConfig.java @@ -1,6 +1,6 @@ package inu.codin.codin.common.config; -import inu.codin.codin.common.util.MultipartJackson2HttpMessageConverter; +import inu.codin.common.util.MultipartJackson2HttpMessageConverter; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; diff --git a/codin-core/src/main/java/inu/codin/codin/common/config/WebSocketConfig.java b/codin-core/src/main/java/inu/codin/codin/common/config/WebSocketConfig.java index ed6205a6..a259e022 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/config/WebSocketConfig.java +++ b/codin-core/src/main/java/inu/codin/codin/common/config/WebSocketConfig.java @@ -1,6 +1,6 @@ package inu.codin.codin.common.config; -import inu.codin.codin.common.security.service.JwtService; +import inu.codin.security.service.JwtService; import inu.codin.codin.common.stomp.HttpHandShakeInterceptor; import inu.codin.codin.common.stomp.StompMessageProcessor; import lombok.RequiredArgsConstructor; diff --git a/codin-core/src/main/java/inu/codin/codin/common/exception/GlobalExceptionHandler.java b/codin-core/src/main/java/inu/codin/codin/common/exception/GlobalExceptionHandler.java index 88d55c4b..1d5dd37a 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/exception/GlobalExceptionHandler.java +++ b/codin-core/src/main/java/inu/codin/codin/common/exception/GlobalExceptionHandler.java @@ -1,7 +1,10 @@ package inu.codin.codin.common.exception; -import inu.codin.codin.common.response.ExceptionResponse; -import inu.codin.codin.common.security.exception.JwtException; +import inu.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalException; +import inu.codin.common.exception.NotFoundException; +import inu.codin.common.response.ExceptionResponse; +import inu.codin.security.exception.JwtException; import inu.codin.codin.domain.block.exception.BlockErrorCode; import inu.codin.codin.domain.block.exception.BlockException; import inu.codin.codin.domain.board.notice.exception.NoticeErrorCode; @@ -14,6 +17,7 @@ import inu.codin.codin.domain.chat.exception.ChattingException; import inu.codin.codin.domain.info.exception.InfoErrorCode; import inu.codin.codin.domain.info.exception.InfoException; +import inu.codin.security.exception.SecurityErrorCode; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.RedisSystemException; import org.springframework.http.HttpStatus; @@ -83,12 +87,11 @@ protected ResponseEntity handleNotFoundException(NotFoundExce @ExceptionHandler(JwtException.class) protected ResponseEntity handleJwtException(JwtException e) { - if (e.getErrorCode().getErrorCode().equals("SEC_005")){ - return ResponseEntity.status(HttpStatus.FORBIDDEN) - .body(new ExceptionResponse(e.getMessage(), HttpStatus.FORBIDDEN.value())); - } - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(new ExceptionResponse(e.getMessage(), HttpStatus.UNAUTHORIZED.value())); + SecurityErrorCode code = e.getErrorCode(); + HttpStatus status = code.httpStatus(); + + return ResponseEntity.status(status) + .body(new ExceptionResponse(code.message(), status.value())); } @ExceptionHandler(MethodArgumentNotValidException.class) diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/controller/AuthController.java b/codin-core/src/main/java/inu/codin/codin/common/security/controller/AuthController.java index 3fc22dae..d9bbed3f 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/controller/AuthController.java +++ b/codin-core/src/main/java/inu/codin/codin/common/security/controller/AuthController.java @@ -1,10 +1,10 @@ package inu.codin.codin.common.security.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.common.security.dto.SignUpAndLoginRequestDto; import inu.codin.codin.common.security.service.AuthCommonService; import inu.codin.codin.common.security.service.AuthSessionService; -import inu.codin.codin.common.security.service.JwtService; +import inu.codin.security.service.JwtService; import inu.codin.codin.domain.user.dto.request.UserProfileRequestDto; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -32,8 +32,11 @@ public class AuthController { @GetMapping("/google") public ResponseEntity> googleLogin( HttpServletResponse response, - @RequestParam(required = false, value = "redirect_url") String redirect_url) throws IOException { - authSessionService.setSession(redirect_url); + @RequestParam(required = false, value = "redirect_host") String redirect_host, + @RequestParam(required = false, value = "redirect_path") String redirect_path + ) throws IOException { + authSessionService.setRedirectHostSession(redirect_host); + authSessionService.setRedirectPathSession(redirect_path); response.sendRedirect("/api/oauth2/authorization/google"); return ResponseEntity.ok() .body(new SingleResponse<>(200, "google OAuth2 Login Redirect",null)); diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/dto/PortalLoginResponseDto.java b/codin-core/src/main/java/inu/codin/codin/common/security/dto/PortalLoginResponseDto.java index 018bdeae..2509b5a2 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/dto/PortalLoginResponseDto.java +++ b/codin-core/src/main/java/inu/codin/codin/common/security/dto/PortalLoginResponseDto.java @@ -1,6 +1,6 @@ package inu.codin.codin.common.security.dto; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/exception/SecurityErrorCode.java b/codin-core/src/main/java/inu/codin/codin/common/security/exception/SecurityErrorCode.java deleted file mode 100644 index b6f5b5d2..00000000 --- a/codin-core/src/main/java/inu/codin/codin/common/security/exception/SecurityErrorCode.java +++ /dev/null @@ -1,22 +0,0 @@ -package inu.codin.codin.common.security.exception; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public enum SecurityErrorCode { - - INVALID_TOKEN("SEC_001", "유효하지 않은 토큰입니다."), - EXPIRED_TOKEN("SEC_002", "만료된 토큰입니다."), - TOKEN_NOT_FOUND("SEC_003", "토큰이 존재하지 않습니다."), - INVALID_SIGNATURE("SEC_004", "잘못된 토큰 서명입니다."), - ACCESS_DENIED("SEC_005", "접근 권한이 없습니다."), - ACCOUNT_LOCKED("SEC_006", "계정이 잠겼습니다. 관리자에게 문의하세요."), - INVALID_CREDENTIALS("SEC_007", "잘못된 인증 정보입니다."), - INVALID_TYPE("SEC_008", "잘못된 토큰 타입입니다."); - - private final String errorCode; - private final String message; - -} diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/jwt/JwtTokenProvider.java b/codin-core/src/main/java/inu/codin/codin/common/security/jwt/JwtTokenProvider.java index 2cee57fe..87fcbd90 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/jwt/JwtTokenProvider.java +++ b/codin-core/src/main/java/inu/codin/codin/common/security/jwt/JwtTokenProvider.java @@ -1,7 +1,7 @@ package inu.codin.codin.common.security.jwt; -import inu.codin.codin.common.security.exception.JwtException; -import inu.codin.codin.common.security.exception.SecurityErrorCode; +import inu.codin.security.exception.JwtException; +import inu.codin.security.exception.SecurityErrorCode; import inu.codin.codin.domain.user.security.CustomUserDetails; import inu.codin.codin.infra.redis.RedisStorageService; import io.jsonwebtoken.Claims; diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/service/AuthCommonService.java b/codin-core/src/main/java/inu/codin/codin/common/security/service/AuthCommonService.java index 2bf3d76b..6b6bc66d 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/service/AuthCommonService.java +++ b/codin-core/src/main/java/inu/codin/codin/common/security/service/AuthCommonService.java @@ -1,12 +1,12 @@ package inu.codin.codin.common.security.service; -import inu.codin.codin.common.exception.NotFoundException; +import inu.codin.common.exception.NotFoundException; import inu.codin.codin.common.security.dto.SignUpAndLoginRequestDto; import inu.codin.codin.domain.user.dto.request.UserProfileRequestDto; import inu.codin.codin.domain.user.entity.UserEntity; import inu.codin.codin.domain.user.exception.UserCreateFailException; import inu.codin.codin.domain.user.exception.UserNicknameDuplicateException; -import inu.codin.codin.common.security.service.AbstractAuthService; +import inu.codin.security.service.JwtService; import inu.codin.codin.domain.user.repository.UserRepository; import inu.codin.codin.infra.s3.S3Service; import jakarta.servlet.http.HttpServletResponse; @@ -16,7 +16,7 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; - +import inu.codin.codin.common.security.service.oauth2.AbstractAuthService; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/service/AuthSessionService.java b/codin-core/src/main/java/inu/codin/codin/common/security/service/AuthSessionService.java index d019b15c..10f1f5db 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/service/AuthSessionService.java +++ b/codin-core/src/main/java/inu/codin/codin/common/security/service/AuthSessionService.java @@ -12,7 +12,11 @@ public class AuthSessionService { private final HttpSession httpSession; - public void setSession(String redirectUrl) { - if (!Objects.equals(redirectUrl, null)) httpSession.setAttribute("redirect_url", redirectUrl); + public void setRedirectHostSession(String redirectHost) { + if (!Objects.equals(redirectHost, null)) httpSession.setAttribute("redirect_host", redirectHost); + } + + public void setRedirectPathSession(String redirectPath) { + if (!Objects.equals(redirectPath, null)) httpSession.setAttribute("redirect_path", redirectPath); } } diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/service/JwtService.java b/codin-core/src/main/java/inu/codin/codin/common/security/service/JwtService.java deleted file mode 100644 index 9c0280fe..00000000 --- a/codin-core/src/main/java/inu/codin/codin/common/security/service/JwtService.java +++ /dev/null @@ -1,242 +0,0 @@ -package inu.codin.codin.common.security.service; - -import inu.codin.codin.common.security.exception.JwtException; -import inu.codin.codin.common.security.exception.SecurityErrorCode; -import inu.codin.codin.common.security.jwt.JwtAuthenticationToken; -import inu.codin.codin.common.security.jwt.JwtTokenProvider; -import inu.codin.codin.common.security.jwt.JwtUtils; -import inu.codin.codin.domain.user.security.CustomUserDetailsService; -import inu.codin.codin.infra.redis.RedisStorageService; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.MalformedJwtException; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; - -/** - * JWT 토큰 관련 비즈니스 로직을 처리하는 서비스 - * - */ -@Service -@Slf4j -@RequiredArgsConstructor -public class JwtService { - - private final RedisStorageService redisStorageService; - private final JwtTokenProvider jwtTokenProvider; - private final JwtUtils jwtUtils; - private final CustomUserDetailsService userDetailsService; - - @Value("${server.domain}") - private String BASEURL; - - private final String REFRESH_TOKEN = "x-refresh-token"; - private final String ACCESS_TOKEN = "Authorization"; - private final String ACCESS_TOKEN_PREFIX = "Bearer "; - - /** - * 최초 로그인 시 Access Token, Refresh Token 발급 - * @param response - */ - public void createToken(HttpServletResponse response) { - createBothToken(response); - log.info("[createToken] Access Token, Refresh Token 발급 완료"); - } - - /** - * Refresh Token을 이용하여 Access Token, Refresh Token 재발급 - * @param request - * @param response - */ - public void checkRefreshTokenAndReissue(HttpServletRequest request, HttpServletResponse response) { - String refreshToken = getRefreshToken(request); - if (refreshToken == null) { - log.error("[reissueToken] Refresh Token이 없습니다."); - throw new JwtException(SecurityErrorCode.INVALID_TOKEN, "Refresh Token이 없습니다."); - } - - String username = jwtTokenProvider.getUsername(refreshToken); - validateRefreshTokenWithAccessToken(request, username); - UserDetails userDetails = userDetailsService.loadUserByUsername(username); - - // 토큰이 유효하고, SecurityContext에 Authentication 객체가 없는 경우 - if (userDetails != null) { - // Authentication 객체 생성 후 SecurityContext에 저장 (인증 완료) - JwtAuthenticationToken authentication = new JwtAuthenticationToken(userDetails, userDetails.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - - reissueToken(refreshToken, response); - } - - //만료된 accessToken과 username을 비교 - private void validateRefreshTokenWithAccessToken(HttpServletRequest request, String username) { - String accessToken = getAccessToken(request); - String accessUsername; - try { - accessUsername = jwtTokenProvider.getUsername(accessToken); - } catch (ExpiredJwtException e) { - Claims expiredClaims = e.getClaims(); - accessUsername = expiredClaims.getSubject(); - } - if (!accessUsername.equals(username)) { - log.error("[reissueToken] Access Token의 username과 Refresh Token가 일치하지 않습니다."); - throw new JwtException(SecurityErrorCode.INVALID_TOKEN, "Access Token의 username과 Refresh Token가 일치하지 않습니다."); - } - } - - /** - * Refresh Token을 이용하여 Access Token, Refresh Token 재발급 - * @param refreshToken - * @param response - */ - public void reissueToken(String refreshToken, HttpServletResponse response) { - if (!jwtTokenProvider.validateRefreshToken(refreshToken)) { - log.error("[reissueToken] Refresh Token이 유효하지 않습니다. : {}", refreshToken); - throw new JwtException(SecurityErrorCode.INVALID_TOKEN, "Refresh Token이 유효하지 않습니다."); - } - - createBothToken(response); - log.info("[reissueToken] Access Token, Refresh Token 재발급 완료"); - } - - /** - * Access Token, Refresh Token 생성 - */ - private void createBothToken(HttpServletResponse response) { - // 새로운 Access Token 발급 - var authentication = SecurityContextHolder.getContext().getAuthentication(); - JwtTokenProvider.TokenDto newToken = jwtTokenProvider.createToken(authentication); - - // Authorization 헤더에 Access Token 추가 - response.setHeader(ACCESS_TOKEN, ACCESS_TOKEN_PREFIX + newToken.getAccessToken()); - - // todo: x-access-token 쿠키에 Access Token 추가 - 추후 제거 - Cookie newAccessToken = new Cookie("x-access-token", newToken.getAccessToken()); - newAccessToken.setHttpOnly(true); - newAccessToken.setSecure(true); - newAccessToken.setPath("/"); - newAccessToken.setMaxAge(10 * 24 * 60 * 60); // 10일 - newAccessToken.setDomain(BASEURL.split("//")[1]); - newAccessToken.setAttribute("SameSite", "None"); - response.addCookie(newAccessToken); - - // x-rfresh-token 쿠키에 Refresh Token 추가 - Cookie newRefreshToken = new Cookie(REFRESH_TOKEN, newToken.getRefreshToken()); - newRefreshToken.setHttpOnly(true); - newRefreshToken.setSecure(true); - newRefreshToken.setPath("/"); - newRefreshToken.setMaxAge(10 * 24 * 60 * 60); // 10일 - newRefreshToken.setDomain(BASEURL.split("//")[1]); - newRefreshToken.setAttribute("SameSite", "None"); - response.addCookie(newRefreshToken); - - log.info("[createBothToken] Access Token, Refresh Token 발급 완료, email = {}, Access: {}", authentication.getName(), newToken.getAccessToken()); - } - - - /** - * 로그아웃 - - * Access,Refresh Token 제거/ 서버측 RT 삭제 - */ - public void deleteToken(HttpServletResponse response) { - // 어차피 JwtAuthenticationFilter 단에서 토큰을 검증하여 인증을 처리하므로 - // SecurityContext에 Authentication 객체가 없는 경우는 없다. - var authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null && authentication.getName()!=null){ - redisStorageService.deleteRefreshToken(authentication.getName()); - deleteCookie(response); - log.info("[deleteToken] Refresh Token 삭제 완료"); - } - } - - private void deleteCookie(HttpServletResponse response) { - - String domain = BASEURL.replaceFirst("https?://", "").split(":")[0]; - log.info("[deleteCookie] BASEURL={}, domain={}", BASEURL, domain); - - Cookie refreshCookie = new Cookie(REFRESH_TOKEN, ""); - refreshCookie.setHttpOnly(true); - refreshCookie.setSecure(true); - refreshCookie.setPath("/"); - refreshCookie.setDomain(domain); - refreshCookie.setMaxAge(0); - refreshCookie.setAttribute("SameSite","None"); - response.addCookie(refreshCookie); - - log.info("[deleteCookie] refreshCookie info => name={}, domain={}, path={}, secure={}, httpOnly={}, maxAge={}, sameSite=None", - refreshCookie.getName(), refreshCookie.getDomain(), refreshCookie.getPath(), - refreshCookie.getSecure(), refreshCookie.isHttpOnly(), refreshCookie.getMaxAge()); - - Cookie accessCookie = new Cookie("x-access-token", ""); - accessCookie.setHttpOnly(true); - accessCookie.setSecure(true); - accessCookie.setPath("/"); - accessCookie.setDomain(domain); - accessCookie.setMaxAge(0); - refreshCookie.setAttribute("SameSite","None"); - response.addCookie(accessCookie); - - log.info("[deleteCookie] accessCookie info => name={}, domain={}, path={}, secure={}, httpOnly={}, maxAge={}, sameSite=None", - accessCookie.getName(), accessCookie.getDomain(), accessCookie.getPath(), - accessCookie.getSecure(), accessCookie.isHttpOnly(), accessCookie.getMaxAge()); - - log.info("[deleteToken] Access/Refresh Cookie 삭제 완료"); - } - - public void setAuthentication(HttpServletRequest request){ - String accessToken = getAccessToken(request); - - // Access Token이 있는 경우 - if (accessToken != null) { - getUserDetailsAndSetAuthentication(accessToken); - } else { - SecurityContextHolder.clearContext(); - throw new MalformedJwtException("[Chatting] JWT를 찾을 수 없습니다."); - } - } - - public void getUserDetailsAndSetAuthentication(String token) { - jwtTokenProvider.validateToken(token); - String email = jwtTokenProvider.getUsername(token); - UserDetails userDetails = userDetailsService.loadUserByUsername(email); - - // 토큰이 유효하고, SecurityContext에 Authentication 객체가 없는 경우 - if (userDetails != null) { - // Authentication 객체 생성 후 SecurityContext에 저장 (인증 완료) - JwtAuthenticationToken authentication = new JwtAuthenticationToken(userDetails, userDetails.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - } - - public String getAccessToken(HttpServletRequest request) { - String accessToken = jwtUtils.getAccessToken(request); - - if (!StringUtils.hasText(accessToken)) { - return null; - } - - if (!jwtTokenProvider.validType(accessToken, "access")) { - log.error("[getAccessToken] Access Token이 아닙니다."); - throw new JwtException(SecurityErrorCode.INVALID_TYPE, "Access Token이 아닙니다."); - } - return accessToken; - } - - public String getRefreshToken(HttpServletRequest request) { - String refreshToken = jwtUtils.getRefreshToken(request); - if (!jwtTokenProvider.validType(refreshToken, "refresh")) { - log.error("[getRefreshToken] Refresh Token이 아닙니다."); - throw new JwtException(SecurityErrorCode.INVALID_TYPE, "Refresh Token이 아닙니다."); - } - return refreshToken; - } -} \ No newline at end of file diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/AbstractAuthService.java b/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/AbstractAuthService.java index 7b57afbc..1d061ec7 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/AbstractAuthService.java +++ b/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/AbstractAuthService.java @@ -1,7 +1,8 @@ -package inu.codin.codin.common.security.service; +package inu.codin.codin.common.security.service.oauth2; import inu.codin.codin.domain.user.repository.UserRepository; import inu.codin.codin.infra.s3.S3Service; +import inu.codin.security.service.JwtService; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -19,12 +20,15 @@ public abstract class AbstractAuthService { protected final UserDetailsService userDetailsService; protected void issueJwtToken(String identifier, HttpServletResponse response) { - jwtService.deleteToken(response); - UserDetails userDetails = userDetailsService.loadUserByUsername(identifier); - UsernamePasswordAuthenticationToken authToken = - new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(authToken); - jwtService.createToken(response); + // TODO: Phase 2에서 codin-auth로 이동 예정 + // 토큰 발급 기능은 Authorization Server에서 담당 + // jwtService.deleteToken(response); + // UserDetails userDetails = userDetailsService.loadUserByUsername(identifier); + // UsernamePasswordAuthenticationToken authToken = + // new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); + // SecurityContextHolder.getContext().setAuthentication(authToken); + // jwtService.createToken(response); + throw new UnsupportedOperationException("토큰 발급 기능은 Phase 2에서 codin-auth 모듈로 이동 예정"); } } diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/AppleAuthService.java b/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/AppleAuthService.java index 380bc517..ab6aa129 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/AppleAuthService.java +++ b/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/AppleAuthService.java @@ -1,10 +1,9 @@ package inu.codin.codin.common.security.service.oauth2; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.exception.NotFoundException; +import inu.codin.common.dto.Department; +import inu.codin.common.exception.NotFoundException; import inu.codin.codin.common.security.enums.AuthResultStatus; -import inu.codin.codin.common.security.service.AbstractAuthService; -import inu.codin.codin.common.security.service.JwtService; +import inu.codin.security.service.JwtService; import inu.codin.codin.domain.user.entity.UserEntity; import inu.codin.codin.domain.user.entity.UserRole; import inu.codin.codin.domain.user.entity.UserStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/GoogleAuthService.java b/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/GoogleAuthService.java index 51e75860..e8bc330c 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/GoogleAuthService.java +++ b/codin-core/src/main/java/inu/codin/codin/common/security/service/oauth2/GoogleAuthService.java @@ -1,10 +1,9 @@ package inu.codin.codin.common.security.service.oauth2; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.exception.NotFoundException; +import inu.codin.common.dto.Department; +import inu.codin.common.exception.NotFoundException; import inu.codin.codin.common.security.enums.AuthResultStatus; -import inu.codin.codin.common.security.service.AbstractAuthService; -import inu.codin.codin.common.security.service.JwtService; +import inu.codin.security.service.JwtService; import inu.codin.codin.domain.user.entity.UserEntity; import inu.codin.codin.domain.user.entity.UserRole; import inu.codin.codin.domain.user.entity.UserStatus; @@ -32,15 +31,21 @@ public GoogleAuthService(UserRepository userRepository, S3Service s3Service, Jwt public AuthResultStatus oauthLogin(OAuth2User oAuth2User, HttpServletResponse response) { // Google에서는 email, family_name, given_name 등 제공됨. Map attributes = oAuth2User.getAttributes(); + log.info("OAuth2User attributes: {}", attributes); + String email = (String) attributes.get("email"); - String name = (String) attributes.get("family_name"); - if (name == null || name.isEmpty()) { - name = (String) attributes.get("name"); - } - String department = (String) attributes.get("given_name"); - if (department == null) { - department = ""; - } + String familyName = (String) attributes.get("family_name"); + String givenName = (String) attributes.get("given_name"); + String fullName = (String) attributes.get("name"); + + String name = (familyName != null && !familyName.isEmpty()) + ? familyName + : (fullName != null ? fullName : ""); + + String department = (givenName != null) ? givenName : ""; + + log.info("OAuth2 login parsed values -> email={}, family_name={}, given_name={}, name={}, department={}", + email, familyName, givenName, fullName, department); log.info("OAuth2 login: email={}, name={}, department={}", email, name, department); diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/util/CustomAuthenticationEntryPoint.java b/codin-core/src/main/java/inu/codin/codin/common/security/util/CustomAuthenticationEntryPoint.java index 342d6728..e18d0e81 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/util/CustomAuthenticationEntryPoint.java +++ b/codin-core/src/main/java/inu/codin/codin/common/security/util/CustomAuthenticationEntryPoint.java @@ -1,7 +1,7 @@ package inu.codin.codin.common.security.util; import com.fasterxml.jackson.databind.ObjectMapper; -import inu.codin.codin.common.response.ExceptionResponse; +import inu.codin.common.response.ExceptionResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/util/OAuth2LoginFailureHandler.java b/codin-core/src/main/java/inu/codin/codin/common/security/util/OAuth2LoginFailureHandler.java index f45f98b4..e9af4cbf 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/util/OAuth2LoginFailureHandler.java +++ b/codin-core/src/main/java/inu/codin/codin/common/security/util/OAuth2LoginFailureHandler.java @@ -1,8 +1,8 @@ package inu.codin.codin.common.security.util; import com.fasterxml.jackson.databind.ObjectMapper; -import inu.codin.codin.common.response.ExceptionResponse; -import inu.codin.codin.common.security.service.JwtService; +import inu.codin.common.response.ExceptionResponse; +import inu.codin.security.service.JwtService; import inu.codin.codin.common.util.CookieUtil; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/util/OAuth2LoginSuccessHandler.java b/codin-core/src/main/java/inu/codin/codin/common/security/util/OAuth2LoginSuccessHandler.java index ce23c404..1ce354d3 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/util/OAuth2LoginSuccessHandler.java +++ b/codin-core/src/main/java/inu/codin/codin/common/security/util/OAuth2LoginSuccessHandler.java @@ -55,29 +55,42 @@ private void handleLoginResult(HttpServletRequest request, HttpServletResponse r response.setContentType("application/json;charset=UTF-8"); PrintWriter writer = response.getWriter(); - String redirectUrl = (String) request.getSession().getAttribute("redirect_url"); - if (Objects.equals(redirectUrl, null)) redirectUrl = BASEURL; - request.getSession().removeAttribute("redirect_url"); + String redirectHost = (String) request.getSession().getAttribute("redirect_host"); + String redirectPath = (String) request.getSession().getAttribute("redirect_path"); + + boolean isPathExists = true; + if (Objects.isNull(redirectHost)) { + redirectHost = BASEURL; + } + if (Objects.isNull(redirectPath)) { + isPathExists = false; + } + request.getSession().removeAttribute("redirect_host"); + request.getSession().removeAttribute("redirect_path"); switch (result) { case LOGIN_SUCCESS -> { - getRedirectStrategy().sendRedirect(request, response, redirectUrl); + if (isPathExists) { + getRedirectStrategy().sendRedirect(request, response, redirectHost + redirectPath); + } else { + getRedirectStrategy().sendRedirect(request, response, redirectHost + "/main"); + } log.info("{\"code\":200, \"message\":\"정상 로그인 완료: {}\"}", email); } case NEW_USER_REGISTERED -> { - getRedirectStrategy().sendRedirect(request, response, redirectUrl + "/auth/profile?email=" + email); + getRedirectStrategy().sendRedirect(request, response, redirectHost + "/auth/profile?email=" + email); log.info("{\"code\":201, \"message\":\"신규 회원 등록 완료: {}\"}", email); } case PROFILE_INCOMPLETE -> { - getRedirectStrategy().sendRedirect(request, response, redirectUrl + "/auth/profile?email=" + email); + getRedirectStrategy().sendRedirect(request, response, redirectHost + "/auth/profile?email=" + email); log.info("{\"code\":200, \"message\":\"회원 프로필 설정 미완료: {}\"}", email); } case SUSPENDED_USER -> { - getRedirectStrategy().sendRedirect(request, response, redirectUrl + "/api/suspends"); + getRedirectStrategy().sendRedirect(request, response, redirectHost + "/api/suspends"); log.info("{\"code\":200, \"message\":\"정지된 회원: {}\"}", email); } default -> { - getRedirectStrategy().sendRedirect(request, response, redirectUrl + "/login"); + getRedirectStrategy().sendRedirect(request, response, redirectHost + "/login"); log.info("{\"code\":500, \"message\":\"알 수 없는 오류 발생: {}\"}", email); } } diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/util/SecurityUtils.java b/codin-core/src/main/java/inu/codin/codin/common/security/util/SecurityUtils.java deleted file mode 100644 index 977514de..00000000 --- a/codin-core/src/main/java/inu/codin/codin/common/security/util/SecurityUtils.java +++ /dev/null @@ -1,76 +0,0 @@ -package inu.codin.codin.common.security.util; - -import inu.codin.codin.common.security.exception.JwtException; -import inu.codin.codin.common.security.exception.SecurityErrorCode; -import inu.codin.codin.domain.user.entity.UserRole; -import inu.codin.codin.domain.user.security.CustomUserDetails; -import lombok.extern.slf4j.Slf4j; -import org.bson.types.ObjectId; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; - -/** - * SecurityContext와 관련된 유틸리티 클래스. - */ -@Slf4j -public class SecurityUtils { - - /** - * 현재 인증된 사용자의 ID를 반환. - * - * @return 인증된 사용자의 ID - * @throws JwtException 인증 정보가 없는 경우 예외 발생 - */ - public static ObjectId getCurrentUserId() { - log.info("getCurrentUserId."); - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - log.info("auth={} / principalClass={}", authentication, (authentication!=null ? authentication.getPrincipal().getClass() : null)); - if (authentication == null || !(authentication.getPrincipal() instanceof CustomUserDetails userDetails)) { - throw new JwtException(SecurityErrorCode.ACCESS_DENIED); - } - - return userDetails.getId(); - } - - - /** - * 현재 인증된 사용자의 ID를 반환 (nullable 안전 버전) - * - 인증이 없거나 익명이면 null 반환 - */ - public static ObjectId getCurrentUserIdOrNull() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication == null || !authentication.isAuthenticated() - || authentication instanceof AnonymousAuthenticationToken - || !(authentication.getPrincipal() instanceof CustomUserDetails userDetails)) { - return null; - } - - return userDetails.getId(); - } - - public static UserRole getCurrentUserRole(){ - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication == null || !(authentication.getPrincipal() instanceof CustomUserDetails userDetails)) { - throw new JwtException(SecurityErrorCode.ACCESS_DENIED); - } - - return userDetails.getRole(); - } - - public static void validateUser(ObjectId id){ - ObjectId userId = SecurityUtils.getCurrentUserId(); - if (!id.equals(userId)) { - throw new JwtException(SecurityErrorCode.ACCESS_DENIED, "현재 유저에게 권한이 없습니다."); - } - } - - public static void validateOwners(ObjectId currentUserId, ObjectId ownerId) { - validateUser(currentUserId); - if (!ownerId.equals(currentUserId)) { - throw new JwtException(SecurityErrorCode.ACCESS_DENIED, "본인 리소스가 아닙니다. "); - } - } -} diff --git a/codin-core/src/main/java/inu/codin/codin/common/stomp/HttpHandShakeInterceptor.java b/codin-core/src/main/java/inu/codin/codin/common/stomp/HttpHandShakeInterceptor.java index 9c8e1c8a..998c3ffb 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/stomp/HttpHandShakeInterceptor.java +++ b/codin-core/src/main/java/inu/codin/codin/common/stomp/HttpHandShakeInterceptor.java @@ -1,8 +1,8 @@ package inu.codin.codin.common.stomp; -import inu.codin.codin.common.security.exception.JwtException; -import inu.codin.codin.common.security.exception.SecurityErrorCode; -import inu.codin.codin.common.security.service.JwtService; +import inu.codin.security.exception.JwtException; +import inu.codin.security.exception.SecurityErrorCode; +import inu.codin.security.service.JwtService; import io.jsonwebtoken.MalformedJwtException; import lombok.RequiredArgsConstructor; import org.springframework.http.server.ServerHttpRequest; diff --git a/codin-core/src/main/java/inu/codin/codin/common/stomp/StompMessageService.java b/codin-core/src/main/java/inu/codin/codin/common/stomp/StompMessageService.java index 410682f1..878e0939 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/stomp/StompMessageService.java +++ b/codin-core/src/main/java/inu/codin/codin/common/stomp/StompMessageService.java @@ -1,6 +1,6 @@ package inu.codin.codin.common.stomp; -import inu.codin.codin.common.exception.NotFoundException; +import inu.codin.common.exception.NotFoundException; import inu.codin.codin.domain.chat.chatroom.entity.ChatRoom; import inu.codin.codin.domain.chat.chatroom.repository.ChatRoomRepository; import inu.codin.codin.domain.chat.chatting.dto.event.UpdateUnreadCountEvent; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/block/controller/BlockController.java b/codin-core/src/main/java/inu/codin/codin/domain/block/controller/BlockController.java index 475b3471..3a5e40b8 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/block/controller/BlockController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/block/controller/BlockController.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.block.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.block.service.BlockService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/block/entity/BlockEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/block/entity/BlockEntity.java index 4b74eb8b..6380d2ab 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/block/entity/BlockEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/block/entity/BlockEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.block.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import lombok.Builder; import lombok.Getter; import org.bson.types.ObjectId; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/block/exception/BlockErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/block/exception/BlockErrorCode.java index d99b98bd..aafeff0c 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/block/exception/BlockErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/block/exception/BlockErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.block.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/block/exception/BlockException.java b/codin-core/src/main/java/inu/codin/codin/domain/block/exception/BlockException.java index a7fc2d36..1f6f9e24 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/block/exception/BlockException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/block/exception/BlockException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.block.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/block/service/BlockService.java b/codin-core/src/main/java/inu/codin/codin/domain/block/service/BlockService.java index 0374cac5..36f9bc0e 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/block/service/BlockService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/block/service/BlockService.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.block.service; -import inu.codin.codin.common.security.util.SecurityUtils; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.security.util.SecurityUtil; +import inu.codin.common.util.ObjectIdUtil; import inu.codin.codin.domain.block.entity.BlockEntity; import inu.codin.codin.domain.block.exception.BlockErrorCode; import inu.codin.codin.domain.block.exception.BlockException; @@ -14,6 +14,8 @@ import java.util.List; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @Slf4j @RequiredArgsConstructor @@ -27,7 +29,7 @@ public class BlockService { * @param strBlockedUserId */ public void blockUser(String strBlockedUserId) { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); ObjectId blockedId = ObjectIdUtil.toObjectId(strBlockedUserId); if (userId.equals(blockedId)) { @@ -55,7 +57,7 @@ public void blockUser(String strBlockedUserId) { * @param strBlockedUserId 차단 해제할 유저 */ public void unblockUser(String strBlockedUserId) { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); ObjectId blockedId = ObjectIdUtil.toObjectId(strBlockedUserId); if (userId.equals(blockedId)) { @@ -82,12 +84,12 @@ public void unblockUser(String strBlockedUserId) { * @return 차단한 유저 목록 (빈 리스트가 제공될 수 있음) */ public List getBlockedUsers() { - ObjectId currentUserId = SecurityUtils.getCurrentUserIdOrNull(); + ObjectId currentUserId = toObjectId(SecurityUtil.getCurrentUserIdOrNull()); if (currentUserId == null) { return List.of(); } - return blockRepository.findByUserId(SecurityUtils.getCurrentUserId()) + return blockRepository.findByUserId(toObjectId(SecurityUtil.getCurrentUserId())) .map(BlockEntity::getBlockedUsers) .orElse(List.of()); } diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/notice/controller/NoticeController.java b/codin-core/src/main/java/inu/codin/codin/domain/board/notice/controller/NoticeController.java index 92300c67..3b72ab54 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/notice/controller/NoticeController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/notice/controller/NoticeController.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.board.notice.controller; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.dto.Department; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.board.notice.dto.request.NoticeCreateUpdateRequestDTO; import inu.codin.codin.domain.board.notice.dto.response.NoticeDetailResponseDto; import inu.codin.codin.domain.board.notice.dto.response.NoticePageResponse; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/notice/exception/NoticeErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/board/notice/exception/NoticeErrorCode.java index f19fbe6e..5d88b796 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/notice/exception/NoticeErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/notice/exception/NoticeErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.board.notice.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/notice/exception/NoticeException.java b/codin-core/src/main/java/inu/codin/codin/domain/board/notice/exception/NoticeException.java index 73a8e483..3599e3b6 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/notice/exception/NoticeException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/notice/exception/NoticeException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.board.notice.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/notice/service/NoticeService.java b/codin-core/src/main/java/inu/codin/codin/domain/board/notice/service/NoticeService.java index fc6fca1d..1787e039 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/notice/service/NoticeService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/notice/service/NoticeService.java @@ -1,7 +1,8 @@ package inu.codin.codin.domain.board.notice.service; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.common.dto.Department; +import inu.codin.common.util.ObjectIdUtil; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.board.notice.dto.request.NoticeCreateUpdateRequestDTO; import inu.codin.codin.domain.board.notice.dto.response.NoticeDetailResponseDto; import inu.codin.codin.domain.board.notice.dto.response.NoticeListResponseDto; @@ -32,6 +33,8 @@ import java.util.Map; import java.util.regex.Pattern; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @Slf4j @RequiredArgsConstructor @@ -81,7 +84,7 @@ public NoticeDetailResponseDto getNoticesWithDetail(String postId) { */ public Map createNotice(NoticeCreateUpdateRequestDTO noticeCreateUpdateRequestDTO, List noticeImages) { List imageUrls = s3Service.handleImageUpload(noticeImages); - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); validateUserAndPost(userId); UserEntity user = getUserEntity(userId); @@ -149,7 +152,7 @@ private UserEntity getUserEntity(ObjectId userId) { } private NoticeDetailResponseDto.UserInfo getUserInfoAboutNotice(ObjectId postUserId, ObjectId postId){ - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); return NoticeDetailResponseDto.UserInfo.builder() .isScrap(scrapService.isPostScraped(postId, userId)) .isMine(postUserId.equals(userId)) @@ -157,9 +160,9 @@ private NoticeDetailResponseDto.UserInfo getUserInfoAboutNotice(ObjectId postUse } private void validateUserAndPost(ObjectId postUserId) { - if (SecurityUtils.getCurrentUserRole().equals(UserRole.USER)) { + if (SecurityUtil.getCurrentUserRole().equals(UserRole.USER)) { throw new NoticeException(NoticeErrorCode.NOTICE_ACCESS_DENIED); } - SecurityUtils.validateUser(postUserId); + SecurityUtil.validateUser(ObjectIdUtil.toString(postUserId)); } } diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/question/controller/QuestionController.java b/codin-core/src/main/java/inu/codin/codin/domain/board/question/controller/QuestionController.java index d9b9b328..85ba680a 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/question/controller/QuestionController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/question/controller/QuestionController.java @@ -1,8 +1,8 @@ package inu.codin.codin.domain.board.question.controller; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.response.ListResponse; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.dto.Department; +import inu.codin.common.response.ListResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.board.question.dto.request.QuestionCreateUpdateRequestDto; import inu.codin.codin.domain.board.question.dto.response.QuestionResponseDto; import inu.codin.codin.domain.board.question.service.QuestionService; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/question/dto/request/QuestionCreateUpdateRequestDto.java b/codin-core/src/main/java/inu/codin/codin/domain/board/question/dto/request/QuestionCreateUpdateRequestDto.java index 66065957..3a86bc7c 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/question/dto/request/QuestionCreateUpdateRequestDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/question/dto/request/QuestionCreateUpdateRequestDto.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.board.question.dto.request; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/question/entity/QuestionEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/board/question/entity/QuestionEntity.java index b36ee990..869c13a9 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/question/entity/QuestionEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/question/entity/QuestionEntity.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.board.question.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.board.question.dto.request.QuestionCreateUpdateRequestDto; import lombok.AccessLevel; import lombok.Builder; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/question/exception/QuestionErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/board/question/exception/QuestionErrorCode.java index cc1e750d..9ec7ca17 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/question/exception/QuestionErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/question/exception/QuestionErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.board.question.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/question/exception/QuestionException.java b/codin-core/src/main/java/inu/codin/codin/domain/board/question/exception/QuestionException.java index 47f166e6..9fa56679 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/question/exception/QuestionException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/question/exception/QuestionException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.board.question.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter public class QuestionException extends GlobalException { diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/question/repository/QuestionRepository.java b/codin-core/src/main/java/inu/codin/codin/domain/board/question/repository/QuestionRepository.java index 75ebcdf4..d0f4badc 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/question/repository/QuestionRepository.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/question/repository/QuestionRepository.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.board.question.repository; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.board.question.entity.QuestionEntity; import org.bson.types.ObjectId; import org.springframework.data.mongodb.repository.MongoRepository; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/question/service/QuestionService.java b/codin-core/src/main/java/inu/codin/codin/domain/board/question/service/QuestionService.java index 882e8f94..1d9aedae 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/question/service/QuestionService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/question/service/QuestionService.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.board.question.service; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.board.question.dto.request.QuestionCreateUpdateRequestDto; import inu.codin.codin.domain.board.question.dto.response.QuestionResponseDto; import inu.codin.codin.domain.board.question.entity.QuestionEntity; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/controller/VoiceController.java b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/controller/VoiceController.java index 50de9eec..47838474 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/controller/VoiceController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/controller/VoiceController.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.board.voice.controller; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.dto.Department; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.board.voice.service.VoiceService; import inu.codin.codin.domain.board.voice.dto.VoiceBoxAnswerRequest; import inu.codin.codin.domain.board.voice.dto.VoiceBoxCreateRequest; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/dto/VoiceBoxCreateRequest.java b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/dto/VoiceBoxCreateRequest.java index de0e1e04..cbc47709 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/dto/VoiceBoxCreateRequest.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/dto/VoiceBoxCreateRequest.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.board.voice.dto; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/dto/VoiceBoxDetailResponse.java b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/dto/VoiceBoxDetailResponse.java index 9876527b..f203055c 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/dto/VoiceBoxDetailResponse.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/dto/VoiceBoxDetailResponse.java @@ -1,8 +1,8 @@ package inu.codin.codin.domain.board.voice.dto; import com.fasterxml.jackson.annotation.JsonFormat; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.common.dto.Department; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.board.voice.entity.VoiceEntity; import lombok.Builder; import lombok.Getter; @@ -36,8 +36,8 @@ public static VoiceBoxDetailResponse of(VoiceEntity voiceEntity) { .department(voiceEntity.getDepartment()) .question(voiceEntity.getQuestion()) .answer(voiceEntity.getAnswer()) - .isUserInPositive(voiceEntity.getPositiveVoteIds() == null ? null : voiceEntity.getPositiveVoteIds().contains(SecurityUtils.getCurrentUserId())) - .isUserInOpposite(voiceEntity.getOppositeVoteIds() == null ? null : voiceEntity.getOppositeVoteIds().contains(SecurityUtils.getCurrentUserId())) + .isUserInPositive(voiceEntity.getPositiveVoteIds() == null ? null : voiceEntity.getPositiveVoteIds().contains(SecurityUtil.getCurrentUserId())) + .isUserInOpposite(voiceEntity.getOppositeVoteIds() == null ? null : voiceEntity.getOppositeVoteIds().contains(SecurityUtil.getCurrentUserId())) .userCountPositive(voiceEntity.getPositiveVoteIds() == null ? null : voiceEntity.getPositiveVoteIds().size()) .userCountOpposite(voiceEntity.getOppositeVoteIds() == null ? null : voiceEntity.getOppositeVoteIds().size()) .createdAt(voiceEntity.getCreatedAt()) diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/entity/VoiceEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/entity/VoiceEntity.java index 387bae28..2707e263 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/entity/VoiceEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/entity/VoiceEntity.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.board.voice.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.Department; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/repository/VoiceRepository.java b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/repository/VoiceRepository.java index 2ce6feaf..d0a7ca7c 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/repository/VoiceRepository.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/repository/VoiceRepository.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.board.voice.repository; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.board.voice.entity.VoiceEntity; import org.bson.types.ObjectId; import org.springframework.data.domain.Page; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/service/VoiceService.java b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/service/VoiceService.java index 973781ba..48f2269f 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/board/voice/service/VoiceService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/board/voice/service/VoiceService.java @@ -1,8 +1,8 @@ package inu.codin.codin.domain.board.voice.service; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.security.util.SecurityUtils; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.common.dto.Department; +import inu.codin.security.util.SecurityUtil; +import inu.codin.common.util.ObjectIdUtil; import inu.codin.codin.domain.board.voice.dto.VoiceBoxCreateRequest; import inu.codin.codin.domain.board.voice.dto.VoiceBoxDetailResponse; import inu.codin.codin.domain.board.voice.dto.VoiceBoxPageResponse; @@ -19,6 +19,8 @@ import java.util.List; import java.util.stream.Collectors; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor public class VoiceService { @@ -51,7 +53,7 @@ public void toggleVoiceBox(String boxId, Boolean positive) { VoiceEntity voiceEntity = voiceRepository.findByIdAndNotDeleted(objectId) .orElseThrow(() -> new IllegalArgumentException("익명의 소리함 질문을 찾을 수 없습니다.")); - ObjectId currentUserId = SecurityUtils.getCurrentUserId(); + ObjectId currentUserId = toObjectId(SecurityUtil.getCurrentUserId()); if (positive) { voiceEntity.votePositiveToggle(currentUserId); diff --git a/codin-core/src/main/java/inu/codin/codin/domain/calendar/controller/CalendarControllerImpl.java b/codin-core/src/main/java/inu/codin/codin/domain/calendar/controller/CalendarControllerImpl.java index ead43c76..1b4d0f8b 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/calendar/controller/CalendarControllerImpl.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/calendar/controller/CalendarControllerImpl.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.calendar.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.calendar.controller.swagger.CalendarController; import inu.codin.codin.domain.calendar.dto.CalendarCreateRequest; import inu.codin.codin.domain.calendar.dto.CalendarCreateResponse; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/calendar/controller/swagger/CalendarController.java b/codin-core/src/main/java/inu/codin/codin/domain/calendar/controller/swagger/CalendarController.java index 303a79f7..453a6cb0 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/calendar/controller/swagger/CalendarController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/calendar/controller/swagger/CalendarController.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.calendar.controller.swagger; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.calendar.dto.CalendarCreateRequest; import inu.codin.codin.domain.calendar.dto.CalendarCreateResponse; import inu.codin.codin.domain.calendar.dto.CalendarMonthResponse; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/CalendarCreateRequest.java b/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/CalendarCreateRequest.java index 0c4ccd02..3887b41f 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/CalendarCreateRequest.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/CalendarCreateRequest.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.calendar.dto; import com.fasterxml.jackson.annotation.JsonFormat; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/CalendarCreateResponse.java b/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/CalendarCreateResponse.java index ef4e9c07..1e5850b3 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/CalendarCreateResponse.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/CalendarCreateResponse.java @@ -1,8 +1,8 @@ package inu.codin.codin.domain.calendar.dto; import com.fasterxml.jackson.annotation.JsonFormat; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.common.dto.Department; +import inu.codin.common.util.ObjectIdUtil; import inu.codin.codin.domain.calendar.entity.CalendarEntity; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/EventDto.java b/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/EventDto.java index f6652180..f879d392 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/EventDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/calendar/dto/EventDto.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.calendar.dto; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.common.dto.Department; +import inu.codin.common.util.ObjectIdUtil; import inu.codin.codin.domain.calendar.entity.CalendarEntity; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/calendar/entity/CalendarEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/calendar/entity/CalendarEntity.java index d9368b2d..a67beb08 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/calendar/entity/CalendarEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/calendar/entity/CalendarEntity.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.calendar.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.Department; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/calendar/exception/CalendarErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/calendar/exception/CalendarErrorCode.java index 233528e1..504a00cd 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/calendar/exception/CalendarErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/calendar/exception/CalendarErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.calendar.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/calendar/exception/CalendarException.java b/codin-core/src/main/java/inu/codin/codin/domain/calendar/exception/CalendarException.java index 2613c31a..677612f9 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/calendar/exception/CalendarException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/calendar/exception/CalendarException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.calendar.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/calendar/service/CalendarService.java b/codin-core/src/main/java/inu/codin/codin/domain/calendar/service/CalendarService.java index 73389f3d..0d009e51 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/calendar/service/CalendarService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/calendar/service/CalendarService.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.calendar.service; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.common.util.ObjectIdUtil; import inu.codin.codin.domain.calendar.dto.*; import inu.codin.codin.domain.calendar.entity.CalendarEntity; import inu.codin.codin.domain.calendar.exception.CalendarErrorCode; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/controller/ChatRoomController.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/controller/ChatRoomController.java index 3b94ba40..5a575fb3 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/controller/ChatRoomController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/controller/ChatRoomController.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.chat.chatroom.controller; -import inu.codin.codin.common.response.ListResponse; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.ListResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.chat.chatroom.dto.request.ChatRoomCreateRequestDto; import inu.codin.codin.domain.chat.chatroom.dto.response.ChatRoomListResponseDto; import inu.codin.codin.domain.chat.chatroom.service.ChatRoomService; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ChatRoom.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ChatRoom.java index 21cfbb14..51075b9d 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ChatRoom.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ChatRoom.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.chat.chatroom.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import inu.codin.codin.domain.chat.chatroom.dto.request.ChatRoomCreateRequestDto; import jakarta.validation.constraints.NotBlank; import lombok.AccessLevel; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ParticipantInfo.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ParticipantInfo.java index 6e4cc10a..5cadbe7f 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ParticipantInfo.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/entity/ParticipantInfo.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.chat.chatroom.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/service/ChatRoomService.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/service/ChatRoomService.java index 793bcc07..f5258627 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/service/ChatRoomService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatroom/service/ChatRoomService.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.chat.chatroom.service; -import inu.codin.codin.common.exception.NotFoundException; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.common.exception.NotFoundException; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.block.service.BlockService; import inu.codin.codin.domain.chat.chatroom.dto.request.ChatRoomCreateRequestDto; import inu.codin.codin.domain.chat.chatroom.dto.response.ChatRoomListResponseDto; @@ -21,6 +21,8 @@ import java.util.*; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -34,7 +36,7 @@ public class ChatRoomService { public Map createChatRoom(ChatRoomCreateRequestDto chatRoomCreateRequestDto) { - ObjectId senderId = SecurityUtils.getCurrentUserId(); + ObjectId senderId = toObjectId(SecurityUtil.getCurrentUserId()); isValidated(chatRoomCreateRequestDto, senderId); //유효성 검사 log.info("[채팅방 생성 요청] 송신자 ID: {}, 수신자 ID: {}", senderId, chatRoomCreateRequestDto.getReceiverId()); @@ -76,7 +78,7 @@ private void isValidated(ChatRoomCreateRequestDto chatRoomCreateRequestDto, Obje } public List getAllChatRoomByUser() { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); log.info("[유저의 채팅방 조회] 유저 ID: {}", userId); // 차단 목록 조회 List blockedUsersId = blockService.getBlockedUsers(); @@ -92,7 +94,7 @@ public List getAllChatRoomByUser() { } public void leaveChatRoom(String chatRoomId) { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); log.info("[채팅방 나가기 요청] 유저 ID: {}, 채팅방 ID: {}", userId, chatRoomId); ChatRoom chatRoom = chatRoomRepository.findById(chatRoomId) @@ -122,7 +124,7 @@ public void leaveChatRoom(String chatRoomId) { } public void setNotificationChatRoom(String chatRoomId) { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); log.info("[알림 설정 요청] 유저 ID: {}, 채팅방 ID: {}", userId, chatRoomId); ChatRoom chatRoom = chatRoomRepository.findById(chatRoomId) diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/controller/ChattingController.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/controller/ChattingController.java index 53f45c62..3deb37f6 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/controller/ChattingController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/controller/ChattingController.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.chat.chatting.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.chat.chatting.dto.request.ChattingRequestDto; import inu.codin.codin.domain.chat.chatting.service.ChattingService; import io.swagger.v3.oas.annotations.Operation; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/entity/Chatting.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/entity/Chatting.java index 03bc7323..04e3642e 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/entity/Chatting.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/entity/Chatting.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.chat.chatting.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import inu.codin.codin.domain.chat.chatting.dto.ContentType; import inu.codin.codin.domain.chat.chatting.dto.request.ChattingRequestDto; import jakarta.validation.constraints.NotBlank; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingEventListener.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingEventListener.java index 4a353a8f..630bb24c 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingEventListener.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingEventListener.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.chat.chatting.service; -import inu.codin.codin.common.exception.NotFoundException; +import inu.codin.common.exception.NotFoundException; import inu.codin.codin.domain.chat.chatroom.entity.ChatRoom; import inu.codin.codin.domain.chat.chatroom.entity.ParticipantInfo; import inu.codin.codin.domain.chat.chatroom.repository.ChatRoomRepository; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingService.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingService.java index 5c3af31a..5e2f0c43 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/chatting/service/ChattingService.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.chat.chatting.service; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.chat.chatroom.entity.ChatRoom; import inu.codin.codin.domain.chat.chatroom.entity.ParticipantInfo; import inu.codin.codin.domain.chat.chatroom.exception.ChatRoomNotFoundException; @@ -30,6 +30,8 @@ import java.time.LocalDateTime; import java.util.List; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -72,7 +74,7 @@ public ChattingResponseDto sendMessage(String id, ChattingRequestDto chattingReq } public ChattingAndUserIdResponseDto getAllMessage(String id, int page) { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); ChatRoom chatRoom = chatRoomRepository.findById(new ObjectId(id)) .orElseThrow(() -> { log.warn("[채팅방 조회 실패] 채팅방 ID: {}를 찾을 수 없습니다.", id); @@ -97,7 +99,7 @@ public ChattingAndUserIdResponseDto getAllMessage(String id, int page) { log.info("[메시지 조회 성공] 채팅방 ID: {}, 메시지 개수: {}", id, chattingResponseDto.size()); - return new ChattingAndUserIdResponseDto(chattingResponseDto, SecurityUtils.getCurrentUserId().toString()); + return new ChattingAndUserIdResponseDto(chattingResponseDto, SecurityUtil.getCurrentUserId().toString()); } public List sendImageMessage(List chatImages) { diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChatRoomErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChatRoomErrorCode.java index 718f18fa..0c007f70 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChatRoomErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChatRoomErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.chat.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChatRoomException.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChatRoomException.java index 325c0cbe..c45cd393 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChatRoomException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChatRoomException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.chat.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChattingErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChattingErrorCode.java index 3bf11950..500dd90f 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChattingErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChattingErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.chat.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChattingException.java b/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChattingException.java index e78cc1d2..ab9c0455 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChattingException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/chat/exception/ChattingException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.chat.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/email/controller/EmailController.java b/codin-core/src/main/java/inu/codin/codin/domain/email/controller/EmailController.java index 7a55b765..94737f81 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/email/controller/EmailController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/email/controller/EmailController.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.email.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.email.dto.request.JoinEmailCheckRequestDto; import inu.codin.codin.domain.email.dto.request.JoinEmailSendRequestDto; import inu.codin.codin.domain.email.service.JoinEmailAuthService; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/email/entity/EmailAuthEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/email/entity/EmailAuthEntity.java index 8f24b736..f088edc4 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/email/entity/EmailAuthEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/email/entity/EmailAuthEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.email.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/email/service/PasswordResetEmailService.java b/codin-core/src/main/java/inu/codin/codin/domain/email/service/PasswordResetEmailService.java index 1c5761b7..f3632daa 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/email/service/PasswordResetEmailService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/email/service/PasswordResetEmailService.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.email.service; -import inu.codin.codin.common.exception.NotFoundException; +import inu.codin.common.exception.NotFoundException; import inu.codin.codin.domain.email.dto.request.JoinEmailSendRequestDto; import inu.codin.codin.domain.email.entity.EmailAuthEntity; import inu.codin.codin.domain.email.exception.EmailPasswordResetFailException; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/controller/LabController.java b/codin-core/src/main/java/inu/codin/codin/domain/info/controller/LabController.java index f07fdcc0..4521862c 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/controller/LabController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/controller/LabController.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.info.controller; -import inu.codin.codin.common.response.ListResponse; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.ListResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.info.dto.request.LabCreateUpdateRequestDto; import inu.codin.codin.domain.info.dto.response.LabListResponseDto; import inu.codin.codin.domain.info.dto.response.LabThumbnailResponseDto; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/controller/OfficeController.java b/codin-core/src/main/java/inu/codin/codin/domain/info/controller/OfficeController.java index 6dad90d1..e30242e5 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/controller/OfficeController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/controller/OfficeController.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.info.controller; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.dto.Department; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.info.dto.request.OfficeMemberCreateUpdateRequestDto; import inu.codin.codin.domain.info.dto.request.OfficeUpdateRequestDto; import inu.codin.codin.domain.info.service.OfficeService; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/controller/PartnerController.java b/codin-core/src/main/java/inu/codin/codin/domain/info/controller/PartnerController.java index b35d48e5..a26b746e 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/controller/PartnerController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/controller/PartnerController.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.info.controller; -import inu.codin.codin.common.response.ListResponse; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.ListResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.info.dto.request.PartnerCreateRequestDto; import inu.codin.codin.domain.info.dto.response.PartnerDetailsResponseDto; import inu.codin.codin.domain.info.dto.response.PartnerListResponseDto; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/controller/ProfessorController.java b/codin-core/src/main/java/inu/codin/codin/domain/info/controller/ProfessorController.java index 4e7a92e4..1400be80 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/controller/ProfessorController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/controller/ProfessorController.java @@ -1,8 +1,8 @@ package inu.codin.codin.domain.info.controller; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.response.ListResponse; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.dto.Department; +import inu.codin.common.response.ListResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.info.dto.request.ProfessorCreateUpdateRequestDto; import inu.codin.codin.domain.info.dto.response.ProfessorListResponseDto; import inu.codin.codin.domain.info.dto.response.ProfessorThumbnailResponseDto; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/LabCreateUpdateRequestDto.java b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/LabCreateUpdateRequestDto.java index 8ca41081..33fa7344 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/LabCreateUpdateRequestDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/LabCreateUpdateRequestDto.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.dto.request; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/PartnerCreateRequestDto.java b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/PartnerCreateRequestDto.java index b51ed77b..e4569bab 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/PartnerCreateRequestDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/PartnerCreateRequestDto.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.dto.request; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/ProfessorCreateUpdateRequestDto.java b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/ProfessorCreateUpdateRequestDto.java index 3cfd3de4..4979c7c4 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/ProfessorCreateUpdateRequestDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/request/ProfessorCreateUpdateRequestDto.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.dto.request; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/LabThumbnailResponseDto.java b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/LabThumbnailResponseDto.java index fcd0e387..00078443 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/LabThumbnailResponseDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/LabThumbnailResponseDto.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.dto.response; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.entity.Lab; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/OfficeDetailsResponseDto.java b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/OfficeDetailsResponseDto.java index 88516715..b24260e0 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/OfficeDetailsResponseDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/OfficeDetailsResponseDto.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.dto.response; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.entity.Office; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/PartnerDetailsResponseDto.java b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/PartnerDetailsResponseDto.java index f9b901b1..1c79f922 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/PartnerDetailsResponseDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/PartnerDetailsResponseDto.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.info.dto.response; import com.fasterxml.jackson.annotation.JsonFormat; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.entity.Partner; import inu.codin.codin.domain.info.entity.PartnerImg; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/PartnerListResponseDto.java b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/PartnerListResponseDto.java index 4596cff6..b44d3b70 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/PartnerListResponseDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/PartnerListResponseDto.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.dto.response; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.entity.Partner; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/ProfessorListResponseDto.java b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/ProfessorListResponseDto.java index 62bff212..077a5c72 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/ProfessorListResponseDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/ProfessorListResponseDto.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.dto.response; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.entity.Professor; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/ProfessorThumbnailResponseDto.java b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/ProfessorThumbnailResponseDto.java index cf2e5eb0..0d61323e 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/ProfessorThumbnailResponseDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/dto/response/ProfessorThumbnailResponseDto.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.dto.response; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.entity.Professor; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Info.java b/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Info.java index 462c0276..fcef98c9 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Info.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Info.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.info.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.Department; import jakarta.validation.constraints.NotBlank; import lombok.AccessLevel; import lombok.Getter; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Lab.java b/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Lab.java index 36224b63..0703d6a5 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Lab.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Lab.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.entity; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.dto.request.LabCreateUpdateRequestDto; import jakarta.validation.constraints.NotBlank; import lombok.AccessLevel; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/entity/OfficeMember.java b/codin-core/src/main/java/inu/codin/codin/domain/info/entity/OfficeMember.java index 19a7120c..70132de5 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/entity/OfficeMember.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/entity/OfficeMember.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import inu.codin.codin.domain.info.dto.request.OfficeMemberCreateUpdateRequestDto; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Partner.java b/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Partner.java index e55fbbed..ee399434 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Partner.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Partner.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.entity; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.dto.request.PartnerCreateRequestDto; import jakarta.validation.constraints.NotBlank; import lombok.AccessLevel; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Professor.java b/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Professor.java index da280ae3..ff892b67 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Professor.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/entity/Professor.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.entity; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.dto.request.ProfessorCreateUpdateRequestDto; import jakarta.validation.constraints.NotBlank; import lombok.AccessLevel; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/exception/InfoErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/info/exception/InfoErrorCode.java index 743d7b77..f17320a8 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/exception/InfoErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/exception/InfoErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/exception/InfoException.java b/codin-core/src/main/java/inu/codin/codin/domain/info/exception/InfoException.java index 85a383ad..2df18aae 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/exception/InfoException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/exception/InfoException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/repository/InfoRepository.java b/codin-core/src/main/java/inu/codin/codin/domain/info/repository/InfoRepository.java index 23d13c05..f0fbc253 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/repository/InfoRepository.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/repository/InfoRepository.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.repository; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.entity.Info; import inu.codin.codin.domain.info.entity.Lab; import inu.codin.codin.domain.info.entity.Office; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/service/OfficeService.java b/codin-core/src/main/java/inu/codin/codin/domain/info/service/OfficeService.java index 7a2639e8..79c89037 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/service/OfficeService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/service/OfficeService.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.service; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.dto.request.OfficeMemberCreateUpdateRequestDto; import inu.codin.codin.domain.info.dto.request.OfficeUpdateRequestDto; import inu.codin.codin.domain.info.dto.response.OfficeDetailsResponseDto; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/info/service/ProfessorService.java b/codin-core/src/main/java/inu/codin/codin/domain/info/service/ProfessorService.java index 031468f0..8aedd976 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/info/service/ProfessorService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/info/service/ProfessorService.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.info.service; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.info.dto.request.ProfessorCreateUpdateRequestDto; import inu.codin.codin.domain.info.dto.response.ProfessorListResponseDto; import inu.codin.codin.domain.info.dto.response.ProfessorThumbnailResponseDto; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/lecture/controller/LectureController.java b/codin-core/src/main/java/inu/codin/codin/domain/lecture/controller/LectureController.java index efa4ce9e..efe91cc7 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/lecture/controller/LectureController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/lecture/controller/LectureController.java @@ -1,8 +1,8 @@ package inu.codin.codin.domain.lecture.controller; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.response.ListResponse; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.dto.Department; +import inu.codin.common.response.ListResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.lecture.dto.Option; import inu.codin.codin.domain.lecture.service.LectureService; import io.swagger.v3.oas.annotations.Operation; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/lecture/controller/LectureUploadController.java b/codin-core/src/main/java/inu/codin/codin/domain/lecture/controller/LectureUploadController.java index 3933edca..13d7a7cf 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/lecture/controller/LectureUploadController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/lecture/controller/LectureUploadController.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.lecture.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.lecture.service.LectureUploadService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/controller/ReviewController.java b/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/controller/ReviewController.java index 7974d6ad..62346544 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/controller/ReviewController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/controller/ReviewController.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.lecture.domain.review.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.lecture.domain.review.dto.request.CreateReviewRequestDto; import inu.codin.codin.domain.lecture.domain.review.service.ReviewService; import io.swagger.v3.oas.annotations.Operation; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/entity/ReviewEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/entity/ReviewEntity.java index 1459b2bf..e965424e 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/entity/ReviewEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/entity/ReviewEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.lecture.domain.review.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import inu.codin.codin.domain.lecture.domain.review.dto.request.CreateReviewRequestDto; import lombok.AccessLevel; import lombok.Builder; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/service/ReviewService.java b/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/service/ReviewService.java index f86c28cf..3c98b8d4 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/service/ReviewService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/review/service/ReviewService.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.lecture.domain.review.service; -import inu.codin.codin.common.exception.NotFoundException; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.common.exception.NotFoundException; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.lecture.domain.review.dto.request.CreateReviewRequestDto; import inu.codin.codin.domain.lecture.domain.review.dto.response.ReviewListResposneDto; import inu.codin.codin.domain.lecture.domain.review.dto.response.ReviewPageResponse; @@ -24,6 +24,8 @@ import java.util.Optional; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -44,7 +46,7 @@ public void createReview(ObjectId lectureId, CreateReviewRequestDto createReview log.warn("잘못된 평점입니다. 0.25 ~ 5.0 사이의 점수를 입력해주세요"); throw new WrongRatingException("잘못된 평점입니다. 0.25 ~ 5.0 사이의 점수를 입력해주세요."); } - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); Optional review = reviewRepository.findByLectureIdAndUserIdAndDeletedAtIsNull(lectureId, userId); if (review.isPresent()) { log.error("이미 유저가 작성한 후기가 존재합니다. userId: {}, lectureId: {}", userId, lectureId); @@ -82,7 +84,7 @@ public ReviewPageResponse getListOfReviews(String lectureId, int page) { PageRequest pageRequest = PageRequest.of(page, 10, Sort.by("created_at").descending()); Page reviewPage = reviewRepository.getAvgRatingByLectureId(new ObjectId(lectureId), pageRequest); - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); return ReviewPageResponse.of(reviewPage.stream() .map(review -> ReviewListResposneDto.of(review, likeService.isLiked(LikeType.REVIEW, review.get_id().toString(), userId), diff --git a/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/room/controller/LectureRoomController.java b/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/room/controller/LectureRoomController.java index 6ad57578..80651587 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/room/controller/LectureRoomController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/lecture/domain/room/controller/LectureRoomController.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.lecture.domain.room.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.lecture.domain.room.service.LectureRoomService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/lecture/entity/LectureEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/lecture/entity/LectureEntity.java index 7d88c255..25ea2437 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/lecture/entity/LectureEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/lecture/entity/LectureEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.lecture.entity; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.lecture.dto.Emotion; import lombok.AccessLevel; import lombok.Getter; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/lecture/repository/LectureRepository.java b/codin-core/src/main/java/inu/codin/codin/domain/lecture/repository/LectureRepository.java index cfec9fa6..96c39d61 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/lecture/repository/LectureRepository.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/lecture/repository/LectureRepository.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.lecture.repository; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.lecture.entity.LectureEntity; import org.bson.types.ObjectId; import org.springframework.data.domain.Page; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/lecture/service/LectureService.java b/codin-core/src/main/java/inu/codin/codin/domain/lecture/service/LectureService.java index e6d2e410..c1653e24 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/lecture/service/LectureService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/lecture/service/LectureService.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.lecture.service; -import inu.codin.codin.common.dto.Department; -import inu.codin.codin.common.exception.NotFoundException; +import inu.codin.common.dto.Department; +import inu.codin.common.exception.NotFoundException; import inu.codin.codin.domain.lecture.dto.*; import inu.codin.codin.domain.lecture.dto.response.LectureDetailResponseDto; import inu.codin.codin.domain.lecture.dto.response.LectureListResponseDto; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/like/controller/LikeController.java b/codin-core/src/main/java/inu/codin/codin/domain/like/controller/LikeController.java index 14ea7c6f..d5237e3e 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/like/controller/LikeController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/like/controller/LikeController.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.like.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.like.dto.LikeResponseType; import inu.codin.codin.domain.like.dto.LikedResponseDto; import inu.codin.codin.domain.like.dto.request.LikeRequestDto; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/like/entity/LikeEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/like/entity/LikeEntity.java index 6baf7772..11576c6c 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/like/entity/LikeEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/like/entity/LikeEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.like.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/like/service/LikeService.java b/codin-core/src/main/java/inu/codin/codin/domain/like/service/LikeService.java index 80257da9..ec4270f0 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/like/service/LikeService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/like/service/LikeService.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.like.service; -import inu.codin.codin.common.exception.NotFoundException; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.common.exception.NotFoundException; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.like.dto.LikeResponseType; import inu.codin.codin.domain.like.dto.LikedResponseDto; import inu.codin.codin.domain.like.dto.request.LikeRequestDto; @@ -17,13 +17,14 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.bson.types.ObjectId; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -40,7 +41,7 @@ public class LikeService { public LikeResponseType toggleLike(LikeRequestDto likeRequestDto) { String likeId = likeRequestDto.getLikeTypeId(); - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); isEntityNotDeleted(likeRequestDto); // 해당 entity가 삭제되었는지 확인 // 이미 좋아요를 눌렀으면 취소, 그렇지 않으면 추가 diff --git a/codin-core/src/main/java/inu/codin/codin/domain/notification/controller/NotificationController.java b/codin-core/src/main/java/inu/codin/codin/domain/notification/controller/NotificationController.java index ea0dff79..917aa3d4 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/notification/controller/NotificationController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/notification/controller/NotificationController.java @@ -1,16 +1,18 @@ package inu.codin.codin.domain.notification.controller; -import inu.codin.codin.common.response.ListResponse; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.ListResponse; +import inu.codin.common.response.SingleResponse; +import inu.codin.codin.domain.notification.dto.request.OneCharNameRequestDto; import inu.codin.codin.domain.notification.service.NotificationService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; 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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; @RestController @RequestMapping("/notification") @@ -38,4 +40,26 @@ public ResponseEntity> readNotification(@PathVariable String n .body(new SingleResponse<>(200, "알림 읽기 완료", null)); } + @Operation(summary = "이름 1글자 대상 1회성 알림 발송 (관리자)") + @PreAuthorize("hasRole('ADMIN')") + @PostMapping("/onceCharName") + public ResponseEntity> sendNameFix( + @RequestBody @Valid OneCharNameRequestDto request) { + + int sent = notificationService.sendOneCharNameFix(request); + return ResponseEntity.ok() + .body(new SingleResponse<>(200, "1회성 알림 발송 완료", Map.of("sent", sent))); + } + + @Operation(summary = "이름 1글자 대상 1회성 알림 발송 테스트 (관리자)") + @PreAuthorize("hasRole('ADMIN')") + @PostMapping("/test") + public ResponseEntity> sendNameFixTest( + @RequestBody @Valid OneCharNameRequestDto request) { + + int sent = notificationService.sendOneCharNameFixTest(request); + return ResponseEntity.ok() + .body(new SingleResponse<>(200, "1회성 알림 발송 완료", Map.of("sent", sent))); + } + } diff --git a/codin-core/src/main/java/inu/codin/codin/domain/notification/dto/request/OneCharNameRequestDto.java b/codin-core/src/main/java/inu/codin/codin/domain/notification/dto/request/OneCharNameRequestDto.java new file mode 100644 index 00000000..a525d320 --- /dev/null +++ b/codin-core/src/main/java/inu/codin/codin/domain/notification/dto/request/OneCharNameRequestDto.java @@ -0,0 +1,13 @@ +package inu.codin.codin.domain.notification.dto.request; + +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; + +@Getter +public class OneCharNameRequestDto { + @NotBlank(message = "알림 제목은 필수입니다.") + private String title; + + @NotBlank(message = "알림 내용은 필수입니다.") + private String body; +} \ No newline at end of file diff --git a/codin-core/src/main/java/inu/codin/codin/domain/notification/entity/NotificationEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/notification/entity/NotificationEntity.java index c8494559..51ab0835 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/notification/entity/NotificationEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/notification/entity/NotificationEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.notification.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import jakarta.validation.constraints.NotBlank; import lombok.AccessLevel; import lombok.Builder; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/notification/service/NotificationService.java b/codin-core/src/main/java/inu/codin/codin/domain/notification/service/NotificationService.java index ea21c58e..a15390e8 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/notification/service/NotificationService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/notification/service/NotificationService.java @@ -1,8 +1,9 @@ package inu.codin.codin.domain.notification.service; -import inu.codin.codin.common.exception.NotFoundException; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.common.exception.NotFoundException; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.like.entity.LikeType; +import inu.codin.codin.domain.notification.dto.request.OneCharNameRequestDto; import inu.codin.codin.domain.notification.dto.response.NotificationListResponseDto; import inu.codin.codin.domain.notification.entity.NotificationEntity; import inu.codin.codin.domain.notification.repository.NotificationRepository; @@ -13,6 +14,7 @@ import inu.codin.codin.domain.post.entity.PostCategory; import inu.codin.codin.domain.post.entity.PostEntity; import inu.codin.codin.domain.post.repository.PostRepository; +import inu.codin.codin.domain.user.entity.UserEntity; import inu.codin.codin.domain.user.repository.UserRepository; import inu.codin.codin.infra.fcm.dto.FcmMessageTopicDto; import inu.codin.codin.infra.fcm.dto.FcmMessageUserDto; @@ -27,6 +29,8 @@ import java.util.List; import java.util.Map; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -111,7 +115,12 @@ private void saveNotificationLog(FcmMessageUserDto msgDto, Map d .userId(msgDto.getUserId()) .title(msgDto.getTitle()) .message(msgDto.getBody()) - .targetId(new ObjectId(data.get("id"))) + // id가 존재하고 비어있지 않으면 ObjectId로 변환, 아니면 null + .targetId( + (data != null && data.get("id") != null && !data.get("id").isBlank()) + ? new ObjectId(data.get("id")) + : null + ) .type("push") .priority("high") .build(); @@ -202,7 +211,7 @@ public void readNotification(String notificationId){ public List getNotification() { //todo 유저에게 맞는 토픽 알림들도 반환 - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); userRepository.findById(userId) .orElseThrow(() -> new NotFoundException("유저를 찾을 수 없습니다")); @@ -212,4 +221,64 @@ public List getNotification() { .map(NotificationListResponseDto::of) .toList(); } + + public int sendOneCharNameFix(OneCharNameRequestDto oneCharNameRequestDto) { + final String title = oneCharNameRequestDto.getTitle().trim(); + final String body = oneCharNameRequestDto.getBody().trim(); + + List targets = userRepository.findActiveUsersWithOneCharName(); + if (targets.isEmpty()) { + log.info("[이름 1글자 알림] 대상 없음"); + return 0; + } + + Map data = new HashMap<>(); + data.put("route", "/mypage/edit"); // 클릭 시 이동 경로(프론트에서 처리) + + int success = 0; + for (UserEntity u : targets) { + try { + sendFcmMessageToUser(title, body, data, u.get_id()); + success++; + } catch (Exception e) { + log.info("[이름 1글자 알림] 전송 실패 userId={}", u.get_id(), e); + } + } + log.info("[이름 1글자 알림] 전송 완료: total={}, success={}", targets.size(), success); + return success; + } + + public int sendOneCharNameFixTest(OneCharNameRequestDto oneCharNameRequestDto) { + final String title = oneCharNameRequestDto.getTitle().trim(); + final String body = oneCharNameRequestDto.getBody().trim(); + + //이름 1글자 유저 (로그용) + List oneCharTargets = userRepository.findActiveUsersWithOneCharName(); + log.info("[이름 1글자 알림 테스트] 1글자 이름 유저 수: {}", oneCharTargets.size()); + for (UserEntity u : oneCharTargets) { + log.info(" - 대상 후보 (한글자): name={}, userId={}", u.getName(), u.get_id()); + } + + // 실제 발송 대상: ADMIN + List adminTargets = userRepository.findActiveAdmins(); + if (adminTargets.isEmpty()) { + log.info("[이름 1글자 알림 테스트] ADMIN 대상 없음"); + return 0; + } + + Map data = new HashMap<>(); + data.put("route", "/mypage/edit"); // 클릭 시 이동 경로(프론트에서 처리) + + int success = 0; + for (UserEntity u : adminTargets) { + try { + sendFcmMessageToUser(title, body, data, u.get_id()); + success++; + } catch (Exception e) { + log.info("[이름 1글자 알림] 전송 실패 userId={}", u.get_id(), e); + } + } + log.info("[이름 1글자 알림] 전송 완료: total={}, success={}", adminTargets.size(), success); + return success; + } } diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/controller/PostController.java b/codin-core/src/main/java/inu/codin/codin/domain/post/controller/PostController.java index 60bb3976..7719feb9 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/controller/PostController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/controller/PostController.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.post.controller; -import inu.codin.codin.common.response.ListResponse; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.ListResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.post.dto.request.PostAnonymousUpdateRequestDTO; import inu.codin.codin.domain.post.dto.request.PostContentUpdateRequestDTO; import inu.codin.codin.domain.post.dto.request.PostCreateRequestDTO; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/controller/CommentController.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/controller/CommentController.java index c46f08e7..600a572e 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/controller/CommentController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/controller/CommentController.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.post.domain.comment.controller; -import inu.codin.codin.common.response.ListResponse; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.ListResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.post.domain.comment.dto.request.CommentCreateRequestDTO; import inu.codin.codin.domain.post.domain.comment.dto.request.CommentUpdateRequestDTO; import inu.codin.codin.domain.post.domain.comment.dto.response.CommentResponseDTO; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/entity/CommentEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/entity/CommentEntity.java index af42e2d8..21843951 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/entity/CommentEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/entity/CommentEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.comment.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import inu.codin.codin.domain.post.domain.comment.dto.request.CommentCreateRequestDTO; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/exception/CommentErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/exception/CommentErrorCode.java index 6da27025..a0ede16b 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/exception/CommentErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/exception/CommentErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.comment.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/exception/CommentException.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/exception/CommentException.java index 9004c1fd..bcbc42b1 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/exception/CommentException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/exception/CommentException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.comment.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/controller/ReplyCommentController.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/controller/ReplyCommentController.java index f6f68205..ef69f0cc 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/controller/ReplyCommentController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/controller/ReplyCommentController.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.post.domain.comment.reply.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.post.domain.comment.reply.dto.request.ReplyCreateRequestDTO; import inu.codin.codin.domain.post.domain.comment.reply.dto.request.ReplyUpdateRequestDTO; import inu.codin.codin.domain.post.domain.comment.reply.service.ReplyCommandService; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/entity/ReplyCommentEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/entity/ReplyCommentEntity.java index c80a7c00..48a17d47 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/entity/ReplyCommentEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/entity/ReplyCommentEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.comment.reply.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import inu.codin.codin.domain.post.domain.comment.reply.dto.request.ReplyCreateRequestDTO; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/exception/ReplyErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/exception/ReplyErrorCode.java index 3e2f9216..059a09d1 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/exception/ReplyErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/exception/ReplyErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.comment.reply.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/exception/ReplyException.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/exception/ReplyException.java index 15c31f6f..7c25e37f 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/exception/ReplyException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/exception/ReplyException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.comment.reply.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/service/ReplyCommandService.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/service/ReplyCommandService.java index e735b8b3..3f2f9c11 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/service/ReplyCommandService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/service/ReplyCommandService.java @@ -1,7 +1,6 @@ package inu.codin.codin.domain.post.domain.comment.reply.service; -import inu.codin.codin.common.security.util.SecurityUtils; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.notification.service.NotificationService; import inu.codin.codin.domain.post.domain.best.BestService; import inu.codin.codin.domain.post.domain.comment.entity.CommentEntity; @@ -20,6 +19,8 @@ import org.bson.types.ObjectId; import org.springframework.stereotype.Service; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -41,9 +42,9 @@ public class ReplyCommandService { // 대댓글 추가 public void addReply(String id, ReplyCreateRequestDTO requestDTO) { - CommentEntity comment = commentQueryService.findCommentById(ObjectIdUtil.toObjectId(id)); + CommentEntity comment = commentQueryService.findCommentById(toObjectId(id)); PostEntity post = postQueryService.findPostById(comment.getPostId()); - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); ReplyCommentEntity reply = ReplyCommentEntity.create(comment.get_id(), userId, requestDTO); @@ -58,7 +59,7 @@ public void addReply(String id, ReplyCreateRequestDTO requestDTO) { } public void updateReply(String replyId, @Valid ReplyUpdateRequestDTO requestDTO) { - ReplyCommentEntity reply = ownershipPolicy.assertReplyOwner(ObjectIdUtil.toObjectId(replyId)); + ReplyCommentEntity reply = ownershipPolicy.assertReplyOwner(toObjectId(replyId)); reply.updateReply(requestDTO.getContent()); replyCommentRepository.save(reply); @@ -69,7 +70,7 @@ public void updateReply(String replyId, @Valid ReplyUpdateRequestDTO requestDTO) // 대댓글 삭제 (Soft Delete) public void softDeleteReply(String replyId) { - ReplyCommentEntity reply = ownershipPolicy.assertReplyOwner(ObjectIdUtil.toObjectId(replyId)); + ReplyCommentEntity reply = ownershipPolicy.assertReplyOwner(toObjectId(replyId)); CommentEntity comment = commentQueryService.findCommentById(reply.getCommentId()); diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/service/ReplyQueryService.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/service/ReplyQueryService.java index 5de87298..b4b4a22d 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/service/ReplyQueryService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/reply/service/ReplyQueryService.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.comment.reply.service; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.like.entity.LikeType; import inu.codin.codin.domain.like.service.LikeService; import inu.codin.codin.domain.post.domain.comment.dto.response.CommentResponseDTO; @@ -25,6 +25,8 @@ import java.util.Map; import java.util.stream.Collectors; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -105,7 +107,7 @@ private CommentResponseDTO buildReplyResponseDTO( } public UserInfo getUserInfoAboutReply(ObjectId replyId) { - ObjectId userId = SecurityUtils.getCurrentUserIdOrNull(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserIdOrNull()); boolean isLiked = false; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/service/CommentCommandService.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/service/CommentCommandService.java index 267ff399..7251428e 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/service/CommentCommandService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/service/CommentCommandService.java @@ -1,7 +1,6 @@ package inu.codin.codin.domain.post.domain.comment.service; -import inu.codin.codin.common.security.util.SecurityUtils; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.notification.service.NotificationService; import inu.codin.codin.domain.post.domain.comment.dto.request.CommentCreateRequestDTO; import inu.codin.codin.domain.post.domain.comment.dto.request.CommentUpdateRequestDTO; @@ -17,6 +16,8 @@ import org.bson.types.ObjectId; import org.springframework.stereotype.Service; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -31,10 +32,10 @@ public class CommentCommandService { // 댓글 추가 public void addComment(String id, CommentCreateRequestDTO requestDTO) { - ObjectId postId = ObjectIdUtil.toObjectId(id); + ObjectId postId = toObjectId(id); PostEntity post = postQueryService.findPostById(postId); - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); CommentEntity comment = CommentEntity.create(postId, userId, requestDTO); commentRepository.save(comment); @@ -50,7 +51,7 @@ public void addComment(String id, CommentCreateRequestDTO requestDTO) { public void updateComment(String commentId, CommentUpdateRequestDTO requestDTO) { log.info("댓글 업데이트 요청. commentId: {}, 새로운 내용: {}", commentId, requestDTO.getContent()); - CommentEntity comment = ownershipPolicy.assertCommentOwner(ObjectIdUtil.toObjectId(commentId)); + CommentEntity comment = ownershipPolicy.assertCommentOwner(toObjectId(commentId)); comment.updateComment(requestDTO.getContent()); commentRepository.save(comment); @@ -61,7 +62,7 @@ public void updateComment(String commentId, CommentUpdateRequestDTO requestDTO) // 댓글 삭제 (Soft Delete) public void softDeleteComment(String commentId) { - CommentEntity comment = ownershipPolicy.assertCommentOwner(ObjectIdUtil.toObjectId(commentId)); + CommentEntity comment = ownershipPolicy.assertCommentOwner(toObjectId(commentId)); ObjectId postId = comment.getPostId(); PostEntity post = postQueryService.findPostById(postId); diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/service/CommentQueryService.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/service/CommentQueryService.java index fbbc94db..edd128bc 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/service/CommentQueryService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/comment/service/CommentQueryService.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.post.domain.comment.service; -import inu.codin.codin.common.security.util.SecurityUtils; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.security.util.SecurityUtil; +import inu.codin.common.util.ObjectIdUtil; import inu.codin.codin.domain.like.entity.LikeType; import inu.codin.codin.domain.like.service.LikeService; import inu.codin.codin.domain.post.domain.comment.dto.response.CommentResponseDTO; @@ -28,6 +28,8 @@ import java.util.Map; import java.util.stream.Collectors; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -113,7 +115,7 @@ private CommentResponseDTO buildCommentResponseDTO( public UserInfo getUserInfoAboutComment(ObjectId commentId) { - ObjectId userId = SecurityUtils.getCurrentUserIdOrNull(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserIdOrNull()); boolean isLiked = false; if (userId != null) { diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/hits/exception/HitsErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/hits/exception/HitsErrorCode.java index 8f297788..86027f8f 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/hits/exception/HitsErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/hits/exception/HitsErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.hits.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/hits/exception/HitsException.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/hits/exception/HitsException.java index 26c44b89..61aaa9a4 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/hits/exception/HitsException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/hits/exception/HitsException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.hits.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/controller/PollController.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/controller/PollController.java index 5e4cb0a4..9887ebe4 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/controller/PollController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/controller/PollController.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.poll.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.post.domain.poll.dto.request.PollCreateRequestDTO; import inu.codin.codin.domain.post.domain.poll.dto.request.PollVotingRequestDTO; import inu.codin.codin.domain.post.domain.poll.service.PollCommandService; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/entity/PollEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/entity/PollEntity.java index 9510bb81..69b961aa 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/entity/PollEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/entity/PollEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.poll.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import inu.codin.codin.domain.post.domain.poll.dto.request.PollCreateRequestDTO; import lombok.AccessLevel; import lombok.Getter; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/exception/PollErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/exception/PollErrorCode.java index 1f3b037a..722055a8 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/exception/PollErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/exception/PollErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.poll.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/exception/PollException.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/exception/PollException.java index a0cd3bed..6dcff96b 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/exception/PollException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/exception/PollException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.poll.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/service/PollCommandService.java b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/service/PollCommandService.java index c6f57e02..5193bc13 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/service/PollCommandService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/domain/poll/service/PollCommandService.java @@ -1,8 +1,7 @@ package inu.codin.codin.domain.post.domain.poll.service; -import com.mongodb.client.result.UpdateResult; -import inu.codin.codin.common.security.util.SecurityUtils; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.security.util.SecurityUtil; +import inu.codin.common.util.ObjectIdUtil; import inu.codin.codin.domain.post.domain.poll.dto.request.PollCreateRequestDTO; import inu.codin.codin.domain.post.domain.poll.dto.request.PollVotingRequestDTO; import inu.codin.codin.domain.post.domain.poll.entity.PollEntity; @@ -26,6 +25,8 @@ import java.util.List; import java.util.Objects; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -50,10 +51,10 @@ public void createPoll(PollCreateRequestDTO pollRequestDTO) { } public void votingPoll(String postId, PollVotingRequestDTO pollRequestDTO) { - log.info("투표 요청 - postId: {}, userId: {}", postId, SecurityUtils.getCurrentUserId()); + log.info("투표 요청 - postId: {}, userId: {}", postId, SecurityUtil.getCurrentUserId()); PollEntity poll = getActivePollByPostId(postId); - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); ensureNotDuplicatedVote(poll.get_id(), userId); @@ -71,10 +72,10 @@ public void votingPoll(String postId, PollVotingRequestDTO pollRequestDTO) { } public void deleteVoting(String postId) { - log.info("투표 취소 요청 - postId: {}, userId: {}", postId, SecurityUtils.getCurrentUserId()); + log.info("투표 취소 요청 - postId: {}, userId: {}", postId, SecurityUtil.getCurrentUserId()); PollEntity poll = getActivePollByPostId(postId); - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); PollVoteEntity vote = requireUserVote(poll.get_id(), userId); diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/entity/PostEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/post/entity/PostEntity.java index 6ff338f5..8ad50b38 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/entity/PostEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/entity/PostEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import inu.codin.codin.domain.post.dto.request.PostCreateRequestDTO; import inu.codin.codin.domain.post.exception.PostErrorCode; import inu.codin.codin.domain.post.exception.PostException; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/exception/PostErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/post/exception/PostErrorCode.java index 416c211d..57d07a65 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/exception/PostErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/exception/PostErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/exception/PostException.java b/codin-core/src/main/java/inu/codin/codin/domain/post/exception/PostException.java index abb17beb..ac6b1e5c 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/exception/PostException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/exception/PostException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/scheduler/exception/SchedulerErrorCode.java b/codin-core/src/main/java/inu/codin/codin/domain/post/scheduler/exception/SchedulerErrorCode.java index 46a6bace..7d0716db 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/scheduler/exception/SchedulerErrorCode.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/scheduler/exception/SchedulerErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.scheduler.exception; -import inu.codin.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/scheduler/exception/SchedulerException.java b/codin-core/src/main/java/inu/codin/codin/domain/post/scheduler/exception/SchedulerException.java index 68e08457..e73a79b4 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/scheduler/exception/SchedulerException.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/scheduler/exception/SchedulerException.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.scheduler.exception; -import inu.codin.codin.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/security/OwnershipPolicy.java b/codin-core/src/main/java/inu/codin/codin/domain/post/security/OwnershipPolicy.java index b4fffd18..897f870b 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/security/OwnershipPolicy.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/security/OwnershipPolicy.java @@ -1,6 +1,7 @@ package inu.codin.codin.domain.post.security; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.common.util.ObjectIdUtil; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.post.entity.PostEntity; import inu.codin.codin.domain.post.service.PostQueryService; import inu.codin.codin.domain.post.domain.comment.entity.CommentEntity; @@ -39,7 +40,7 @@ public ReplyCommentEntity assertReplyOwner(ObjectId replyId) { } private void validateOwner(ObjectId ownerId) { - ObjectId current = SecurityUtils.getCurrentUserId(); - SecurityUtils.validateOwners(current, ownerId); // 불일치 시 예외 + String current = SecurityUtil.getCurrentUserId(); + SecurityUtil.validateOwners(current, ObjectIdUtil.toString(ownerId)); // 불일치 시 예외 } } \ No newline at end of file diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostCommandService.java b/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostCommandService.java index 4e572dc4..0d1283c8 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostCommandService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostCommandService.java @@ -1,9 +1,9 @@ package inu.codin.codin.domain.post.service; -import inu.codin.codin.common.security.exception.JwtException; -import inu.codin.codin.common.security.exception.SecurityErrorCode; -import inu.codin.codin.common.security.util.SecurityUtils; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.security.exception.JwtException; +import inu.codin.security.exception.SecurityErrorCode; +import inu.codin.security.util.SecurityUtil; +import inu.codin.common.util.ObjectIdUtil; import inu.codin.codin.domain.post.dto.request.PostAnonymousUpdateRequestDTO; import inu.codin.codin.domain.post.dto.request.PostContentUpdateRequestDTO; import inu.codin.codin.domain.post.dto.request.PostCreateRequestDTO; @@ -22,6 +22,8 @@ import java.util.List; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Slf4j @Service @RequiredArgsConstructor @@ -36,7 +38,7 @@ public class PostCommandService { * @param postImages 이미지 파일 리스트 */ public void createPost(PostCreateRequestDTO postCreateRequestDTO, List postImages) { - log.info("게시물 생성 시작. UserId: {}, 제목: {}", SecurityUtils.getCurrentUserId(), postCreateRequestDTO.getTitle()); + log.info("게시물 생성 시작. UserId: {}, 제목: {}", SecurityUtil.getCurrentUserId(), postCreateRequestDTO.getTitle()); List imageUrls = postInteractionService.handleImageUpload(postImages); ObjectId userId = validateUserAndPost(postCreateRequestDTO.getPostCategory()); @@ -163,13 +165,13 @@ public void assignAnonymousNumber(PostEntity post, ObjectId userId) { private ObjectId validateUserAndPost(PostCategory postCategory) { if (isPrivileged()) { // ADMIN / MANAGER 는 카테고리/유저 상태 검증을 통과시킴 - return SecurityUtils.getCurrentUserId(); + return toObjectId(SecurityUtil.getCurrentUserId()); } assertCategoryWriteAllowed(postCategory); - ObjectId userId = SecurityUtils.getCurrentUserId(); - SecurityUtils.validateUser(userId); - return userId; + String userId = SecurityUtil.getCurrentUserId(); + SecurityUtil.validateUser(userId); + return toObjectId(userId); } private PostEntity assertPostOwner(ObjectId postId){ @@ -180,14 +182,14 @@ private PostEntity assertPostOwner(ObjectId postId){ private void assertCategoryWriteAllowed(PostCategory postCategory) { - if (SecurityUtils.getCurrentUserRole().equals(UserRole.USER) && + if (SecurityUtil.getCurrentUserRole().equals(UserRole.USER) && postCategory.toString().split("_")[0].equals("EXTRACURRICULAR")) { throw new JwtException(SecurityErrorCode.ACCESS_DENIED, "비교과 게시글에 대한 권한이 없습니다."); } } private boolean isPrivileged() { - UserRole role = SecurityUtils.getCurrentUserRole(); + UserRole role = UserRole.valueOf(SecurityUtil.getCurrentUserRole()); return role == UserRole.ADMIN || role == UserRole.MANAGER; } diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostDtoAssembler.java b/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostDtoAssembler.java index 4b21f469..cf5b4f01 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostDtoAssembler.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostDtoAssembler.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.service; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.like.entity.LikeType; import inu.codin.codin.domain.like.service.LikeService; import inu.codin.codin.domain.post.domain.hits.service.HitsService; @@ -26,6 +26,8 @@ import java.util.List; import java.util.Objects; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + /** * PostEntity를 다양한 Response DTO로 변환하는 책임을 담당하는 어셈블러 * CQRS의 Query 측면에서 DTO 조립 로직을 분리하여 단일 책임 원칙을 준수 @@ -70,7 +72,7 @@ public PostPageItemResponseDTO toPageItem(PostEntity post, ObjectId currentUserI * PostEntity 리스트를 PostPageItemResponseDTO 리스트로 변환 */ public List toPageItemList(List posts) { - ObjectId currentUserId = SecurityUtils.getCurrentUserIdOrNull(); + ObjectId currentUserId = toObjectId(SecurityUtil.getCurrentUserIdOrNull()); return posts.stream() .map(post -> toPageItem(post, currentUserId)) .toList(); diff --git a/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostQueryService.java b/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostQueryService.java index 8cf1984e..4a131302 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostQueryService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/post/service/PostQueryService.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.post.service; -import inu.codin.codin.common.security.util.SecurityUtils; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.security.util.SecurityUtil; +import inu.codin.common.util.ObjectIdUtil; import inu.codin.codin.domain.block.service.BlockService; import inu.codin.codin.domain.post.domain.best.BestEntity; import inu.codin.codin.domain.post.domain.best.BestService; @@ -26,6 +26,8 @@ import java.util.Optional; import java.util.regex.Pattern; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Slf4j @Service @RequiredArgsConstructor @@ -54,7 +56,7 @@ public PostPageResponse getAllPosts(PostCategory postCategory, int pageNumber) { */ public PostPageItemResponseDTO getPostWithDetail(String postId) { PostEntity post = findPostById(ObjectIdUtil.toObjectId(postId)); - ObjectId userId = SecurityUtils.getCurrentUserIdOrNull(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserIdOrNull()); postInteractionService.increaseHits(post, userId); return postDtoAssembler.toPageItem(post, userId); } @@ -65,7 +67,7 @@ public PostPageItemResponseDTO getPostWithDetail(String postId) { public Optional getPostDetailById(ObjectId postId) { return postRepository.findByIdAndNotDeleted(postId) .map(post -> { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); postInteractionService.increaseHits(post, userId); return postDtoAssembler.toPageItem(post, userId); }); diff --git a/codin-core/src/main/java/inu/codin/codin/domain/report/controller/ReportController.java b/codin-core/src/main/java/inu/codin/codin/domain/report/controller/ReportController.java index ae3eb2eb..0ac127e2 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/report/controller/ReportController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/report/controller/ReportController.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.report.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.report.dto.request.ReportCreateRequestDto; import inu.codin.codin.domain.report.dto.request.ReportExecuteRequestDto; import inu.codin.codin.domain.report.dto.response.ReportPageResponse; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/report/entity/ReportEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/report/entity/ReportEntity.java index f30be721..1798c493 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/report/entity/ReportEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/report/entity/ReportEntity.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.report.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/report/service/ReportService.java b/codin-core/src/main/java/inu/codin/codin/domain/report/service/ReportService.java index 0903201a..df5c8aee 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/report/service/ReportService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/report/service/ReportService.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.report.service; -import inu.codin.codin.common.exception.NotFoundException; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.common.exception.NotFoundException; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.post.domain.comment.dto.response.CommentResponseDTO; import inu.codin.codin.domain.post.domain.comment.entity.CommentEntity; import inu.codin.codin.domain.post.domain.comment.repository.CommentRepository; @@ -42,6 +42,8 @@ import java.util.stream.Collectors; import inu.codin.codin.domain.post.dto.response.PostPageItemResponseDTO; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -69,7 +71,7 @@ public void createReport(@Valid ReportCreateRequestDto reportCreateRequestDto) { log.info("신고 생성 요청 시작: {} ", reportCreateRequestDto); // 신고한 유저 검증 - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); ObjectId reportTargetId = new ObjectId(reportCreateRequestDto.getReportTargetId()); @@ -157,7 +159,7 @@ private ObjectId validateAndGetReportedUserId(ReportTargetType reportTargetType, public void handleReport(ReportExecuteRequestDto requestDto) { log.info("신고 처리 요청: {}", requestDto.getReportTargetId()); //현재 관리자 ID - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); ObjectId targetObjectId = new ObjectId(requestDto.getReportTargetId()); // 해당 신고 대상에 대한 모든 신고 가져오기 @@ -267,7 +269,7 @@ public void resolveReport(String reportTargetId) { log.info(" 신고대상 유지 요청: 신고 ID: {}", reportTargetId); ObjectId targetObjectId = new ObjectId(reportTargetId); - ObjectId userId = SecurityUtils.getCurrentUserId(); // 현재 유저 ID + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); // 현재 유저 ID // 신고 존재 확인 List reports = reportRepository.findByReportTargetId(targetObjectId); diff --git a/codin-core/src/main/java/inu/codin/codin/domain/scrap/controller/ScrapController.java b/codin-core/src/main/java/inu/codin/codin/domain/scrap/controller/ScrapController.java index f64a06fc..30ebcefe 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/scrap/controller/ScrapController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/scrap/controller/ScrapController.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.scrap.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.scrap.service.ScrapService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/scrap/entity/ScrapEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/scrap/entity/ScrapEntity.java index e2887743..d25e611b 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/scrap/entity/ScrapEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/scrap/entity/ScrapEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.scrap.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/scrap/service/ScrapService.java b/codin-core/src/main/java/inu/codin/codin/domain/scrap/service/ScrapService.java index 38d1d15d..ba0134ea 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/scrap/service/ScrapService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/scrap/service/ScrapService.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.scrap.service; -import inu.codin.codin.common.exception.NotFoundException; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.common.exception.NotFoundException; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.post.repository.PostRepository; import inu.codin.codin.domain.scrap.entity.ScrapEntity; import inu.codin.codin.domain.scrap.repository.ScrapRepository; @@ -13,6 +13,8 @@ import java.util.Optional; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @RequiredArgsConstructor @Slf4j @@ -30,7 +32,7 @@ public class ScrapService { public String toggleScrap(String id) { log.info("스크랩 토글 요청 - postId: {}", id); ObjectId postId = new ObjectId(id); - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); postRepository.findByIdAndNotDeleted(postId) .orElseThrow(() -> { diff --git a/codin-core/src/main/java/inu/codin/codin/domain/user/controller/UserController.java b/codin-core/src/main/java/inu/codin/codin/domain/user/controller/UserController.java index 6b92ff97..f4283313 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/user/controller/UserController.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/user/controller/UserController.java @@ -1,7 +1,8 @@ package inu.codin.codin.domain.user.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.post.dto.response.PostPageResponse; +import inu.codin.codin.domain.user.dto.request.UserNameUpdateRequestDto; import inu.codin.codin.domain.user.dto.request.UserNicknameRequestDto; import inu.codin.codin.domain.user.dto.request.UserTicketingParticipationInfoUpdateRequest; import inu.codin.codin.domain.user.dto.response.UserInfoResponseDto; @@ -104,6 +105,16 @@ public ResponseEntity> updateUserProfile(@RequestPart(value = .body(new SingleResponse<>(200, "유저 사진 수정 완료", null)); } + @Operation( + summary = "유저 이름 수정" + ) + @PutMapping("/name") + public ResponseEntity> updateUserName(@RequestBody @Valid UserNameUpdateRequestDto userUpdateRequestDto){ + userService.updateUserName(userUpdateRequestDto); + return ResponseEntity.ok() + .body(new SingleResponse<>(200, "유저 이름 수정 완료", null)); + } + @Operation( summary = "유저 티켓팅 수령자 정보 반환 (학번, 이름, 소속) - [Ticketing]" ) @@ -118,7 +129,7 @@ public ResponseEntity> ge ) @PutMapping("/ticketing-participation") public ResponseEntity> updateUserTicketingParticipationInfo( - @RequestBody UserTicketingParticipationInfoUpdateRequest updateRequest + @Valid @RequestBody UserTicketingParticipationInfoUpdateRequest updateRequest ) { return ResponseEntity.ok().body(new SingleResponse<>(200, "유저 티켓팅 수령 정보 수정 완료", userService.updateUserTicketingParticipationInfo(updateRequest))); diff --git a/codin-core/src/main/java/inu/codin/codin/domain/user/dto/request/UserNameUpdateRequestDto.java b/codin-core/src/main/java/inu/codin/codin/domain/user/dto/request/UserNameUpdateRequestDto.java new file mode 100644 index 00000000..0ad5c370 --- /dev/null +++ b/codin-core/src/main/java/inu/codin/codin/domain/user/dto/request/UserNameUpdateRequestDto.java @@ -0,0 +1,22 @@ +package inu.codin.codin.domain.user.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Getter; + +import java.beans.ConstructorProperties; + +@Getter +public class UserNameUpdateRequestDto { + + @Schema(description = "이름", example = "횃불이") + @NotBlank(message = "이름은 비어 있을 수 없습니다.") + @Size(min = 2, max = 10, message = "이름은 2~10자여야 합니다.") + private String name; + + @ConstructorProperties({"name"}) + public UserNameUpdateRequestDto(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/codin-core/src/main/java/inu/codin/codin/domain/user/dto/request/UserTicketingParticipationInfoUpdateRequest.java b/codin-core/src/main/java/inu/codin/codin/domain/user/dto/request/UserTicketingParticipationInfoUpdateRequest.java index 070267c9..c6754b67 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/user/dto/request/UserTicketingParticipationInfoUpdateRequest.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/user/dto/request/UserTicketingParticipationInfoUpdateRequest.java @@ -1,7 +1,9 @@ package inu.codin.codin.domain.user.dto.request; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; import lombok.Builder; import lombok.Getter; @@ -10,7 +12,15 @@ @Schema(description = "유저 티켓팅 수령자 정보 Update Dto") public class UserTicketingParticipationInfoUpdateRequest { + @Schema(description = "학과", example = "컴퓨터공학부") private Department department; + + @Schema(description = "학번", example = "2025123456") private String studentId; + @Schema(description = "이름", example = "횃불이") + @NotBlank(message = "이름은 비어 있을 수 없습니다.") + @Size(min = 2, max = 10, message = "이름은 2~10자여야 합니다.") + private String name; + } diff --git a/codin-core/src/main/java/inu/codin/codin/domain/user/dto/response/UserInfoResponseDto.java b/codin-core/src/main/java/inu/codin/codin/domain/user/dto/response/UserInfoResponseDto.java index 2e0bdcc1..78ebdd4d 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/user/dto/response/UserInfoResponseDto.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/user/dto/response/UserInfoResponseDto.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.user.dto.response; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.user.entity.UserEntity; import inu.codin.codin.domain.user.entity.UserRole; import lombok.Builder; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/user/dto/response/UserTicketingParticipationInfoResponse.java b/codin-core/src/main/java/inu/codin/codin/domain/user/dto/response/UserTicketingParticipationInfoResponse.java index 543f55ed..57b70b8c 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/user/dto/response/UserTicketingParticipationInfoResponse.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/user/dto/response/UserTicketingParticipationInfoResponse.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.user.dto.response; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.user.entity.UserEntity; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/user/entity/UserEntity.java b/codin-core/src/main/java/inu/codin/codin/domain/user/entity/UserEntity.java index eca9f5b2..780a63b7 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/user/entity/UserEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/user/entity/UserEntity.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.user.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.Department; import inu.codin.codin.common.security.dto.PortalLoginResponseDto; import inu.codin.codin.domain.notification.entity.NotificationPreference; import inu.codin.codin.domain.user.dto.request.UserTicketingParticipationInfoUpdateRequest; @@ -59,6 +59,10 @@ public UserEntity(String email, String password, String studentId, String name, this.status = status; } + public void updateName(String name) { + this.name = name; + } + public void updateNickname(String nickname) { this.nickname = nickname; } @@ -108,5 +112,6 @@ public void updateTotalSuspensionEndDate(LocalDateTime totalSuspensionEndDate){ public void updateParticipationInfo(UserTicketingParticipationInfoUpdateRequest updateRequest) { this.studentId = updateRequest.getStudentId(); this.department = updateRequest.getDepartment(); + this.name = updateRequest.getName(); } } diff --git a/codin-core/src/main/java/inu/codin/codin/domain/user/repository/UserRepository.java b/codin-core/src/main/java/inu/codin/codin/domain/user/repository/UserRepository.java index e65c0b34..58d83bc4 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/user/repository/UserRepository.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/user/repository/UserRepository.java @@ -30,4 +30,12 @@ public interface UserRepository extends MongoRepository { //정지 종료일(suspensionEndDate)이 현재 날짜보다 이전($lt) @Query("{'status': 'SUSPENDED', 'totalSuspensionEndDate': { $lt: ?0 }}") List findSuspendedUsers(LocalDateTime now); + + // 이름이 정확히 1글자(한글/영문), role=USER, status=ACTIVE + @Query("{ 'role': 'USER', 'status': 'ACTIVE', 'name': { $regex: '^[A-Za-z가-힣]$' } }") + List findActiveUsersWithOneCharName(); + + @Query("{ 'role': 'ADMIN', 'status': 'ACTIVE' }") + List findActiveAdmins(); + } diff --git a/codin-core/src/main/java/inu/codin/codin/domain/user/security/CustomUserDetails.java b/codin-core/src/main/java/inu/codin/codin/domain/user/security/CustomUserDetails.java index 8b4401a6..7d11e391 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/user/security/CustomUserDetails.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/user/security/CustomUserDetails.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.user.security; -import inu.codin.codin.common.dto.Department; +import inu.codin.common.dto.Department; import inu.codin.codin.domain.user.entity.UserEntity; import inu.codin.codin.domain.user.entity.UserRole; import inu.codin.codin.domain.user.entity.UserStatus; diff --git a/codin-core/src/main/java/inu/codin/codin/domain/user/service/UserService.java b/codin-core/src/main/java/inu/codin/codin/domain/user/service/UserService.java index 52801bff..f33f1c42 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/user/service/UserService.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/user/service/UserService.java @@ -1,8 +1,8 @@ package inu.codin.codin.domain.user.service; -import inu.codin.codin.common.exception.NotFoundException; -import inu.codin.codin.common.security.service.JwtService; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.common.exception.NotFoundException; +import inu.codin.security.service.JwtService; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.like.entity.LikeEntity; import inu.codin.codin.domain.like.entity.LikeType; import inu.codin.codin.domain.like.repository.LikeRepository; @@ -14,6 +14,7 @@ import inu.codin.codin.domain.post.service.PostDtoAssembler; import inu.codin.codin.domain.scrap.entity.ScrapEntity; import inu.codin.codin.domain.scrap.repository.ScrapRepository; +import inu.codin.codin.domain.user.dto.request.UserNameUpdateRequestDto; import inu.codin.codin.domain.user.dto.request.UserNicknameRequestDto; import inu.codin.codin.domain.user.dto.request.UserTicketingParticipationInfoUpdateRequest; import inu.codin.codin.domain.user.dto.response.UserInfoResponseDto; @@ -35,6 +36,9 @@ import java.util.List; import java.util.Optional; + +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Slf4j @Service @RequiredArgsConstructor @@ -53,7 +57,7 @@ public class UserService { //해당 유저가 작성한 모든 글 반환 :: 게시글 내용 + 댓글+대댓글의 수 + 좋아요,스크랩 count 수 반환 public PostPageResponse getAllUserPosts(int pageNumber) { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); log.info("[게시글 조회] 유저 ID: {}, 페이지 번호: {}", userId, pageNumber); PageRequest pageRequest = PageRequest.of(pageNumber, 20, Sort.by("createdAt").descending()); @@ -68,7 +72,7 @@ public PostPageResponse getAllUserPosts(int pageNumber) { } public PostPageResponse getPostUserInteraction(int pageNumber, InteractionType interactionType) { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); log.info("[유저 상호작용 조회] 유저 ID: {}, 타입: {}, 페이지 번호: {}", userId, interactionType, pageNumber); PageRequest pageRequest = PageRequest.of(pageNumber, 20, Sort.by("createdAt").descending()); @@ -118,7 +122,7 @@ public PostPageResponse getPostUserInteraction(int pageNumber, InteractionType i } public void deleteUser(HttpServletResponse response) { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); UserEntity user = userRepository.findByUserId(userId) .orElseThrow(() -> { @@ -135,7 +139,7 @@ public void deleteUser(HttpServletResponse response) { } public UserInfoResponseDto getUserInfo() { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); log.info("[유저 정보 조회] 유저 ID: {}", userId); UserEntity user = userRepository.findById(userId) @@ -154,7 +158,7 @@ public void updateUserInfo(@Valid UserNicknameRequestDto userNicknameRequestDto) throw new UserNicknameDuplicateException("이미 사용중인 닉네임입니다."); } - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); log.info("[유저 정보 업데이트] 현재 사용자 ID: {}", userId); UserEntity user = userRepository.findById(userId) @@ -169,7 +173,7 @@ public void updateUserInfo(@Valid UserNicknameRequestDto userNicknameRequestDto) } public void updateUserProfile(MultipartFile profileImage) { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); log.info("[프로필 이미지 업데이트] 현재 사용자 ID: {}", userId); UserEntity user = userRepository.findById(userId) @@ -184,13 +188,45 @@ public void updateUserProfile(MultipartFile profileImage) { log.info("[프로필 이미지 업데이트 성공] 사용자 ID: {}, 프로필 이미지 URL: {}", userId, profileImageUrl); } + public void updateUserName(@Valid UserNameUpdateRequestDto request){ + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); + log.info("[유저 실명 수정] 현재 사용자 ID: {}, 요청 이름: {}", userId, request.getName()); + + UserEntity user = userRepository.findById(userId) + .orElseThrow(() -> { + log.info("[유저 실명 수정 실패] 유저 정보를 찾을 수 없음. 사용자 ID: {}", userId); + return new NotFoundException("유저 정보를 찾을 수 없습니다."); + }); + + String newName = request.getName().trim(); + + if (newName.isEmpty()) { + throw new IllegalArgumentException("이름은 비어 있을 수 없습니다."); + } + if (newName.length() > 10) { + throw new IllegalArgumentException("이름은 10자 이하여야 합니다."); + } + if (!newName.matches("^[가-힣a-zA-Z]+$")) { + throw new IllegalArgumentException("이름은 한글 또는 영어만 입력 가능합니다."); + } + + if (newName.equals(user.getName())) { + log.info("[유저 실명 수정] 변경 사항 없음. 사용자 ID: {}", userId); + return; + } + + user.updateName(newName); + userRepository.save(user); + + log.info("[유저 실명 수정 성공] 사용자 ID: {}, 변경 이름: {}", userId, newName); + } /** * 유저 티켓팅 수령 정보 반환 * @return UserTicketingParticipationInfoResponse 유저의 학번, 이름, 소속 Dto 반환 */ public UserTicketingParticipationInfoResponse getUserTicketingParticipationInfo() { return UserTicketingParticipationInfoResponse.of( - userRepository.findByUserId(SecurityUtils.getCurrentUserId()) + userRepository.findByUserId(toObjectId(SecurityUtil.getCurrentUserId())) .orElseThrow(() -> new NotFoundException("유저 정보를 찾을 수 없습니다."))); } @@ -199,11 +235,12 @@ public UserTicketingParticipationInfoResponse getUserTicketingParticipationInfo( * @return UserTicketingParticipationInfoResponse 유저의 학번, 이름, 소속 Dto 반환 */ public UserTicketingParticipationInfoResponse updateUserTicketingParticipationInfo(UserTicketingParticipationInfoUpdateRequest updateRequest) { - UserEntity userEntity = userRepository.findByUserId(SecurityUtils.getCurrentUserId()) + UserEntity userEntity = userRepository.findByUserId(toObjectId(SecurityUtil.getCurrentUserId())) .orElseThrow(() -> new NotFoundException("유저 정보를 찾을 수 없습니다.")); userEntity.updateParticipationInfo(updateRequest); userEntity = userRepository.save(userEntity); + log.info("User Ticketing Participation Update: Email: {}, Name:{}", userEntity.getEmail(),userEntity.getName()); return UserTicketingParticipationInfoResponse.of(userEntity); } diff --git a/codin-core/src/main/java/inu/codin/codin/domain/user/service/UserValidator.java b/codin-core/src/main/java/inu/codin/codin/domain/user/service/UserValidator.java index 9aa92121..f5dd0376 100644 --- a/codin-core/src/main/java/inu/codin/codin/domain/user/service/UserValidator.java +++ b/codin-core/src/main/java/inu/codin/codin/domain/user/service/UserValidator.java @@ -1,6 +1,5 @@ package inu.codin.codin.domain.user.service; -import inu.codin.codin.common.exception.NotFoundException; import inu.codin.codin.domain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.bson.types.ObjectId; diff --git a/codin-core/src/main/java/inu/codin/codin/infra/fcm/FcmConfig.java b/codin-core/src/main/java/inu/codin/codin/infra/fcm/FcmConfig.java index e00f381c..d00e8a87 100644 --- a/codin-core/src/main/java/inu/codin/codin/infra/fcm/FcmConfig.java +++ b/codin-core/src/main/java/inu/codin/codin/infra/fcm/FcmConfig.java @@ -6,12 +6,14 @@ import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; import java.io.IOException; import java.io.InputStream; @Component +@Profile("fcm") @Slf4j public class FcmConfig { diff --git a/codin-core/src/main/java/inu/codin/codin/infra/fcm/controller/FcmControllerImpl.java b/codin-core/src/main/java/inu/codin/codin/infra/fcm/controller/FcmControllerImpl.java index 96eac2eb..6877c898 100644 --- a/codin-core/src/main/java/inu/codin/codin/infra/fcm/controller/FcmControllerImpl.java +++ b/codin-core/src/main/java/inu/codin/codin/infra/fcm/controller/FcmControllerImpl.java @@ -1,6 +1,6 @@ package inu.codin.codin.infra.fcm.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.infra.fcm.controller.swagger.FcmController; import inu.codin.codin.infra.fcm.dto.request.FcmTokenRequest; import inu.codin.codin.infra.fcm.service.FcmService; diff --git a/codin-core/src/main/java/inu/codin/codin/infra/fcm/controller/swagger/FcmController.java b/codin-core/src/main/java/inu/codin/codin/infra/fcm/controller/swagger/FcmController.java index d8d1eb15..3f9f3656 100644 --- a/codin-core/src/main/java/inu/codin/codin/infra/fcm/controller/swagger/FcmController.java +++ b/codin-core/src/main/java/inu/codin/codin/infra/fcm/controller/swagger/FcmController.java @@ -1,6 +1,6 @@ package inu.codin.codin.infra.fcm.controller.swagger; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.infra.fcm.dto.request.FcmTokenRequest; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; diff --git a/codin-core/src/main/java/inu/codin/codin/infra/fcm/entity/FcmTokenEntity.java b/codin-core/src/main/java/inu/codin/codin/infra/fcm/entity/FcmTokenEntity.java index d5991104..cd94831f 100644 --- a/codin-core/src/main/java/inu/codin/codin/infra/fcm/entity/FcmTokenEntity.java +++ b/codin-core/src/main/java/inu/codin/codin/infra/fcm/entity/FcmTokenEntity.java @@ -1,6 +1,6 @@ package inu.codin.codin.infra.fcm.entity; -import inu.codin.codin.common.dto.BaseTimeEntity; +import inu.codin.common.dto.BaseTimeEntity; import inu.codin.codin.infra.fcm.exception.FcmDuplicatedTokenException; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/codin-core/src/main/java/inu/codin/codin/infra/fcm/service/FcmService.java b/codin-core/src/main/java/inu/codin/codin/infra/fcm/service/FcmService.java index eda13223..1bba3fa0 100644 --- a/codin-core/src/main/java/inu/codin/codin/infra/fcm/service/FcmService.java +++ b/codin-core/src/main/java/inu/codin/codin/infra/fcm/service/FcmService.java @@ -1,8 +1,8 @@ package inu.codin.codin.infra.fcm.service; import com.google.firebase.messaging.*; -import inu.codin.codin.common.exception.NotFoundException; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.common.exception.NotFoundException; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.notification.entity.NotificationPreference; import inu.codin.codin.domain.user.entity.UserEntity; import inu.codin.codin.domain.user.repository.UserRepository; @@ -21,6 +21,8 @@ import java.util.List; import java.util.Optional; +import static inu.codin.common.util.ObjectIdUtil.toObjectId; + @Service @Slf4j @RequiredArgsConstructor @@ -35,7 +37,7 @@ public class FcmService { */ public void saveFcmToken(@Valid FcmTokenRequest fcmTokenRequest) { // 유저의 FCM 토큰이 존재하는지 확인 - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); Optional fcmToken = fcmTokenRepository.findByUserId(userId); if (fcmToken.isPresent()) { // 이미 존재하는 유저라면 토큰 추가 @@ -159,7 +161,7 @@ private void removeFcmToken(FcmTokenEntity fcmTokenEntity, String fcmToken) { * @param topic 구독할 토픽 이름 */ public void subscribeTopic(String topic) { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); FcmTokenEntity fcmTokenEntity = fcmTokenRepository.findByUserId(userId) .orElseThrow(() -> new FcmTokenNotFoundException("유저의 FCM 토큰이 존재하지 않습니다.")); @@ -178,7 +180,7 @@ public void subscribeTopic(String topic) { * @param topic 구독 해제할 토픽 이름 */ public void unsubscribeTopic(String topic) { - ObjectId userId = SecurityUtils.getCurrentUserId(); + ObjectId userId = toObjectId(SecurityUtil.getCurrentUserId()); FcmTokenEntity fcmTokenEntity = fcmTokenRepository.findByUserId(userId) .orElseThrow(() -> new FcmTokenNotFoundException("유저의 FCM 토큰이 존재하지 않습니다.")); diff --git a/codin-core/src/main/resources/application-aws.yml b/codin-core/src/main/resources/application-aws.yml new file mode 100644 index 00000000..eadbceed --- /dev/null +++ b/codin-core/src/main/resources/application-aws.yml @@ -0,0 +1,12 @@ +# 이름 : application-aws.yml +# [취급주의] 실제 AWS 환경에서 사용할 설정 파일 +cloud: + aws: + s3: + defaultProfileImageUrl: ${AWS_S3_DEFAULT_PROFILE_IMAGE_URL} + bucket: ${AWS_S3_BUCKET} + stack.auto: ${AWS_STACK_AUTO} + region.static: ${AWS_REGION_STATIC} + credentials: + accessKey: ${AWS_ACCESS_KEY} + secretKey: ${AWS_SECRET_KEY} diff --git a/codin-core/src/main/resources/application-email.yml b/codin-core/src/main/resources/application-email.yml new file mode 100644 index 00000000..c3770193 --- /dev/null +++ b/codin-core/src/main/resources/application-email.yml @@ -0,0 +1,14 @@ +spring: + mail: + host: ${SPRING_MAIL_HOST} + port: ${SPRING_MAIL_PORT} + username: ${SPRING_MAIL_USERNAME} + password: ${SPRING_MAIL_PASSWORD} + properties: + mail.smtp.debug: ${SPRING_MAIL_PROPERTIES_SMTP_DEBUG} + mail.smtp.connectiontimeout: ${SPRING_MAIL_PROPERTIES_SMTP_CONNECTIONTIMEOUT} #1초 + mail.starttls.enable: ${SPRING_MAIL_PROPERTIES_STARTTLS_ENABLE} + mail.smtp.auth: ${SPRING_MAIL_PROPERTIES_SMTP_AUTH} + + access: + domain: ${SPRING_MAIL_ACCESS_DOMAIN} \ No newline at end of file diff --git a/codin-core/src/main/resources/application-jwt.yml b/codin-core/src/main/resources/application-jwt.yml new file mode 100644 index 00000000..9ba47355 --- /dev/null +++ b/codin-core/src/main/resources/application-jwt.yml @@ -0,0 +1,8 @@ +# 이름 : applicatino-jwt.yml +# JWT 설정 파일 +spring: + jwt: + secret: ${SPRING_JWT_SECRET} + expiration: + access: ${SPRING_JWT_EXPIRATION_ACCESS} + refresh: ${SPRING_JWT_EXPIRATION_REFRESH} diff --git a/codin-core/src/main/resources/application-local.yml b/codin-core/src/main/resources/application-local.yml new file mode 100644 index 00000000..a5ae1c52 --- /dev/null +++ b/codin-core/src/main/resources/application-local.yml @@ -0,0 +1,20 @@ +# 이름 : application-local.yml + +security: + permit-all: + urls: + - "/auth/**" + - "/v3/api/test1" + - "/ws-stomp/**" + - "/suspends" + - "/login/oauth2/code/**" + # --- Swagger / Springdoc --- + - "/swagger-ui/**" + - "/v3/api-docs/**" + - "/swagger-resources/**" + - "/webjars/**" + # --- 정적/루트/헬스체크 --- + - "/" + - "/favicon.ico" + - "/.well-known/**" + - "/actuator/health" diff --git a/codin-core/src/main/resources/application-oauth.yml b/codin-core/src/main/resources/application-oauth.yml new file mode 100644 index 00000000..67d8d756 --- /dev/null +++ b/codin-core/src/main/resources/application-oauth.yml @@ -0,0 +1,46 @@ +# 이름 : application-oauth.yml +# oauth2 설정을 위한 파일 +spring: + security: + oauth2: + client: + registration: + google: + client-id: ${GOOGLE_CLIENT_ID} + client-secret: ${GOOGLE_CLIENT_SECRET} + redirect-uri: "${baseUrl}/login/oauth2/code/google" + authorization-grant-type: authorization_code + scope: + - email + - profile + apple: + client-id: ${APPLE_CLIENT_ID} + client-secret: ${APPLE_CLIENT_SECRET} # 동적으로 생성된 애플 client-secret + redirect-uri: "${baseUrl}/login/oauth2/code/apple" + authorization-grant-type: authorization_code + clientAuthenticationMethod: POST + clientName: Apple + scope: + - email + - name + provider: +# google: +# authorization-uri: "https://accounts.google.com/o/oauth2/auth?prompt=consent" + apple: + authorization-uri: ${APPLE_AUTHORIZATION_URI} + token-uri: ${APPLE_TOKEN_URI} + jwk-set-uri: ${APPLE_JWK_SET_URI} + user-name-attribute: sub + user-info-uri: ${APPLE_USER_INFO_URI} + +apple: + auth: + token-url: ${APPLE_AUTH_TOKEN_URL} + public-key-url: ${APPLE_AUTH_PUBLIC_KEY_URL} + redirect-uri: ${APPLE_REDIRECT_URI} + iss: ${APPLE_ISS} + aud: ${APPLE_AUD} + team-id: ${APPLE_TEAM_ID} + key: + id: ${APPLE_KEY_ID} + path: ${APPLE_KEY_PATH} \ No newline at end of file diff --git a/codin-core/src/main/resources/application-prod.yml b/codin-core/src/main/resources/application-prod.yml new file mode 100644 index 00000000..e2ec335a --- /dev/null +++ b/codin-core/src/main/resources/application-prod.yml @@ -0,0 +1,9 @@ +# 이름 : application-prod.yml + +security: + #투표 이벤트 공개 API + public-api: + urls: + - "/posts/category" + - "/posts/{postId}" + - "/comments/post/{postId}" \ No newline at end of file diff --git a/codin-core/src/main/resources/application.yml b/codin-core/src/main/resources/application.yml new file mode 100644 index 00000000..cba69db1 --- /dev/null +++ b/codin-core/src/main/resources/application.yml @@ -0,0 +1,69 @@ +# Spring Boot Configuration +# application.yml +# src/main/resources/** +spring: + application: + name: codin-core + config: + import: optional:file:./.env[.properties], optional:file:./.env.core[.properties] + profiles: + active: + local + include: jwt, aws, email, oauth + servlet: + multipart: + max-request-size: ${SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE} + max-file-size: ${SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE} + data: + redis: # [Redis] 설정 + host: ${REDIS_HOST} + port: ${REDIS_PORT} + password: ${REDIS_PASSWORD} + mongodb: # [MongoDB] 설정 + host: ${MONGODB_HOST} + port: ${MONGODB_PORT} + database: ${MONGODB_DATABASE} + + +google: + firebase: + project-id: ${GOOGLE_FIREBASE_PROJECT_ID} + key-path: ${GOOGLE_FIREBASE_KEY_PATH} + +server: + servlet: + encoding: + force-response: true + charset: UTF-8 + enabled: true + domain: ${SERVER_DOMAIN} + forward-headers-strategy: framework + port: ${SERVER_PORT} + +springdoc: + api-docs: + enabled: ${SPRINGDOC_API_DOCS_ENABLED} + swagger-ui: + groups-order: DESC + doc-expansion: list + tags-sorter: alpha + operationsSorter: method + disable-swagger-default-url: true + display-request-duration: true + +schedule: + path: ${SCHEDULE_PATH} + department: + cron: ${SCHEDULE_DEPARTMENT_CRON:0 00 4 * * *} + starinu: + cron: ${SCHEDULE_STARINU_CRON:0 30 4 * * *} + +lecture: + python: + path: ${LECTURE_PYTHON_PATH} + file: + path: ${LECTURE_FILE_PATH} + +logging: + level: + org.springframework.security: DEBUG \ No newline at end of file diff --git a/codin-core/src/test/java/inu/codin/codin/domain/block/controller/BlockControllerTest.java b/codin-core/src/test/java/inu/codin/codin/domain/block/controller/BlockControllerTest.java index 6909b8b1..21133416 100644 --- a/codin-core/src/test/java/inu/codin/codin/domain/block/controller/BlockControllerTest.java +++ b/codin-core/src/test/java/inu/codin/codin/domain/block/controller/BlockControllerTest.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.block.controller; -import inu.codin.codin.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codin.domain.block.exception.BlockErrorCode; import inu.codin.codin.domain.block.exception.BlockException; import inu.codin.codin.domain.block.service.BlockService; diff --git a/codin-core/src/test/java/inu/codin/codin/domain/block/service/BlockServiceTest.java b/codin-core/src/test/java/inu/codin/codin/domain/block/service/BlockServiceTest.java index bfb5b7a3..f28f5d60 100644 --- a/codin-core/src/test/java/inu/codin/codin/domain/block/service/BlockServiceTest.java +++ b/codin-core/src/test/java/inu/codin/codin/domain/block/service/BlockServiceTest.java @@ -1,7 +1,7 @@ package inu.codin.codin.domain.block.service; -import inu.codin.codin.common.security.util.SecurityUtils; -import inu.codin.codin.common.util.ObjectIdUtil; +import inu.codin.security.util.SecurityUtil; +import inu.codin.common.util.ObjectIdUtil; import inu.codin.codin.domain.block.entity.BlockEntity; import inu.codin.codin.domain.block.exception.BlockErrorCode; import inu.codin.codin.domain.block.exception.BlockException; @@ -41,9 +41,9 @@ class BlockServiceTest { @Test @DisplayName("유저 차단 성공") void blockUser_성공() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)){ + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)){ //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); BlockEntity existingBlock = BlockEntity.ofNew(testUserId); when(blockRepository.findByUserId(testUserId)).thenReturn(Optional.of(existingBlock)); @@ -62,9 +62,9 @@ class BlockServiceTest { @Test @DisplayName("유저 차단 성공 - 새로운 BlockEntity 생성") void blockUser_성공_새로운BlockEntity생성() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)) { + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)) { //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); when(blockRepository.findByUserId(testUserId)).thenReturn(Optional.empty()); when(blockRepository.save(any(BlockEntity.class))).thenReturn(BlockEntity.ofNew(testUserId)); @@ -80,9 +80,9 @@ class BlockServiceTest { @Test @DisplayName("유저 차단 실패 - 자기 자신 차단") void blockUser_실패_자기자신차단() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)) { + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)) { //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); //when & then assertThatThrownBy(() -> blockService.blockUser(testUserId.toString())) @@ -95,9 +95,9 @@ class BlockServiceTest { @Test @DisplayName("유저 차단 실패 - 차단유저 존재하지 않음") void blockUser_실패_차단유저X() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)) { + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)) { //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); doNothing().when(userValidator).validateUserExists(eq(testUserId), any()); doThrow(new BlockException(BlockErrorCode.BLOCKED_USER_NOT_FOUND)) @@ -114,9 +114,9 @@ class BlockServiceTest { @Test @DisplayName("유저 차단 실패 - 차단 실행 유저 존재하지 않음") void blockUser_실패_차단실행유저X() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)) { + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)) { //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); doThrow(new BlockException(BlockErrorCode.BLOCKING_USER_NOT_FOUND)) .when(userValidator).validateUserExists(eq(testUserId), any()); @@ -132,9 +132,9 @@ class BlockServiceTest { @Test @DisplayName("유저 차단해제 성공") void unblockUser_성공() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)) { + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)) { //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); BlockEntity existingBlock = BlockEntity.ofNew(testUserId); existingBlock.addBlockedUser(blockedUserId); @@ -154,9 +154,9 @@ class BlockServiceTest { @Test @DisplayName("유저 차단해제 실패 - 자기 자신 차단해제") void unblockUser_실패_자기자신차단해제() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)) { + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)) { //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); //when & then assertThatThrownBy(() -> blockService.unblockUser(testUserId.toString())) @@ -169,9 +169,9 @@ class BlockServiceTest { @Test @DisplayName("유저 차단해제 실패 - 차단 정보 없음") void unblockUser_실패_차단정보없음() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)) { + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)) { //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); when(blockRepository.findByUserId(testUserId)).thenReturn(Optional.empty()); @@ -186,9 +186,9 @@ class BlockServiceTest { @Test @DisplayName("유저 차단 실패 - 이미 차단된 사용자") void blockUser_실패_이미차단된사용자() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)) { + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)) { //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); BlockEntity existingBlock = BlockEntity.ofNew(testUserId); existingBlock.addBlockedUser(blockedUserId); // 이미 차단된 상태 @@ -211,9 +211,9 @@ class BlockServiceTest { @Test @DisplayName("유저 차단해제 실패 - 차단되지 않은 사용자") void unblockUser_실패_차단되지않은사용자() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)) { + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)) { //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); // 다른 사용자는 차단되어 있지만, 요청한 사용자는 차단되지 않은 상태 ObjectId otherBlockedUserId = ObjectIdUtil.toObjectId("507f1f77bcf86cd799439011"); @@ -238,9 +238,9 @@ class BlockServiceTest { @Test @DisplayName("차단된 유저 목록 조회 성공") void getBlockedUsers_성공() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)) { + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)) { //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); BlockEntity blockEntity = spy(BlockEntity.ofNew(testUserId)); when(blockEntity.getBlockedUsers()).thenReturn(List.of(blockedUserId)); @@ -261,9 +261,9 @@ class BlockServiceTest { @Test @DisplayName("차단된 유저 목록 조회 - 차단 정보 없음") void getBlockedUsers_차단정보없음() { - try (MockedStatic mSecurityUtils = mockStatic(SecurityUtils.class)) { + try (MockedStatic mSecurityUtils = mockStatic(SecurityUtil.class)) { //given - mSecurityUtils.when(SecurityUtils::getCurrentUserId).thenReturn(testUserId); + mSecurityUtils.when(SecurityUtil::getCurrentUserId).thenReturn(testUserId); when(blockRepository.findByUserId(testUserId)).thenReturn(Optional.empty()); diff --git a/codin-core/src/test/java/inu/codin/codin/domain/email/service/PasswordResetEmailServiceTest.java b/codin-core/src/test/java/inu/codin/codin/domain/email/service/PasswordResetEmailServiceTest.java index 98260040..d75e0fb7 100644 --- a/codin-core/src/test/java/inu/codin/codin/domain/email/service/PasswordResetEmailServiceTest.java +++ b/codin-core/src/test/java/inu/codin/codin/domain/email/service/PasswordResetEmailServiceTest.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.email.service; -import inu.codin.codin.common.exception.NotFoundException; +import inu.codin.common.exception.NotFoundException; import inu.codin.codin.domain.email.dto.request.JoinEmailSendRequestDto; import inu.codin.codin.domain.email.entity.EmailAuthEntity; import inu.codin.codin.domain.email.exception.EmailPasswordResetFailException; diff --git a/codin-core/src/test/java/inu/codin/codin/domain/post/PostCommandServiceTest.java b/codin-core/src/test/java/inu/codin/codin/domain/post/PostCommandServiceTest.java index d36079e6..11366dae 100644 --- a/codin-core/src/test/java/inu/codin/codin/domain/post/PostCommandServiceTest.java +++ b/codin-core/src/test/java/inu/codin/codin/domain/post/PostCommandServiceTest.java @@ -1,8 +1,7 @@ package inu.codin.codin.domain.post; -import inu.codin.codin.common.exception.NotFoundException; -import inu.codin.codin.common.security.exception.JwtException; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.security.exception.JwtException; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.post.dto.request.*; import inu.codin.codin.domain.post.entity.PostAnonymous; import inu.codin.codin.domain.post.entity.PostCategory; @@ -12,7 +11,6 @@ import inu.codin.codin.domain.post.security.OwnershipPolicy; import inu.codin.codin.domain.post.service.PostCommandService; import inu.codin.codin.domain.post.service.PostInteractionService; -import inu.codin.codin.domain.post.service.PostQueryService; import inu.codin.codin.domain.user.entity.UserRole; import org.bson.types.ObjectId; import org.junit.jupiter.api.*; @@ -41,7 +39,7 @@ class PostCommandServiceTest { @BeforeEach void setUp() { - securityUtilsMock = Mockito.mockStatic(SecurityUtils.class); + securityUtilsMock = Mockito.mockStatic(SecurityUtil.class); } @AfterEach @@ -56,8 +54,8 @@ void tearDown() throws Exception { List images = new ArrayList<>(); ObjectId userId = new ObjectId(); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); - given(SecurityUtils.getCurrentUserRole()).willReturn(UserRole.ADMIN); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserRole()).willReturn(UserRole.ADMIN); given(postInteractionService.handleImageUpload(any())).willReturn(new ArrayList<>()); given(postRepository.save(any())).willAnswer(inv -> { PostEntity entity = inv.getArgument(0); @@ -77,8 +75,8 @@ void tearDown() throws Exception { List images = new ArrayList<>(); ObjectId userId = new ObjectId(); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); - given(SecurityUtils.getCurrentUserRole()).willReturn(UserRole.USER); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserRole()).willReturn(UserRole.USER); // When & Then assertThatThrownBy(() -> postCommandService.createPost(dto, images)) @@ -96,8 +94,8 @@ void tearDown() throws Exception { List imageUrls = Arrays.asList("image1.jpg", "image2.jpg"); given(ownershipPolicy.assertPostOwner(any(ObjectId.class))).willReturn(post); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); - given(SecurityUtils.getCurrentUserRole()).willReturn(UserRole.ADMIN); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserRole()).willReturn(UserRole.ADMIN); given(postInteractionService.handleImageUpload(any())).willReturn(imageUrls); given(postRepository.save(any())).willReturn(post); @@ -114,8 +112,8 @@ void tearDown() throws Exception { PostEntity post = createPostEntity(); given(ownershipPolicy.assertPostOwner(any(ObjectId.class))).willReturn(post); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); - given(SecurityUtils.getCurrentUserRole()).willReturn(UserRole.ADMIN); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserRole()).willReturn(UserRole.ADMIN); given(postRepository.save(any())).willReturn(post); // When & Then @@ -131,8 +129,8 @@ void tearDown() throws Exception { PostEntity post = createPostEntity(); given(ownershipPolicy.assertPostOwner(any(ObjectId.class))).willReturn(post); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); - given(SecurityUtils.getCurrentUserRole()).willReturn(UserRole.ADMIN); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserRole()).willReturn(UserRole.ADMIN); given(postRepository.save(any())).willReturn(post); // When & Then @@ -147,8 +145,8 @@ void tearDown() throws Exception { PostEntity post = createPostEntity(); given(ownershipPolicy.assertPostOwner(any(ObjectId.class))).willReturn(post); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); - given(SecurityUtils.getCurrentUserRole()).willReturn(UserRole.ADMIN); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserRole()).willReturn(UserRole.ADMIN); given(postRepository.save(any())).willReturn(post); // When & Then @@ -164,8 +162,8 @@ void tearDown() throws Exception { PostEntity post = createPostEntity(); given(ownershipPolicy.assertPostOwner(any(ObjectId.class))).willReturn(post); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); - given(SecurityUtils.getCurrentUserRole()).willReturn(UserRole.ADMIN); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserRole()).willReturn(UserRole.ADMIN); doNothing().when(postInteractionService).deletePostImageInternal(any(), any()); // When & Then diff --git a/codin-core/src/test/java/inu/codin/codin/domain/post/PostDtoAssemblerTest.java b/codin-core/src/test/java/inu/codin/codin/domain/post/PostDtoAssemblerTest.java index ed3fc973..81e1eb17 100644 --- a/codin-core/src/test/java/inu/codin/codin/domain/post/PostDtoAssemblerTest.java +++ b/codin-core/src/test/java/inu/codin/codin/domain/post/PostDtoAssemblerTest.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.like.entity.LikeType; import inu.codin.codin.domain.like.service.LikeService; import inu.codin.codin.domain.post.domain.hits.service.HitsService; @@ -50,7 +50,7 @@ class PostDtoAssemblerTest { @BeforeEach void setUp() { - securityUtilsMock = Mockito.mockStatic(SecurityUtils.class); + securityUtilsMock = Mockito.mockStatic(SecurityUtil.class); } @AfterEach @@ -160,7 +160,7 @@ void tearDown() throws Exception { List posts = Arrays.asList(post1, post2); UserEntity user = createUserEntity(); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); given(userRepository.findById(any())).willReturn(Optional.of(user)); given(s3Service.getDefaultProfileImageUrl()).willReturn("default.jpg"); given(likeService.getLikeCount(any(), any())).willReturn(0); diff --git a/codin-core/src/test/java/inu/codin/codin/domain/post/PostQueryServiceTest.java b/codin-core/src/test/java/inu/codin/codin/domain/post/PostQueryServiceTest.java index 9c855dd6..23aea4fc 100644 --- a/codin-core/src/test/java/inu/codin/codin/domain/post/PostQueryServiceTest.java +++ b/codin-core/src/test/java/inu/codin/codin/domain/post/PostQueryServiceTest.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.block.service.BlockService; import inu.codin.codin.domain.post.domain.best.BestEntity; import inu.codin.codin.domain.post.domain.best.BestService; @@ -46,7 +46,7 @@ class PostQueryServiceTest { @BeforeEach void setUp() { - securityUtilsMock = Mockito.mockStatic(SecurityUtils.class); + securityUtilsMock = Mockito.mockStatic(SecurityUtil.class); } @AfterEach @@ -106,7 +106,7 @@ void tearDown() throws Exception { PostPageItemResponseDTO mockDto = createMockPostPageItemResponseDTO(); given(postRepository.findByIdAndNotDeleted(any())).willReturn(Optional.of(post)); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(postDtoAssembler.toPageItem(post, userId)).willReturn(mockDto); doNothing().when(postInteractionService).increaseHits(any(), any()); @@ -140,7 +140,7 @@ void tearDown() throws Exception { PostPageItemResponseDTO mockDto = createMockPostPageItemResponseDTO(); given(postRepository.findByIdAndNotDeleted(postId)).willReturn(Optional.of(post)); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(postDtoAssembler.toPageItem(post, userId)).willReturn(mockDto); doNothing().when(postInteractionService).increaseHits(any(), any()); diff --git a/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/CommentCommandServiceTest.java b/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/CommentCommandServiceTest.java index 4eb3f9b6..7d69c05c 100644 --- a/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/CommentCommandServiceTest.java +++ b/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/CommentCommandServiceTest.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.comment; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.notification.service.NotificationService; import inu.codin.codin.domain.post.domain.best.BestService; import inu.codin.codin.domain.post.domain.comment.dto.request.CommentCreateRequestDTO; @@ -43,7 +43,7 @@ class CommentCommandServiceTest { @BeforeEach void setUp() { - securityUtilsMock = Mockito.mockStatic(SecurityUtils.class); + securityUtilsMock = Mockito.mockStatic(SecurityUtil.class); } @AfterEach @@ -60,7 +60,7 @@ void tearDown() throws Exception { ObjectId userId = new ObjectId(); given(postQueryService.findPostById(any())).willReturn(post); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(commentRepository.save(any())).willAnswer(inv -> { CommentEntity entity = inv.getArgument(0); setIdField(entity, new ObjectId()); @@ -90,7 +90,7 @@ void tearDown() throws Exception { setIdField(post, new ObjectId()); given(postQueryService.findPostById(any())).willReturn(post); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(commentRepository.save(any())).willAnswer(inv -> { CommentEntity entity = inv.getArgument(0); setIdField(entity, new ObjectId()); @@ -120,7 +120,7 @@ void tearDown() throws Exception { setIdField(post, new ObjectId()); given(postQueryService.findPostById(any())).willReturn(post); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(commentRepository.save(any())).willAnswer(inv -> { CommentEntity entity = inv.getArgument(0); setIdField(entity, new ObjectId()); @@ -151,9 +151,9 @@ void tearDown() throws Exception { ObjectId userId = new ObjectId(); given(ownershipPolicy.assertCommentOwner(any(ObjectId.class))).willReturn(comment); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); - doNothing().when(SecurityUtils.class); - SecurityUtils.validateUser(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); + doNothing().when(SecurityUtil.class); + SecurityUtil.validateUser(userId); given(commentRepository.save(any())).willReturn(comment); // When & Then @@ -170,8 +170,8 @@ void tearDown() throws Exception { ObjectId userId = comment.getUserId(); given(ownershipPolicy.assertCommentOwner(any(ObjectId.class))).willReturn(comment); - doNothing().when(SecurityUtils.class); - SecurityUtils.validateUser(userId); + doNothing().when(SecurityUtil.class); + SecurityUtil.validateUser(userId); given(postQueryService.findPostById(comment.getPostId())).willReturn(post); given(commentRepository.save(any())).willReturn(comment); doNothing().when(postCommandService).decreaseCommentCount(any()); diff --git a/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/CommentQueryServiceTest.java b/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/CommentQueryServiceTest.java index 1dfe7b6f..039056bb 100644 --- a/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/CommentQueryServiceTest.java +++ b/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/CommentQueryServiceTest.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.comment; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.like.entity.LikeType; import inu.codin.codin.domain.like.service.LikeService; import inu.codin.codin.domain.post.domain.comment.dto.response.CommentResponseDTO; @@ -46,7 +46,7 @@ class CommentQueryServiceTest { @BeforeEach void setUp() { - securityUtilsMock = Mockito.mockStatic(SecurityUtils.class); + securityUtilsMock = Mockito.mockStatic(SecurityUtil.class); } @AfterEach @@ -79,7 +79,7 @@ void tearDown() throws Exception { given(postQueryService.getUserAnonymousNumber(any(), any())).willReturn(1); given(replyQueryService.getRepliesByCommentId(any(), any())).willReturn(replies); given(likeService.getLikeCount(eq(LikeType.COMMENT), any())).willReturn(5); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); given(likeService.isLiked(eq(LikeType.COMMENT), any(), (ObjectId) any())).willReturn(false); // When @@ -132,7 +132,7 @@ void tearDown() throws Exception { given(postQueryService.getUserAnonymousNumber(any(), any())).willReturn(2); // 익명 번호 given(replyQueryService.getRepliesByCommentId(any(), any())).willReturn(replies); given(likeService.getLikeCount(eq(LikeType.COMMENT), any())).willReturn(3); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); given(likeService.isLiked(eq(LikeType.COMMENT), any(), (ObjectId) any())).willReturn(true); // When @@ -180,7 +180,7 @@ void tearDown() throws Exception { ObjectId commentId = new ObjectId(); ObjectId userId = new ObjectId(); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(likeService.isLiked(LikeType.COMMENT, commentId.toString(), userId)).willReturn(true); // When @@ -197,7 +197,7 @@ void tearDown() throws Exception { ObjectId commentId = new ObjectId(); ObjectId userId = new ObjectId(); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(likeService.isLiked(LikeType.COMMENT, commentId.toString(), userId)).willReturn(false); // When @@ -232,7 +232,7 @@ void tearDown() throws Exception { given(postQueryService.getUserAnonymousNumber(any(), any())).willReturn(1); given(replyQueryService.getRepliesByCommentId(any(), any())).willReturn(replies); given(likeService.getLikeCount(eq(LikeType.COMMENT), any())).willReturn(0); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); given(likeService.isLiked(eq(LikeType.COMMENT), any(), (ObjectId) any())).willReturn(false); // When diff --git a/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/reply/ReplyCommandServiceTest.java b/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/reply/ReplyCommandServiceTest.java index 07642e6a..b6703949 100644 --- a/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/reply/ReplyCommandServiceTest.java +++ b/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/reply/ReplyCommandServiceTest.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.comment.reply; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.notification.service.NotificationService; import inu.codin.codin.domain.post.domain.best.BestService; import inu.codin.codin.domain.post.domain.comment.entity.CommentEntity; @@ -10,7 +10,6 @@ import inu.codin.codin.domain.post.domain.comment.reply.entity.ReplyCommentEntity; import inu.codin.codin.domain.post.domain.comment.reply.repository.ReplyCommentRepository; import inu.codin.codin.domain.post.domain.comment.reply.service.ReplyCommandService; -import inu.codin.codin.domain.post.domain.comment.reply.service.ReplyQueryService; import inu.codin.codin.domain.post.entity.PostCategory; import inu.codin.codin.domain.post.entity.PostEntity; import inu.codin.codin.domain.post.security.OwnershipPolicy; @@ -43,7 +42,7 @@ class ReplyCommandServiceTest { @BeforeEach void setUp() { - securityUtilsMock = Mockito.mockStatic(SecurityUtils.class); + securityUtilsMock = Mockito.mockStatic(SecurityUtil.class); } @AfterEach @@ -63,7 +62,7 @@ void tearDown() throws Exception { given(commentQueryService.findCommentById(any())).willReturn(comment); given(postQueryService.findPostById(comment.getPostId())).willReturn(post); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(replyCommentRepository.save(any())).willAnswer(inv -> { ReplyCommentEntity entity = inv.getArgument(0); setIdField(entity, new ObjectId()); @@ -95,7 +94,7 @@ void tearDown() throws Exception { given(commentQueryService.findCommentById(any())).willReturn(comment); given(postQueryService.findPostById(comment.getPostId())).willReturn(post); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(replyCommentRepository.save(any())).willAnswer(inv -> { ReplyCommentEntity entity = inv.getArgument(0); setIdField(entity, new ObjectId()); @@ -134,7 +133,7 @@ void tearDown() throws Exception { given(commentQueryService.findCommentById(any())).willReturn(comment); given(postQueryService.findPostById(comment.getPostId())).willReturn(post); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(replyCommentRepository.save(any())).willAnswer(inv -> { ReplyCommentEntity entity = inv.getArgument(0); setIdField(entity, new ObjectId()); @@ -181,8 +180,8 @@ void tearDown() throws Exception { ObjectId userId = reply.getUserId(); given(ownershipPolicy.assertReplyOwner(any(ObjectId.class))).willReturn(reply); - doNothing().when(SecurityUtils.class); - SecurityUtils.validateUser(userId); + doNothing().when(SecurityUtil.class); + SecurityUtil.validateUser(userId); given(commentQueryService.findCommentById(reply.getCommentId())).willReturn(comment); given(postQueryService.findPostById(comment.getPostId())).willReturn(post); given(replyCommentRepository.save(any())).willReturn(reply); diff --git a/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/reply/ReplyQueryServiceTest.java b/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/reply/ReplyQueryServiceTest.java index d24082f7..a46d44b7 100644 --- a/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/reply/ReplyQueryServiceTest.java +++ b/codin-core/src/test/java/inu/codin/codin/domain/post/domain/comment/reply/ReplyQueryServiceTest.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.comment.reply; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.like.entity.LikeType; import inu.codin.codin.domain.like.service.LikeService; import inu.codin.codin.domain.post.domain.comment.dto.response.CommentResponseDTO; @@ -43,7 +43,7 @@ class ReplyQueryServiceTest { @BeforeEach void setUp() { - securityUtilsMock = Mockito.mockStatic(SecurityUtils.class); + securityUtilsMock = Mockito.mockStatic(SecurityUtil.class); } @AfterEach @@ -73,7 +73,7 @@ void tearDown() throws Exception { given(s3Service.getDefaultProfileImageUrl()).willReturn("default.jpg"); given(postQueryService.getUserAnonymousNumber(any(), any())).willReturn(1); given(likeService.getLikeCount(eq(LikeType.REPLY), any())).willReturn(3); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); given(likeService.isLiked(eq(LikeType.REPLY), any(),(ObjectId) any())).willReturn(false); // When @@ -121,7 +121,7 @@ void tearDown() throws Exception { given(s3Service.getDefaultProfileImageUrl()).willReturn("default.jpg"); given(postQueryService.getUserAnonymousNumber(postAnonymous, userId)).willReturn(3); // 익명 번호 given(likeService.getLikeCount(eq(LikeType.REPLY), any())).willReturn(2); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); given(likeService.isLiked(eq(LikeType.REPLY), any(),(ObjectId) any())).willReturn(true); // When @@ -169,7 +169,7 @@ void tearDown() throws Exception { ObjectId replyId = new ObjectId(); ObjectId userId = new ObjectId(); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(likeService.isLiked(LikeType.REPLY, replyId.toString(), userId)).willReturn(true); // When @@ -186,7 +186,7 @@ void tearDown() throws Exception { ObjectId replyId = new ObjectId(); ObjectId userId = new ObjectId(); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(likeService.isLiked(LikeType.REPLY, replyId.toString(), userId)).willReturn(false); // When @@ -219,7 +219,7 @@ void tearDown() throws Exception { given(s3Service.getDefaultProfileImageUrl()).willReturn("default.jpg"); given(postQueryService.getUserAnonymousNumber(any(), any())).willReturn(1); given(likeService.getLikeCount(eq(LikeType.REPLY), any())).willReturn(0); - given(SecurityUtils.getCurrentUserId()).willReturn(new ObjectId()); + given(SecurityUtil.getCurrentUserId()).willReturn(new ObjectId()); given(likeService.isLiked(eq(LikeType.REPLY), any(),(ObjectId) any())).willReturn(false); // When diff --git a/codin-core/src/test/java/inu/codin/codin/domain/post/domain/poll/PollCommandServiceTest.java b/codin-core/src/test/java/inu/codin/codin/domain/post/domain/poll/PollCommandServiceTest.java index d503c53a..1365f40c 100644 --- a/codin-core/src/test/java/inu/codin/codin/domain/post/domain/poll/PollCommandServiceTest.java +++ b/codin-core/src/test/java/inu/codin/codin/domain/post/domain/poll/PollCommandServiceTest.java @@ -1,6 +1,6 @@ package inu.codin.codin.domain.post.domain.poll; -import inu.codin.codin.common.security.util.SecurityUtils; +import inu.codin.security.util.SecurityUtil; import inu.codin.codin.domain.post.domain.poll.dto.request.PollCreateRequestDTO; import inu.codin.codin.domain.post.domain.poll.dto.request.PollVotingRequestDTO; import inu.codin.codin.domain.post.domain.poll.entity.PollEntity; @@ -43,7 +43,7 @@ class PollCommandServiceTest { @BeforeEach void setUp() { - securityUtilsMock = Mockito.mockStatic(SecurityUtils.class); + securityUtilsMock = Mockito.mockStatic(SecurityUtil.class); } @AfterEach @@ -85,7 +85,7 @@ void tearDown() throws Exception { given(postQueryService.findPostById(any())).willReturn(post); given(pollRepository.findByPostId(post.get_id())).willReturn(Optional.of(poll)); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(pollVoteRepository.existsByPollIdAndUserId(poll.get_id(), userId)).willReturn(false); given(pollVoteRepository.save(any())).willReturn(createPollVoteEntity()); given(pollRepository.incOption(eq(poll.get_id()), eq(0))).willReturn(1L); @@ -151,7 +151,7 @@ void tearDown() throws Exception { given(postQueryService.findPostById(any())).willReturn(post); given(pollRepository.findByPostId(post.get_id())).willReturn(Optional.of(poll)); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(pollVoteRepository.existsByPollIdAndUserId(poll.get_id(), userId)).willReturn(true); // When & Then @@ -170,7 +170,7 @@ void tearDown() throws Exception { given(postQueryService.findPostById(any())).willReturn(post); given(pollRepository.findByPostId(post.get_id())).willReturn(Optional.of(poll)); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(pollVoteRepository.existsByPollIdAndUserId(poll.get_id(), userId)).willReturn(false); // When & Then @@ -189,7 +189,7 @@ void tearDown() throws Exception { given(postQueryService.findPostById(any())).willReturn(post); given(pollRepository.findByPostId(post.get_id())).willReturn(Optional.of(poll)); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(pollVoteRepository.existsByPollIdAndUserId(poll.get_id(), userId)).willReturn(false); // When & Then @@ -209,7 +209,7 @@ void tearDown() throws Exception { given(postQueryService.findPostById(any())).willReturn(post); given(pollRepository.findByPostId(post.get_id())).willReturn(Optional.of(poll)); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(pollVoteRepository.findByPollIdAndUserId(poll.get_id(), userId)).willReturn(Optional.of(vote)); given(pollRepository.dcrOptionIfPositive(eq(poll.get_id()), eq(0))).willReturn(1L); @@ -231,7 +231,7 @@ void tearDown() throws Exception { given(postQueryService.findPostById(any())).willReturn(post); given(pollRepository.findByPostId(post.get_id())).willReturn(Optional.of(poll)); - given(SecurityUtils.getCurrentUserId()).willReturn(userId); + given(SecurityUtil.getCurrentUserId()).willReturn(userId); given(pollVoteRepository.findByPollIdAndUserId(poll.get_id(), userId)).willReturn(Optional.empty()); // When & Then diff --git a/codin-core/src/test/java/inu/codin/codin/domain/user/UserRepositoryQueryTest.java b/codin-core/src/test/java/inu/codin/codin/domain/user/UserRepositoryQueryTest.java new file mode 100644 index 00000000..086c8028 --- /dev/null +++ b/codin-core/src/test/java/inu/codin/codin/domain/user/UserRepositoryQueryTest.java @@ -0,0 +1,118 @@ +package inu.codin.codin.domain.user; + +import inu.codin.codin.domain.user.entity.UserEntity; +import inu.codin.codin.domain.user.entity.UserRole; +import inu.codin.codin.domain.user.entity.UserStatus; +import inu.codin.codin.domain.user.repository.UserRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.MongoDBContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@Testcontainers +@DataMongoTest +class UserRepositoryQueryTest { + + @Container + static MongoDBContainer mongo = new MongoDBContainer("mongo:7.0"); + + @DynamicPropertySource + static void mongoProps(DynamicPropertyRegistry r) { + r.add("spring.data.mongodb.uri", mongo::getReplicaSetUrl); + // 컬렉션/DB 이름은 엔티티/설정에 따라 자동 + } + + @Autowired + UserRepository userRepository; + + @BeforeEach + void setUp() { + userRepository.deleteAll(); + + // 매칭되어야 하는 케이스 (USER + ACTIVE + 이름 1글자) + userRepository.save(UserEntity.builder() + .email("user1@inu.ac.kr") + .name("김") // 1글자 한글 + .nickname("u1") + .role(UserRole.USER) + .status(UserStatus.ACTIVE) + .build()); + + userRepository.save(UserEntity.builder() + .email("user2@inu.ac.kr") + .name("A") // 1글자 영문 + .nickname("u2") + .role(UserRole.USER) + .status(UserStatus.ACTIVE) + .build()); + + // 매칭되면 안 되는 케이스들 + userRepository.save(UserEntity.builder() + .email("user3@inu.ac.kr") + .name("홍 길동") // 공백 포함 (정규식 불일치) + .nickname("u3") + .role(UserRole.USER) + .status(UserStatus.ACTIVE) + .build()); + + userRepository.save(UserEntity.builder() + .email("user4@inu.ac.kr") + .name("AB") // 2글자 + .nickname("u4") + .role(UserRole.USER) + .status(UserStatus.ACTIVE) + .build()); + + userRepository.save(UserEntity.builder() + .email("user5@inu.ac.kr") + .name("김") // 1글자지만 INACTIVE + .nickname("u5") + .role(UserRole.USER) + .status(UserStatus.SUSPENDED) // 또는 INACTIVE + .build()); + + // ADMIN들 + userRepository.save(UserEntity.builder() + .email("admin1@inu.ac.kr") + .name("관리자") + .nickname("a1") + .role(UserRole.ADMIN) + .status(UserStatus.ACTIVE) + .build()); + + userRepository.save(UserEntity.builder() + .email("admin2@inu.ac.kr") + .name("Admin") + .nickname("a2") + .role(UserRole.ADMIN) + .status(UserStatus.ACTIVE) + .build()); + } + + @Test + void findActiveUsersWithOneCharName_정상동작() { + List result = userRepository.findActiveUsersWithOneCharName(); + // user1(김), user2(A)만 매칭되어야 함 -> 총 2명 + assertThat(result).extracting("email") + .containsExactlyInAnyOrder("user1@inu.ac.kr", "user2@inu.ac.kr"); + assertThat(result).hasSize(2); + } + + @Test + void findActiveAdmins_정상동작() { + List result = userRepository.findActiveAdmins(); + assertThat(result).extracting("email") + .containsExactlyInAnyOrder("admin1@inu.ac.kr", "admin2@inu.ac.kr"); + assertThat(result).hasSize(2); + } +} \ No newline at end of file diff --git a/codin-lecture-api/build.gradle b/codin-lecture-api/build.gradle index b1de355c..eb19105c 100644 --- a/codin-lecture-api/build.gradle +++ b/codin-lecture-api/build.gradle @@ -31,6 +31,9 @@ dependencyManagement { } dependencies { + implementation project(':codin-security') + implementation project(':codin-common') + implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' diff --git a/codin-security/build.gradle b/codin-security/build.gradle new file mode 100644 index 00000000..653db60f --- /dev/null +++ b/codin-security/build.gradle @@ -0,0 +1,65 @@ +plugins { + id 'java-library' + id 'org.springframework.boot' version '3.2.0' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'inu.codin' +version = '1.0.0' +sourceCompatibility = '17' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + // Codin Common 의존성 (공통 응답, 예외 클래스) + api project(':codin-common') + + // Spring Boot Security + api 'org.springframework.boot:spring-boot-starter-security' + api 'org.springframework.boot:spring-boot-starter-web' + + // JWT + api 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + // Configuration Properties + api 'org.springframework.boot:spring-boot-configuration-processor' + + // Jackson for JSON processing + api 'com.fasterxml.jackson.core:jackson-databind' + + // Lombok + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + // Test Dependencies + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' + testImplementation 'org.junit.jupiter:junit-jupiter' + testImplementation 'org.mockito:mockito-core' + testImplementation 'org.assertj:assertj-core' +} + +tasks.named('test') { + useJUnitPlatform() +} + +// Disable Spring Boot plugin's jar task since this is a library +jar { + enabled = true + archiveClassifier = '' +} + +// Disable the bootJar task since this is a library +bootJar { + enabled = false +} \ No newline at end of file diff --git a/codin-security/src/main/java/inu/codin/security/config/CodinSecurityAutoConfiguration.java b/codin-security/src/main/java/inu/codin/security/config/CodinSecurityAutoConfiguration.java new file mode 100644 index 00000000..0f5e8026 --- /dev/null +++ b/codin-security/src/main/java/inu/codin/security/config/CodinSecurityAutoConfiguration.java @@ -0,0 +1,29 @@ +package inu.codin.security.config; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; + +/** + * Codin Security 자동 구성 클래스 + * + * Spring Boot 3 방식의 AutoConfiguration으로 구현 + * 의존성만 추가하면 자동으로 보안 설정이 활성화됩니다. + * + * 조건: + * - Web 애플리케이션인 경우 + * - Spring Security가 클래스패스에 있는 경우 + */ +@AutoConfiguration +@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) +@ConditionalOnClass({HttpSecurity.class}) +@ComponentScan(basePackages = "inu.codin.security") +@EnableConfigurationProperties({PermitAllProperties.class, PublicApiProperties.class}) +public class CodinSecurityAutoConfiguration { + + // 필요한 경우 추가 빈 정의 가능 + // 현재는 @ComponentScan으로 자동 스캔되므로 별도 빈 정의 불필요 +} \ No newline at end of file diff --git a/codin-core/src/main/java/inu/codin/codin/common/dto/PermitAllProperties.java b/codin-security/src/main/java/inu/codin/security/config/PermitAllProperties.java similarity index 74% rename from codin-core/src/main/java/inu/codin/codin/common/dto/PermitAllProperties.java rename to codin-security/src/main/java/inu/codin/security/config/PermitAllProperties.java index 7fdc58c2..476b891a 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/dto/PermitAllProperties.java +++ b/codin-security/src/main/java/inu/codin/security/config/PermitAllProperties.java @@ -1,10 +1,11 @@ -package inu.codin.codin.common.dto; +package inu.codin.security.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; +import java.util.ArrayList; import java.util.List; @Getter @@ -12,5 +13,5 @@ @Configuration @ConfigurationProperties(prefix = "security.permit-all") public class PermitAllProperties { - private List urls; + private List urls = new ArrayList<>(); } \ No newline at end of file diff --git a/codin-core/src/main/java/inu/codin/codin/common/dto/PublicApiProperties.java b/codin-security/src/main/java/inu/codin/security/config/PublicApiProperties.java similarity index 74% rename from codin-core/src/main/java/inu/codin/codin/common/dto/PublicApiProperties.java rename to codin-security/src/main/java/inu/codin/security/config/PublicApiProperties.java index 14b3d1e5..86ca358c 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/dto/PublicApiProperties.java +++ b/codin-security/src/main/java/inu/codin/security/config/PublicApiProperties.java @@ -1,10 +1,11 @@ -package inu.codin.codin.common.dto; +package inu.codin.security.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; +import java.util.ArrayList; import java.util.List; @Getter @@ -12,5 +13,5 @@ @Configuration @ConfigurationProperties(prefix = "security.public-api") public class PublicApiProperties { - private List urls; + private List urls = new ArrayList<>(); } \ No newline at end of file diff --git a/codin-security/src/main/java/inu/codin/security/config/ResourceServerSecurityConfig.java b/codin-security/src/main/java/inu/codin/security/config/ResourceServerSecurityConfig.java new file mode 100644 index 00000000..50420e37 --- /dev/null +++ b/codin-security/src/main/java/inu/codin/security/config/ResourceServerSecurityConfig.java @@ -0,0 +1,198 @@ +package inu.codin.security.config; + +/* + * SecurityConfig 리팩토링 이유: + * + * 기존: Authorization Server + Resource Server 역할을 모두 담당하는 단일 설정 + * - OAuth2 로그인 처리 (Google, Apple) + * - JWT 토큰 발급 + * - JWT 토큰 검증 + * - 권한 체크 + * + * 변경: Resource Server 전용 설정으로 분리 (codin-security 모듈) + * - JWT 토큰 검증만 담당 + * - 권한 체크 담당 + * - CORS 설정 담당 + * + * OAuth2 로그인 및 토큰 발급은 별도의 codin-auth 모듈로 분리 예정 + */ + +import inu.codin.security.filter.ExceptionHandlerFilter; +import inu.codin.security.filter.JwtAuthenticationFilter; +import inu.codin.security.service.JwtService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.hierarchicalroles.RoleHierarchy; +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; +import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.List; + +@Slf4j +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class ResourceServerSecurityConfig { + + /* + * Resource Server에 필요한 의존성만 유지 + * - JwtService: JWT 토큰 검증용 + * - PermitAllProperties: 인증 불필요 경로 설정 + * - PublicApiProperties: 공개 API 경로 설정 + * + * 제거된 의존성들 (codin-auth로 이동 예정): + * - OAuth2LoginSuccessHandler, OAuth2LoginFailureHandler: OAuth2 로그인 처리 + * - CustomOAuth2UserService, AppleOAuth2UserService: OAuth2 사용자 정보 처리 + * - ClientRegistrationRepository: OAuth2 클라이언트 등록 정보 + * - CustomOAuth2AccessTokenResponseClient: OAuth2 토큰 응답 처리 + * - UserDetailsService: 사용자 상세 정보 서비스 + */ + private final JwtService jwtService; + private final PermitAllProperties permitAllProperties; + private final PublicApiProperties publicApiProperties; + + @Value("${server.domain:http://localhost:8080}") + private String BASEURL; + + /** + * Resource Server용 보안 필터 체인 + * + * 변경사항: + * 1. OAuth2 로그인 설정 제거 - Authorization Server 역할 제거 + * 2. AuthenticationEntryPoint 제거 - 기본 401 응답 사용 + * 3. JWT 검증 필터만 유지 - Resource Server 핵심 기능 + * 4. 무상태 세션 정책 유지 - JWT 기반 인증 + */ + @Bean + public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception { + + http + .cors(cors -> cors.configurationSource(corsConfigurationSource())) // CORS 설정 유지 + .csrf(CsrfConfigurer::disable) // CSRF 비활성화 - JWT 사용으로 불필요 + .formLogin(FormLoginConfigurer::disable) // 폼 로그인 비활성화 - JWT 전용 + .httpBasic(httpBasic -> httpBasic.disable()) // HTTP Basic 비활성화 + .sessionManagement(sessionManagement -> sessionManagement + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 무상태 세션 - JWT 기반 + ) + // 요청별 권한 설정 - Resource Server의 핵심 기능 + .authorizeHttpRequests((authorizeHttpRequests) -> + authorizeHttpRequests + .requestMatchers(permitAllProperties.getUrls().toArray(new String[0])).permitAll() + .requestMatchers(publicApiProperties.getUrls().toArray(new String[0])).permitAll() + .requestMatchers(ADMIN_AUTH_PATHS).hasRole("ADMIN") + .requestMatchers(MANAGER_AUTH_PATHS).hasRole("MANAGER") + .requestMatchers(USER_AUTH_PATHS).hasRole("USER") + .anyRequest().hasRole("USER") + ) + /* + * OAuth2 로그인 설정 제거됨 - Authorization Server로 이동 + * 기존 .oauth2Login() 설정은 codin-auth 모듈에서 담당 + */ + + // JWT 인증 필터 추가 - Resource Server의 핵심 기능 + .addFilterBefore( + new JwtAuthenticationFilter(jwtService, permitAllProperties, publicApiProperties), + UsernamePasswordAuthenticationFilter.class + ) + // 예외 처리 필터 추가 + .addFilterBefore(new ExceptionHandlerFilter(), LogoutFilter.class); + return http.build(); + } + + /* + * OAuth2 관련 Bean들 제거됨 - Authorization Server로 이동 예정 + * - OAuth2AuthorizationRequestBasedOnCookieRepository + * - delegatingOAuth2UserService + * - AuthenticationManager + * - PasswordEncoder + * - AuthorizationRequestRepository + */ + + /** + * 역할 계층 구조 설정 + * Resource Server에서 권한 체크 시 사용 + * + * Spring Security 6.2.0 버전 변경사항: + * - 이전 5.x: RoleHierarchyImpl.fromHierarchy() 사용 + * - 현재 6.2.0: fromHierarchy() deprecated, setHierarchy() 사용 권장 + */ + @Bean + public RoleHierarchy roleHierarchy() { + RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); + roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_MANAGER > ROLE_USER"); + return roleHierarchy; + } + /** + * CORS 설정 - Resource Server에서 유지 + * + * 모든 마이크로서비스에서 공통으로 사용하는 CORS 정책 + * 프론트엔드 애플리케이션에서의 API 접근을 허용 + */ + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); // 쿠키 및 인증 정보 허용 + config.setAllowedOrigins(List.of( + "http://localhost:3000", // 개발환경 프론트엔드 + "http://localhost:8080", // 개발환경 백엔드 + BASEURL, // 운영환경 도메인 + "https://front-end-peach-two.vercel.app", // Vercel 배포 환경 + "https://front-end-dun-mu.vercel.app", + "https://appleid.apple.com" // Apple OAuth 콜백 (나중에 codin-auth로 이동) + )); + config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); + config.setAllowedHeaders(List.of( + "Authorization", // JWT 토큰 헤더 + "Content-Type", + "X-Requested-With", + "Accept", + "Cache-Control", + "X-Refresh-Token" // 리프레시 토큰 헤더 + )); + config.setExposedHeaders(List.of("Authorization")); // 클라이언트에서 읽을 수 있는 헤더 + config.setMaxAge(3600L); // 프리플라이트 요청 캐시 시간 + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return source; + } + + + /* + * Resource Server 권한 경로 설정 + * + * 이 설정들은 JWT 토큰의 권한 정보를 바탕으로 + * API 엔드포인트별 접근 권한을 제어합니다. + * + * 향후 각 마이크로서비스별로 분리하여 관리할 예정 + */ + + // USER 권한으로 접근 가능한 경로 + private static final String[] USER_AUTH_PATHS = { + "/v3/api/test2", + "/v3/api/test3", + }; + + // ADMIN 권한이 필요한 경로 + private static final String[] ADMIN_AUTH_PATHS = { + "/v3/api/test4", + }; + + // MANAGER 권한이 필요한 경로 + private static final String[] MANAGER_AUTH_PATHS = { + "/v3/api/test5", + }; +} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/exception/CustomAccessDeniedHandler.java b/codin-security/src/main/java/inu/codin/security/exception/CustomAccessDeniedHandler.java similarity index 91% rename from codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/exception/CustomAccessDeniedHandler.java rename to codin-security/src/main/java/inu/codin/security/exception/CustomAccessDeniedHandler.java index 6ef65349..29fcc35d 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/exception/CustomAccessDeniedHandler.java +++ b/codin-security/src/main/java/inu/codin/security/exception/CustomAccessDeniedHandler.java @@ -1,7 +1,7 @@ -package inu.codin.codinticketingapi.security.exception; +package inu.codin.security.exception; import com.fasterxml.jackson.databind.ObjectMapper; -import inu.codin.codinticketingapi.common.response.ExceptionResponse; +import inu.codin.common.response.ExceptionResponse; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/exception/JwtException.java b/codin-security/src/main/java/inu/codin/security/exception/JwtException.java similarity index 88% rename from codin-core/src/main/java/inu/codin/codin/common/security/exception/JwtException.java rename to codin-security/src/main/java/inu/codin/security/exception/JwtException.java index 9079c325..4d84fad5 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/exception/JwtException.java +++ b/codin-security/src/main/java/inu/codin/security/exception/JwtException.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.security.exception; +package inu.codin.security.exception; import lombok.Getter; @@ -17,3 +17,4 @@ public JwtException(SecurityErrorCode errorCode, String message) { this.errorCode = errorCode; } } + \ No newline at end of file diff --git a/codin-security/src/main/java/inu/codin/security/exception/SecurityErrorCode.java b/codin-security/src/main/java/inu/codin/security/exception/SecurityErrorCode.java new file mode 100644 index 00000000..aa604985 --- /dev/null +++ b/codin-security/src/main/java/inu/codin/security/exception/SecurityErrorCode.java @@ -0,0 +1,35 @@ +package inu.codin.security.exception; + +import inu.codin.common.exception.GlobalErrorCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum SecurityErrorCode implements GlobalErrorCode { + + INVALID_TOKEN( HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."), + EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 토큰입니다."), + TOKEN_NOT_FOUND( HttpStatus.UNAUTHORIZED, "토큰이 존재하지 않습니다."), + INVALID_SIGNATURE( HttpStatus.UNAUTHORIZED, "잘못된 토큰 서명입니다."), + ACCESS_DENIED( HttpStatus.FORBIDDEN, "접근 권한이 없습니다."), + ACCOUNT_LOCKED( HttpStatus.FORBIDDEN, "계정이 잠겼습니다. 관리자에게 문의하세요."), + INVALID_CREDENTIALS( HttpStatus.UNAUTHORIZED, "잘못된 인증 정보입니다."), + INVALID_TYPE( HttpStatus.BAD_REQUEST, "잘못된 토큰 타입입니다."); + + private final HttpStatus httpStatus; + private final String message; + + + @Override + public HttpStatus httpStatus() { + return httpStatus; + } + + @Override + public String message() { + return message; + } + +} diff --git a/codin-security/src/main/java/inu/codin/security/exception/SecurityException.java b/codin-security/src/main/java/inu/codin/security/exception/SecurityException.java new file mode 100644 index 00000000..9794dfe1 --- /dev/null +++ b/codin-security/src/main/java/inu/codin/security/exception/SecurityException.java @@ -0,0 +1,20 @@ +package inu.codin.security.exception; + +import lombok.Getter; + +@Getter +public class SecurityException extends RuntimeException { + + private final SecurityErrorCode errorCode; + + public SecurityException(SecurityErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + } + + public SecurityException(SecurityErrorCode errorCode, String message) { + super(message); + this.errorCode = errorCode; + } +} + \ No newline at end of file diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/filter/ExceptionHandlerFilter.java b/codin-security/src/main/java/inu/codin/security/filter/ExceptionHandlerFilter.java similarity index 92% rename from codin-core/src/main/java/inu/codin/codin/common/security/filter/ExceptionHandlerFilter.java rename to codin-security/src/main/java/inu/codin/security/filter/ExceptionHandlerFilter.java index 8d356f62..33f7559e 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/filter/ExceptionHandlerFilter.java +++ b/codin-security/src/main/java/inu/codin/security/filter/ExceptionHandlerFilter.java @@ -1,8 +1,8 @@ -package inu.codin.codin.common.security.filter; +package inu.codin.security.filter; import com.fasterxml.jackson.databind.ObjectMapper; -import inu.codin.codin.common.response.ExceptionResponse; -import inu.codin.codin.common.security.exception.SecurityErrorCode; +import inu.codin.common.response.ExceptionResponse; +import inu.codin.security.exception.SecurityErrorCode; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -35,6 +35,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse log.warn("[doFilterInternal] Exception in ExceptionHandlerFilter: {}", e.getMessage()); sendErrorResponse(response, SecurityErrorCode.INVALID_TOKEN); } + } private void sendErrorResponse(HttpServletResponse response, SecurityErrorCode code) throws IOException { diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/filter/JwtAuthenticationFilter.java b/codin-security/src/main/java/inu/codin/security/filter/JwtAuthenticationFilter.java similarity index 60% rename from codin-core/src/main/java/inu/codin/codin/common/security/filter/JwtAuthenticationFilter.java rename to codin-security/src/main/java/inu/codin/security/filter/JwtAuthenticationFilter.java index e7499d35..fea939bd 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/filter/JwtAuthenticationFilter.java +++ b/codin-security/src/main/java/inu/codin/security/filter/JwtAuthenticationFilter.java @@ -1,8 +1,8 @@ -package inu.codin.codin.common.security.filter; +package inu.codin.security.filter; -import inu.codin.codin.common.dto.PermitAllProperties; -import inu.codin.codin.common.dto.PublicApiProperties; -import inu.codin.codin.common.security.service.JwtService; +import inu.codin.security.config.PermitAllProperties; +import inu.codin.security.config.PublicApiProperties; +import inu.codin.security.service.JwtService; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -34,13 +34,23 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { "/swagger-resources/**" }; + /** + * JWT 토큰 검증 및 인증 처리 + * + * 변경사항: + * - JwtService를 사용하여 토큰 검증 간소화 + * - Swagger 관련 특별 처리 제거 (일관된 JWT 검증) + * - 의존성 최소화 + */ @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String requestURI = request.getRequestURI(); + // 1. 인증이 필요하지 않은 경로 체크 final boolean isPermitAll = permitAllProperties.getUrls().stream() - .anyMatch(url -> pathMatcher.match(url, requestURI)); + .anyMatch(url -> pathMatcher.match(url, requestURI)) || + Arrays.stream(SWAGGER_AUTH_PATHS).anyMatch(url -> pathMatcher.match(url, requestURI)); final boolean isPublicApi = publicApiProperties.getUrls().stream() .anyMatch(url -> pathMatcher.match(url, requestURI)); @@ -50,23 +60,19 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse return; } - String token = null; - if (Arrays.stream(SWAGGER_AUTH_PATHS).anyMatch(url -> pathMatcher.match(url, requestURI))) { - token = jwtService.getRefreshToken(request); - } else { - token = jwtService.getAccessToken(request); - } - - // Access Token이 있는 경우 - if (StringUtils.hasText(token)) { - jwtService.getUserDetailsAndSetAuthentication(token); - } else { + // 2. JWT 토큰 검증 및 인증 설정 + boolean isAuthenticated = jwtService.validateAndSetAuthentication(request); + + // 3. 인증 실패 시 PublicApi 경로는 통과, 나머지는 차단 + if (!isAuthenticated) { SecurityContextHolder.clearContext(); - - if (isPublicApi) { - filterChain.doFilter(request, response); - return; - } + + if (isPublicApi) { + filterChain.doFilter(request, response); + return; + } + // 인증이 필요한 경로에서 토큰이 없거나 유효하지 않으면 401 에러 발생 + // (ExceptionHandlerFilter에서 처리됨) } filterChain.doFilter(request, response); diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/filter/TokenValidationFilter.java b/codin-security/src/main/java/inu/codin/security/filter/TokenValidationFilter.java similarity index 91% rename from codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/filter/TokenValidationFilter.java rename to codin-security/src/main/java/inu/codin/security/filter/TokenValidationFilter.java index 9fd83e39..9d0c5813 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/filter/TokenValidationFilter.java +++ b/codin-security/src/main/java/inu/codin/security/filter/TokenValidationFilter.java @@ -1,8 +1,8 @@ -package inu.codin.codinticketingapi.security.filter; +package inu.codin.security.filter; -import inu.codin.codinticketingapi.security.jwt.JwtTokenValidator; -import inu.codin.codinticketingapi.security.jwt.TokenUserDetails; -import inu.codin.codinticketingapi.security.util.TokenUtil; +import inu.codin.security.jwt.JwtTokenValidator; +import inu.codin.security.jwt.TokenUserDetails; +import inu.codin.security.util.TokenUtil; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/jwt/JwtAuthenticationToken.java b/codin-security/src/main/java/inu/codin/security/jwt/JwtAuthenticationToken.java similarity index 94% rename from codin-core/src/main/java/inu/codin/codin/common/security/jwt/JwtAuthenticationToken.java rename to codin-security/src/main/java/inu/codin/security/jwt/JwtAuthenticationToken.java index fa82aab1..29df6d6b 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/jwt/JwtAuthenticationToken.java +++ b/codin-security/src/main/java/inu/codin/security/jwt/JwtAuthenticationToken.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.security.jwt; +package inu.codin.security.jwt; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/jwt/JwtTokenValidator.java b/codin-security/src/main/java/inu/codin/security/jwt/JwtTokenValidator.java similarity index 83% rename from codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/jwt/JwtTokenValidator.java rename to codin-security/src/main/java/inu/codin/security/jwt/JwtTokenValidator.java index 4c5dded8..bea1fbdc 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/jwt/JwtTokenValidator.java +++ b/codin-security/src/main/java/inu/codin/security/jwt/JwtTokenValidator.java @@ -1,7 +1,7 @@ -package inu.codin.codinticketingapi.security.jwt; +package inu.codin.security.jwt; -import inu.codin.codinticketingapi.security.exception.SecurityErrorCode; -import inu.codin.codinticketingapi.security.exception.SecurityException; +import inu.codin.security.exception.JwtException; +import inu.codin.security.exception.SecurityErrorCode; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; @@ -32,7 +32,7 @@ protected void init() { * 토큰 유효성 검사 (토큰 변조, 만료) * @param accessToken * @return true: 유효한 토큰 - * @throws SecurityException: 토큰 만료, 유효하지 않은 토큰 + * @throws JwtException: 토큰 만료, 유효하지 않은 토큰 */ public boolean validateAccessToken(String accessToken) { try { @@ -44,10 +44,10 @@ public boolean validateAccessToken(String accessToken) { return true; } catch (ExpiredJwtException e) { // 토큰 만료 log.error("[validateAccessToken] 토큰 만료 : {}", e.getMessage()); - throw new SecurityException(SecurityErrorCode.EXPIRED_TOKEN); + throw new JwtException(SecurityErrorCode.EXPIRED_TOKEN); } catch (Exception e) { // 토큰 변조 log.error("[validateAccessToken] 유효하지 않은 토큰 : {}", e.getMessage()); - throw new SecurityException(SecurityErrorCode.INVALID_TOKEN); + throw new JwtException(SecurityErrorCode.INVALID_TOKEN); } } diff --git a/codin-core/src/main/java/inu/codin/codin/common/security/jwt/JwtUtils.java b/codin-security/src/main/java/inu/codin/security/jwt/JwtUtils.java similarity index 97% rename from codin-core/src/main/java/inu/codin/codin/common/security/jwt/JwtUtils.java rename to codin-security/src/main/java/inu/codin/security/jwt/JwtUtils.java index 37358bbc..a0f25962 100644 --- a/codin-core/src/main/java/inu/codin/codin/common/security/jwt/JwtUtils.java +++ b/codin-security/src/main/java/inu/codin/security/jwt/JwtUtils.java @@ -1,4 +1,4 @@ -package inu.codin.codin.common.security.jwt; +package inu.codin.security.jwt; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/jwt/TokenUserDetails.java b/codin-security/src/main/java/inu/codin/security/jwt/TokenUserDetails.java similarity index 97% rename from codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/jwt/TokenUserDetails.java rename to codin-security/src/main/java/inu/codin/security/jwt/TokenUserDetails.java index 7ecba9cf..0d4e6dc4 100644 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/jwt/TokenUserDetails.java +++ b/codin-security/src/main/java/inu/codin/security/jwt/TokenUserDetails.java @@ -1,4 +1,4 @@ -package inu.codin.codinticketingsse.security.jwt; +package inu.codin.security.jwt; import lombok.Builder; import lombok.Getter; diff --git a/codin-security/src/main/java/inu/codin/security/service/JwtService.java b/codin-security/src/main/java/inu/codin/security/service/JwtService.java new file mode 100644 index 00000000..ffca2912 --- /dev/null +++ b/codin-security/src/main/java/inu/codin/security/service/JwtService.java @@ -0,0 +1,111 @@ +package inu.codin.security.service; + +import inu.codin.security.exception.JwtException; +import inu.codin.security.exception.SecurityErrorCode; +import inu.codin.security.jwt.JwtAuthenticationToken; +import inu.codin.security.jwt.JwtTokenValidator; +import inu.codin.security.jwt.TokenUserDetails; +import inu.codin.security.util.TokenUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +/** + * JWT 토큰 검증 전용 서비스 + * + * 기존 JwtService에서 검증 기능만 분리 + * - 토큰 발급 기능 제거 (codin-auth로 이동 예정) + * - 도메인 의존성 제거 (CustomUserDetailsService, RedisStorageService) + * - 순수한 토큰 검증만 담당 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class JwtService { + + private final JwtTokenValidator jwtTokenValidator; + + /** + * 요청에서 토큰을 추출하고 검증하여 SecurityContext에 설정 + * + * @param request HTTP 요청 + * @return 검증 성공 여부 + */ + public boolean validateAndSetAuthentication(HttpServletRequest request) { + try { + // 1. 토큰 추출 + String token = TokenUtil.extractToken(request); + if (token == null) { + log.debug("[validateAndSetAuthentication] 토큰이 없습니다."); + return false; + } + + // 2. 토큰 검증 + if (!jwtTokenValidator.validateAccessToken(token)) { + log.debug("[validateAndSetAuthentication] 토큰 검증 실패"); + return false; + } + + // 3. 토큰에서 사용자 정보 추출 + String userId = jwtTokenValidator.getUserId(token); + String email = jwtTokenValidator.getUsername(token); + String role = jwtTokenValidator.getUserRole(token); + + // 4. TokenUserDetails 생성 + TokenUserDetails userDetails = TokenUserDetails.fromTokenClaims( + userId, email, role, token + ); + + // 5. Authentication 객체 생성 후 SecurityContext에 설정 + JwtAuthenticationToken authentication = new JwtAuthenticationToken( + userDetails, userDetails.getAuthorities() + ); + SecurityContextHolder.getContext().setAuthentication(authentication); + + log.debug("[validateAndSetAuthentication] 인증 성공: userId={}, email={}, role={}", + userId, email, role); + return true; + + } catch (JwtException e) { + log.warn("[validateAndSetAuthentication] JWT 검증 실패: {}", e.getMessage()); + return false; + } catch (Exception e) { + log.error("[validateAndSetAuthentication] 예상치 못한 오류: {}", e.getMessage(), e); + return false; + } + } + + /** + * 토큰 유효성만 검증 (SecurityContext 설정 없이) + * + * @param token JWT 토큰 + * @return 유효성 검증 결과 + */ + public boolean isValidToken(String token) { + try { + return jwtTokenValidator.validateAccessToken(token); + } catch (Exception e) { + log.debug("[isValidToken] 토큰 검증 실패: {}", e.getMessage()); + return false; + } + } + + /** + * 요청에서 토큰을 추출하여 유효성 검증 + * + * @param request HTTP 요청 + * @return 유효성 검증 결과 + */ + public boolean isValidRequest(HttpServletRequest request) { + String token = TokenUtil.extractToken(request); + return token != null && isValidToken(token); + } + + //todo: 임시 메서드 생성 : pahse 2 에서 auth 에서 분리구현 예정 + public void deleteToken(HttpServletResponse response) {} + public void checkRefreshTokenAndReissue(HttpServletRequest request, HttpServletResponse response) {} + public void setAuthentication(HttpServletRequest servletRequest) {} +} \ No newline at end of file diff --git a/codin-security/src/main/java/inu/codin/security/util/SecurityUtil.java b/codin-security/src/main/java/inu/codin/security/util/SecurityUtil.java new file mode 100644 index 00000000..7dce19e3 --- /dev/null +++ b/codin-security/src/main/java/inu/codin/security/util/SecurityUtil.java @@ -0,0 +1,196 @@ +package inu.codin.security.util; + +import inu.codin.security.exception.SecurityException; +import inu.codin.security.exception.JwtException; +import inu.codin.security.exception.SecurityErrorCode; +import inu.codin.security.jwt.TokenUserDetails; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * SecurityContext와 관련된 유틸리티 클래스. + * + * 변경사항: + * - CustomUserDetails -> TokenUserDetails로 변경 (codin-security 독립성) + * - ObjectId -> String으로 변경 (MongoDB 의존성 제거) + * - UserRole enum 제거 -> String 사용 + * - 도메인 특화 검증 로직은 각 서비스에서 구현하도록 변경 + */ +@Slf4j +public class SecurityUtil { + + /** + * 현재 인증된 사용자의 ID를 반환. + * + * @return 인증된 사용자의 ID (String) + * @throws JwtException 인증 정보가 없는 경우 예외 발생 + */ + public static String getCurrentUserId() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + UserDetails userDetails = getCurrentUserDetails(); + + if (userDetails instanceof TokenUserDetails tokenUserDetails) { + return tokenUserDetails.getUserId(); + } + + // 호환성을 위해 username 반환 (다른 UserDetails 구현체) + return userDetails.getUsername(); + } + + /** + * 현재 인증된 사용자의 ID를 반환 (nullable 안전 버전) + * - 인증이 없거나 익명이면 null 반환 + */ + public static String getCurrentUserIdOrNull() { + try { + return getCurrentUserId(); + } catch (JwtException e) { + return null; + } + } + + /** + * 현재 인증된 사용자의 역할을 반환 + */ + public static String getCurrentUserRole() { + UserDetails userDetails = getCurrentUserDetails(); + + if (userDetails instanceof TokenUserDetails tokenUserDetails) { + return tokenUserDetails.getRole(); + } + + // 기본적으로 첫 번째 권한 반환 + return userDetails.getAuthorities().iterator().next().getAuthority(); + } + + /** + * 현재 인증된 사용자인지 검증 + */ + public static void validateUser(String userId) { + String currentUserId = getCurrentUserId(); + if (!userId.equals(currentUserId)) { + throw new JwtException(SecurityErrorCode.ACCESS_DENIED, "현재 유저에게 권한이 없습니다."); + } + } + + /** + * 현재 인증된 사용자가 리소스 소유자인지 검증 + */ + public static void validateOwners(String currentUserId, String ownerId) { + validateUser(currentUserId); + if (!ownerId.equals(currentUserId)) { + throw new JwtException(SecurityErrorCode.ACCESS_DENIED, "본인 리소스가 아닙니다."); + } + } + + /** + * 현재 UserDetails 반환 (내부 유틸리티) + */ + private static UserDetails getCurrentUserDetails() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication == null || !authentication.isAuthenticated() + || authentication instanceof AnonymousAuthenticationToken) { + throw new JwtException(SecurityErrorCode.ACCESS_DENIED); + } + + if (!(authentication.getPrincipal() instanceof UserDetails userDetails)) { + throw new JwtException(SecurityErrorCode.ACCESS_DENIED); + } + + return userDetails; + } + + /** + * Todo: + * package inu.codin.codinticketingapi.security.util; + * 추후 충돌해결 및 수정 필요 + **/ + + /** + * 현재 인증된 사용자의 유저 ID 반환 + */ + public static String getUserId() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { + throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); + } + + return userDetails.getUserId(); + } + + /** + * 현재 인증된 사용자의 이메일(유저이름) 반환 + */ + public static String getUsername() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { + throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); + } + + return userDetails.getUsername(); + } + + /** + * 현재 인증된 사용자의 유저 토큰 반환 + */ + public static String getUserToken() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { + throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); + } + + return userDetails.getToken(); + } + + /** + * 현재 인증된 사용자의 권한 반환 + */ +// public static String getCurrentUserRole() { +// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); +// +// if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { +// throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); +// } +// +// return userDetails.getRole(); +// } + + /** + * 현재 사용자와 주어진 사용자 ID가 같은지 검증 + */ +// public static void validateUser(String userId) { +// String currentUserId = getUserId(); +// if (!currentUserId.equals(userId)) { +// throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); +// } +// } + + /** + * 현재 사용자가 인증되어 있는지 확인 + */ + public static boolean isAuthenticated() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return authentication != null && + authentication.isAuthenticated() && + authentication.getPrincipal() instanceof TokenUserDetails; + } + + /** + * 현재 사용자가 특정 권한을 가지고 있는지 확인 + */ + public static boolean hasRole(String role) { + try { + String currentRole = getCurrentUserRole(); + return role.equals(currentRole); + } catch (SecurityException e) { + return false; + } + } +} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/util/TokenUtil.java b/codin-security/src/main/java/inu/codin/security/util/TokenUtil.java similarity index 96% rename from codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/util/TokenUtil.java rename to codin-security/src/main/java/inu/codin/security/util/TokenUtil.java index faf577dd..8ec28019 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/util/TokenUtil.java +++ b/codin-security/src/main/java/inu/codin/security/util/TokenUtil.java @@ -1,4 +1,4 @@ -package inu.codin.codinticketingapi.security.util; +package inu.codin.security.util; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; diff --git a/codin-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/codin-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..83222e83 --- /dev/null +++ b/codin-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +# Codin Security Auto Configuration +inu.codin.security.config.CodinSecurityAutoConfiguration \ No newline at end of file diff --git a/codin-ticketing-api/build.gradle b/codin-ticketing-api/build.gradle index 8a62100c..275e799f 100644 --- a/codin-ticketing-api/build.gradle +++ b/codin-ticketing-api/build.gradle @@ -34,6 +34,9 @@ dependencyManagement { } dependencies { + implementation project(':codin-security') + implementation project(':codin-common') + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/exception/GlobalErrorCode.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/exception/GlobalErrorCode.java deleted file mode 100644 index c227a567..00000000 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/exception/GlobalErrorCode.java +++ /dev/null @@ -1,8 +0,0 @@ -package inu.codin.codinticketingapi.common.exception; - -import org.springframework.http.HttpStatus; - -public interface GlobalErrorCode { - HttpStatus httpStatus(); - String message(); -} \ No newline at end of file diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/exception/GlobalException.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/exception/GlobalException.java deleted file mode 100644 index c3a8b1f0..00000000 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/exception/GlobalException.java +++ /dev/null @@ -1,14 +0,0 @@ -package inu.codin.codinticketingapi.common.exception; - -import lombok.Getter; - -@Getter -public class GlobalException extends RuntimeException{ - - private final GlobalErrorCode errorCode; - - public GlobalException(GlobalErrorCode errorCode) { - super(errorCode.message()); - this.errorCode = errorCode; - } -} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/exception/GlobalExceptionHandler.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/exception/GlobalExceptionHandler.java index 21a2bf71..09032e4a 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/exception/GlobalExceptionHandler.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/exception/GlobalExceptionHandler.java @@ -1,6 +1,8 @@ package inu.codin.codinticketingapi.common.exception; -import inu.codin.codinticketingapi.common.response.ExceptionResponse; +import inu.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalException; +import inu.codin.common.response.ExceptionResponse; import inu.codin.codinticketingapi.domain.ticketing.exception.TicketingErrorCode; import inu.codin.codinticketingapi.domain.ticketing.exception.TicketingException; import inu.codin.codinticketingapi.domain.user.exception.UserErrorCode; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/CommonResponse.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/CommonResponse.java deleted file mode 100644 index 4201dcb3..00000000 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/CommonResponse.java +++ /dev/null @@ -1,18 +0,0 @@ -package inu.codin.codinticketingapi.common.response; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; - -@Getter -@Schema -public class CommonResponse { - boolean success; - int code; - String message; - - public CommonResponse(boolean success, int code, String message) { - this.success = success; - this.code = code; - this.message = message; - } -} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/ExceptionResponse.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/ExceptionResponse.java deleted file mode 100644 index 1aada9b3..00000000 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/ExceptionResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package inu.codin.codinticketingapi.common.response; - -public class ExceptionResponse extends CommonResponse{ - public ExceptionResponse(String message, int code) { - super(false, code, message); - } -} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/ListResponse.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/ListResponse.java deleted file mode 100644 index 0038d2b1..00000000 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/ListResponse.java +++ /dev/null @@ -1,17 +0,0 @@ -package inu.codin.codinticketingapi.common.response; - -import lombok.Builder; -import lombok.Getter; - -import java.util.List; - -@Getter -public class ListResponse extends CommonResponse{ - List dataList; - - @Builder - public ListResponse(int code, String message, List dataList) { - super(true, code, message); - this.dataList = dataList; - } -} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/SingleResponse.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/SingleResponse.java deleted file mode 100644 index fad3c08f..00000000 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/response/SingleResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package inu.codin.codinticketingapi.common.response; - -import lombok.Builder; -import lombok.Getter; - -@Getter -public class SingleResponse extends CommonResponse { - T data; - - @Builder - public SingleResponse(int code, String message, T data) { - super(true, code, message); - this.data = data; - } -} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/util/MultipartJackson2HttpMessageConverter.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/util/MultipartJackson2HttpMessageConverter.java deleted file mode 100644 index 2ed2152f..00000000 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/common/util/MultipartJackson2HttpMessageConverter.java +++ /dev/null @@ -1,34 +0,0 @@ -package inu.codin.codinticketingapi.common.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Type; - -@Component -public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter { - - /** - * "Content-Type: multipart/form-data" 헤더를 지원하는 HTTP 요청 변환기 - */ - public MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) { - super(objectMapper, MediaType.APPLICATION_OCTET_STREAM); - } - - @Override - public boolean canWrite(Class clazz, MediaType mediaType) { - return false; - } - - @Override - public boolean canWrite(Type type, Class clazz, MediaType mediaType) { - return false; - } - - @Override - protected boolean canWrite(MediaType mediaType) { - return false; - } -} \ No newline at end of file diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/config/SecurityConfig.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/config/SecurityConfig.java index 6193549d..d377512f 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/config/SecurityConfig.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/config/SecurityConfig.java @@ -1,77 +1,78 @@ -package inu.codin.codinticketingapi.config; - -import inu.codin.codinticketingapi.security.exception.CustomAccessDeniedHandler; -import inu.codin.codinticketingapi.security.filter.SecurityExceptionHandlerFilter; -import inu.codin.codinticketingapi.security.filter.TokenValidationFilter; -import inu.codin.codinticketingapi.security.jwt.JwtTokenValidator; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; -import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - -import java.util.List; - -@Configuration -@EnableWebSecurity -@EnableMethodSecurity -@RequiredArgsConstructor -public class SecurityConfig { - private final JwtTokenValidator jwtTokenValidator; - private final CustomAccessDeniedHandler customAccessDeniedHandler; - - @Value("${server.domain}") - private String BASE_DOMAIN_URL; - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http, CorsConfigurationSource corsConfigurationSource) throws Exception { - return http - .cors(cors -> cors.configurationSource(corsConfigurationSource())) - .csrf(CsrfConfigurer::disable) - .formLogin(FormLoginConfigurer::disable) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests(auth -> auth - // Swagger 관련 경로 허용 - .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**").permitAll() - // 테스트 API 경로 - @PreAuthorize로 권한 제어 - .requestMatchers("/v3/api/test**").permitAll() - // 모든 요청은 인증 필요, 단 특정 경로는 예외 - .requestMatchers("/public/**").permitAll() // Public endpoints - .anyRequest().hasAnyRole("USER", "MANAGER", "ADMIN") - ) - .addFilterBefore( - new TokenValidationFilter(jwtTokenValidator), - UsernamePasswordAuthenticationFilter.class - ) - .addFilterBefore(new SecurityExceptionHandlerFilter(), TokenValidationFilter.class) - .exceptionHandling(exceptionHandling -> - exceptionHandling.accessDeniedHandler(customAccessDeniedHandler) - ) - .build(); - } - - @Bean - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration config = new CorsConfiguration(); - - config.setAllowCredentials(true); - config.setAllowedOrigins(List.of("http://localhost:3000", BASE_DOMAIN_URL, "https://front-end-dun-mu.vercel.app")); - config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); - config.setAllowedHeaders(List.of("*")); - config.setExposedHeaders(List.of("*")); - - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", config); - return source; - } -} +//package inu.codin.codinticketingapi.config; +// +//import inu.codin.security.exception.CustomAccessDeniedHandler; +//import inu.codin.codinticketingapi.security.filter.SecurityExceptionHandlerFilter; +//import inu.codin.security.filter.TokenValidationFilter; +// +//import inu.codin.security.jwt.JwtTokenValidator; +//import lombok.RequiredArgsConstructor; +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +//import org.springframework.security.config.annotation.web.builders.HttpSecurity; +//import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +//import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; +//import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; +//import org.springframework.security.config.http.SessionCreationPolicy; +//import org.springframework.security.web.SecurityFilterChain; +//import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +//import org.springframework.web.cors.CorsConfiguration; +//import org.springframework.web.cors.CorsConfigurationSource; +//import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +// +//import java.util.List; +// +//@Configuration +//@EnableWebSecurity +//@EnableMethodSecurity +//@RequiredArgsConstructor +//public class SecurityConfig { +// private final JwtTokenValidator jwtTokenValidator; +// private final CustomAccessDeniedHandler customAccessDeniedHandler; +// +// @Value("${server.domain}") +// private String BASE_DOMAIN_URL; +// +// @Bean +// public SecurityFilterChain filterChain(HttpSecurity http, CorsConfigurationSource corsConfigurationSource) throws Exception { +// return http +// .cors(cors -> cors.configurationSource(corsConfigurationSource())) +// .csrf(CsrfConfigurer::disable) +// .formLogin(FormLoginConfigurer::disable) +// .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) +// .authorizeHttpRequests(auth -> auth +// // Swagger 관련 경로 허용 +// .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**").permitAll() +// // 테스트 API 경로 - @PreAuthorize로 권한 제어 +// .requestMatchers("/v3/api/test**").permitAll() +// // 모든 요청은 인증 필요, 단 특정 경로는 예외 +// .requestMatchers("/public/**").permitAll() // Public endpoints +// .anyRequest().hasAnyRole("USER", "MANAGER", "ADMIN") +// ) +// .addFilterBefore( +// new TokenValidationFilter(jwtTokenValidator), +// UsernamePasswordAuthenticationFilter.class +// ) +// .addFilterBefore(new SecurityExceptionHandlerFilter(), TokenValidationFilter.class) +// .exceptionHandling(exceptionHandling -> +// exceptionHandling.accessDeniedHandler(customAccessDeniedHandler) +// ) +// .build(); +// } +// +// @Bean +// public CorsConfigurationSource corsConfigurationSource() { +// CorsConfiguration config = new CorsConfiguration(); +// +// config.setAllowCredentials(true); +// config.setAllowedOrigins(List.of("http://localhost:3000", BASE_DOMAIN_URL, "https://front-end-dun-mu.vercel.app")); +// config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); +// config.setAllowedHeaders(List.of("*")); +// config.setExposedHeaders(List.of("*")); +// +// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); +// source.registerCorsConfiguration("/**", config); +// return source; +// } +//} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/config/WebConfig.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/config/WebConfig.java index 4da7ca7d..b2dc7e6b 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/config/WebConfig.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/config/WebConfig.java @@ -1,7 +1,7 @@ package inu.codin.codinticketingapi.config; import inu.codin.codinticketingapi.common.converter.CampusConverter; -import inu.codin.codinticketingapi.common.util.MultipartJackson2HttpMessageConverter; +import inu.codin.common.util.MultipartJackson2HttpMessageConverter; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/controller/EventAdminControllerImpl.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/controller/EventAdminControllerImpl.java index 2c3fe107..afb1a31f 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/controller/EventAdminControllerImpl.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/controller/EventAdminControllerImpl.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.admin.controller; -import inu.codin.codinticketingapi.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codinticketingapi.domain.admin.controller.swagger.EventAdminController; import inu.codin.codinticketingapi.domain.admin.dto.request.EventCreateRequest; import inu.codin.codinticketingapi.domain.admin.dto.request.EventUpdateRequest; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/controller/swagger/EventAdminController.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/controller/swagger/EventAdminController.java index 03d7372f..830f9168 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/controller/swagger/EventAdminController.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/controller/swagger/EventAdminController.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.admin.controller.swagger; -import inu.codin.codinticketingapi.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codinticketingapi.domain.admin.dto.request.EventCreateRequest; import inu.codin.codinticketingapi.domain.admin.dto.request.EventUpdateRequest; import inu.codin.codinticketingapi.domain.admin.dto.response.EventParticipationProfilePageResponse; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/exception/ExcelErrorCode.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/exception/ExcelErrorCode.java index bf64ed8a..c47bb843 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/exception/ExcelErrorCode.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/exception/ExcelErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.admin.exception; -import inu.codin.codinticketingapi.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/exception/ExcelException.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/exception/ExcelException.java index 84214323..cd6d16dc 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/exception/ExcelException.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/exception/ExcelException.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.admin.exception; -import inu.codin.codinticketingapi.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/service/EventAdminService.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/service/EventAdminService.java index acecd67c..621e1f68 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/service/EventAdminService.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/admin/service/EventAdminService.java @@ -23,7 +23,8 @@ import inu.codin.codinticketingapi.domain.user.exception.UserErrorCode; import inu.codin.codinticketingapi.domain.user.exception.UserException; import inu.codin.codinticketingapi.domain.user.service.UserClientService; -import inu.codin.codinticketingapi.security.util.SecurityUtil; + +import inu.codin.security.util.SecurityUtil; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/image/exception/ImageErrorCode.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/image/exception/ImageErrorCode.java index 10216f9f..6d511d64 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/image/exception/ImageErrorCode.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/image/exception/ImageErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.image.exception; -import inu.codin.codinticketingapi.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/image/exception/ImageException.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/image/exception/ImageException.java index 342fbbd3..0527e697 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/image/exception/ImageException.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/image/exception/ImageException.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.image.exception; -import inu.codin.codinticketingapi.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/controller/EventController.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/controller/EventController.java index 0bc06d65..cd54d310 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/controller/EventController.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/controller/EventController.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.ticketing.controller; -import inu.codin.codinticketingapi.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codinticketingapi.domain.ticketing.dto.response.EventDetailResponse; import inu.codin.codinticketingapi.domain.ticketing.dto.response.EventPageResponse; import inu.codin.codinticketingapi.domain.ticketing.dto.response.EventParticipationHistoryPageResponse; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/controller/TicketingController.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/controller/TicketingController.java index 41982d7a..dacadd12 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/controller/TicketingController.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/controller/TicketingController.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.ticketing.controller; -import inu.codin.codinticketingapi.common.response.SingleResponse; +import inu.codin.common.response.SingleResponse; import inu.codin.codinticketingapi.domain.ticketing.dto.response.ParticipationResponse; import inu.codin.codinticketingapi.domain.ticketing.service.ParticipationService; import inu.codin.codinticketingapi.domain.ticketing.service.TicketingService; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/exception/TicketingErrorCode.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/exception/TicketingErrorCode.java index 6e4d7151..91a622e8 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/exception/TicketingErrorCode.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/exception/TicketingErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.ticketing.exception; -import inu.codin.codinticketingapi.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/exception/TicketingException.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/exception/TicketingException.java index 863527bf..2e43c6dc 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/exception/TicketingException.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/exception/TicketingException.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.ticketing.exception; -import inu.codin.codinticketingapi.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/service/ParticipationService.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/service/ParticipationService.java index 9a6e45f9..71cc8ba8 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/service/ParticipationService.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/ticketing/service/ParticipationService.java @@ -15,7 +15,7 @@ import inu.codin.codinticketingapi.domain.user.exception.UserErrorCode; import inu.codin.codinticketingapi.domain.user.exception.UserException; import inu.codin.codinticketingapi.domain.user.service.UserClientService; -import inu.codin.codinticketingapi.security.util.SecurityUtil; +import inu.codin.security.util.SecurityUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/config/FeignClientConfig.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/config/FeignClientConfig.java index 17539b5a..8ddcd49a 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/config/FeignClientConfig.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/config/FeignClientConfig.java @@ -1,7 +1,7 @@ package inu.codin.codinticketingapi.domain.user.config; import feign.RequestInterceptor; -import inu.codin.codinticketingapi.security.util.SecurityUtil; +import inu.codin.security.util.SecurityUtil; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/exception/UserErrorCode.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/exception/UserErrorCode.java index 6c66fa2c..e99a7740 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/exception/UserErrorCode.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/exception/UserErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.user.exception; -import inu.codin.codinticketingapi.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/exception/UserException.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/exception/UserException.java index db9db664..6b582f90 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/exception/UserException.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/domain/user/exception/UserException.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingapi.domain.user.exception; -import inu.codin.codinticketingapi.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/exception/SecurityErrorCode.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/exception/SecurityErrorCode.java deleted file mode 100644 index 6fdcda0b..00000000 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/exception/SecurityErrorCode.java +++ /dev/null @@ -1,27 +0,0 @@ -package inu.codin.codinticketingapi.security.exception; - -import inu.codin.codinticketingapi.common.exception.GlobalErrorCode; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; - -@RequiredArgsConstructor -public enum SecurityErrorCode implements GlobalErrorCode { - - INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."), - EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 토큰입니다."), - TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, "토큰이 없습니다."), - ACCESS_DENIED(HttpStatus.FORBIDDEN,"접근 권한이 없습니다."); - - private final HttpStatus httpStatus; - private final String message; - - @Override - public HttpStatus httpStatus() { - return httpStatus; - } - - @Override - public String message() { - return message; - } -} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/exception/SecurityException.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/exception/SecurityException.java deleted file mode 100644 index f4ab70ef..00000000 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/exception/SecurityException.java +++ /dev/null @@ -1,14 +0,0 @@ -package inu.codin.codinticketingapi.security.exception; - -import inu.codin.codinticketingapi.common.exception.GlobalException; -import lombok.Getter; - -@Getter -public class SecurityException extends GlobalException { - private final SecurityErrorCode securityErrorCode; - - public SecurityException(SecurityErrorCode errorCode) { - super(errorCode); - this.securityErrorCode = errorCode; - } -} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/filter/SecurityExceptionHandlerFilter.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/filter/SecurityExceptionHandlerFilter.java index 277950bb..bc58be31 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/filter/SecurityExceptionHandlerFilter.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/filter/SecurityExceptionHandlerFilter.java @@ -1,53 +1,53 @@ -package inu.codin.codinticketingapi.security.filter; - -import com.fasterxml.jackson.databind.ObjectMapper; -import inu.codin.codinticketingapi.common.response.ExceptionResponse; -import inu.codin.codinticketingapi.security.exception.SecurityException; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.MediaType; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -/** - * SecurityFilterChain에서 발생하는 예외를 처리하는 필터 - */ -@Slf4j -public class SecurityExceptionHandlerFilter extends OncePerRequestFilter { - - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - try { - filterChain.doFilter(request, response); - } catch (SecurityException e) { - log.error("[ExceptionHandlerFilter] SecurityException 발생: {}", e.getMessage()); - setErrorResponse(response, e); - } catch (Exception e) { - log.error("[ExceptionHandlerFilter] 예상치 못한 예외 발생: {}", e.getMessage(), e); - // SecurityException이 아닌 다른 예외는 기존 처리 방식으로 전달 - throw e; - } - } - - /** - * SecurityException을 ExceptionResponse로 변환하여 응답 - */ - private void setErrorResponse(HttpServletResponse response, SecurityException e) throws IOException { - response.setStatus(e.getSecurityErrorCode().httpStatus().value()); - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - response.setCharacterEncoding(StandardCharsets.UTF_8.name()); - - ExceptionResponse exceptionResponse = new ExceptionResponse(e.getSecurityErrorCode().message(), e.getSecurityErrorCode().httpStatus().value()); - - String jsonResponse = objectMapper.writeValueAsString(exceptionResponse); - response.getWriter().write(jsonResponse); - } -} +//package inu.codin.codinticketingapi.security.filter; +// +//import com.fasterxml.jackson.databind.ObjectMapper; +//import inu.codin.codinticketingapi.common.response.ExceptionResponse; +//import inu.codin.codinticketingapi.security.exception.SecurityException; +//import jakarta.servlet.FilterChain; +//import jakarta.servlet.ServletException; +//import jakarta.servlet.http.HttpServletRequest; +//import jakarta.servlet.http.HttpServletResponse; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.http.MediaType; +//import org.springframework.web.filter.OncePerRequestFilter; +// +//import java.io.IOException; +//import java.nio.charset.StandardCharsets; +// +///** +// * SecurityFilterChain에서 발생하는 예외를 처리하는 필터 +//// */ +//@Slf4j +//public class SecurityExceptionHandlerFilter extends OncePerRequestFilter { +// +// private final ObjectMapper objectMapper = new ObjectMapper(); +// +// @Override +// protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) +// throws ServletException, IOException { +// try { +// filterChain.doFilter(request, response); +// } catch (SecurityException e) { +// log.error("[ExceptionHandlerFilter] SecurityException 발생: {}", e.getMessage()); +// setErrorResponse(response, e); +// } catch (Exception e) { +// log.error("[ExceptionHandlerFilter] 예상치 못한 예외 발생: {}", e.getMessage(), e); +// // SecurityException이 아닌 다른 예외는 기존 처리 방식으로 전달 +// throw e; +// } +// } +// +// /** +// * SecurityException을 ExceptionResponse로 변환하여 응답 +// */ +// private void setErrorResponse(HttpServletResponse response, SecurityException e) throws IOException { +// response.setStatus(e.getSecurityErrorCode().httpStatus().value()); +// response.setContentType(MediaType.APPLICATION_JSON_VALUE); +// response.setCharacterEncoding(StandardCharsets.UTF_8.name()); +// +// ExceptionResponse exceptionResponse = new ExceptionResponse(e.getSecurityErrorCode().message(), e.getSecurityErrorCode().httpStatus().value()); +// +// String jsonResponse = objectMapper.writeValueAsString(exceptionResponse); +// response.getWriter().write(jsonResponse); +// } +//} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/jwt/TokenUserDetails.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/jwt/TokenUserDetails.java deleted file mode 100644 index 2e42269f..00000000 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/jwt/TokenUserDetails.java +++ /dev/null @@ -1,78 +0,0 @@ -package inu.codin.codinticketingapi.security.jwt; - -import lombok.Builder; -import lombok.Getter; -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; - -/** - * 토큰 검증 전용 UserDetails - DB 조회 없이 JWT 토큰에서 파싱한 정보만 담음 - */ -@Getter -public class TokenUserDetails implements UserDetails { - - private final String userId; - private final String email; - private final String role; - private final String token; - private final Collection authorities; - - @Builder - public TokenUserDetails(String userId, String email, String role, String token) { - this.userId = userId; - this.email = email; - this.role = role; - this.token = token; - // JWT에서 이미 ROLE_ 접두사가 있는 경우 그대로 사용, 없는 경우 추가 - String authority = role.startsWith("ROLE_") ? role : "ROLE_" + role; - this.authorities = Collections.singletonList(new SimpleGrantedAuthority(authority)); - } - - public static TokenUserDetails fromTokenClaims(String userId, String email, String role, String token) { - return TokenUserDetails.builder() - .userId(userId) - .email(email) - .role(role) - .token(token) - .build(); - } - - @Override - public Collection getAuthorities() { - return authorities; - } - - @Override - public String getPassword() { - return null; - } - - @Override - public String getUsername() { - return email; - } - - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return true; - } - - @Override - public boolean isCredentialsNonExpired() { - return true; - } - - @Override - public boolean isEnabled() { - return true; - } -} diff --git a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/util/SecurityUtil.java b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/util/SecurityUtil.java index be3b1a2a..00c4fee5 100644 --- a/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/util/SecurityUtil.java +++ b/codin-ticketing-api/src/main/java/inu/codin/codinticketingapi/security/util/SecurityUtil.java @@ -1,98 +1,97 @@ -package inu.codin.codinticketingapi.security.util; - -import inu.codin.codinticketingapi.security.exception.SecurityErrorCode; -import inu.codin.codinticketingapi.security.exception.SecurityException; -import inu.codin.codinticketingapi.security.jwt.TokenUserDetails; -import io.jsonwebtoken.JwtException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; - -/** - * SecurityContext 관련 유틸리티 (토큰 검증 전용) - */ -public class SecurityUtil { - - /** - * 현재 인증된 사용자의 유저 ID 반환 - */ - public static String getUserId() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { - throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); - } - - return userDetails.getUserId(); - } - - /** - * 현재 인증된 사용자의 이메일(유저이름) 반환 - */ - public static String getUsername() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { - throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); - } - - return userDetails.getUsername(); - } - - /** - * 현재 인증된 사용자의 유저 토큰 반환 - */ - public static String getUserToken() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { - throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); - } - - return userDetails.getToken(); - } - - /** - * 현재 인증된 사용자의 권한 반환 - */ - public static String getCurrentUserRole() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { - throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); - } - - return userDetails.getRole(); - } - - /** - * 현재 사용자와 주어진 사용자 ID가 같은지 검증 - */ - public static void validateUser(String userId) { - String currentUserId = getUserId(); - if (!currentUserId.equals(userId)) { - throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); - } - } - - /** - * 현재 사용자가 인증되어 있는지 확인 - */ - public static boolean isAuthenticated() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - return authentication != null && - authentication.isAuthenticated() && - authentication.getPrincipal() instanceof TokenUserDetails; - } - - /** - * 현재 사용자가 특정 권한을 가지고 있는지 확인 - */ - public static boolean hasRole(String role) { - try { - String currentRole = getCurrentUserRole(); - return role.equals(currentRole); - } catch (SecurityException e) { - return false; - } - } -} \ No newline at end of file +//package inu.codin.codinticketingapi.security.util; +// +//import inu.codin.codinticketingapi.security.exception.SecurityErrorCode; +//import inu.codin.codinticketingapi.security.exception.SecurityException; +//import inu.codin.security.jwt.TokenUserDetails; +//import org.springframework.security.core.Authentication; +//import org.springframework.security.core.context.SecurityContextHolder; +// +///** +// * SecurityContext 관련 유틸리티 (토큰 검증 전용) +// */ +//public class SecurityUtil { +// +// /** +// * 현재 인증된 사용자의 유저 ID 반환 +// */ +// public static String getUserId() { +// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); +// +// if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { +// throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); +// } +// +// return userDetails.getUserId(); +// } +// +// /** +// * 현재 인증된 사용자의 이메일(유저이름) 반환 +// */ +// public static String getUsername() { +// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); +// +// if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { +// throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); +// } +// +// return userDetails.getUsername(); +// } +// +// /** +// * 현재 인증된 사용자의 유저 토큰 반환 +// */ +// public static String getUserToken() { +// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); +// +// if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { +// throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); +// } +// +// return userDetails.getToken(); +// } +// +// /** +// * 현재 인증된 사용자의 권한 반환 +// */ +// public static String getCurrentUserRole() { +// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); +// +// if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { +// throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); +// } +// +// return userDetails.getRole(); +// } +// +// /** +// * 현재 사용자와 주어진 사용자 ID가 같은지 검증 +// */ +// public static void validateUser(String userId) { +// String currentUserId = getUserId(); +// if (!currentUserId.equals(userId)) { +// throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); +// } +// } +// +// /** +// * 현재 사용자가 인증되어 있는지 확인 +// */ +// public static boolean isAuthenticated() { +// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); +// return authentication != null && +// authentication.isAuthenticated() && +// authentication.getPrincipal() instanceof TokenUserDetails; +// } +// +// /** +// * 현재 사용자가 특정 권한을 가지고 있는지 확인 +// */ +// public static boolean hasRole(String role) { +// try { +// String currentRole = getCurrentUserRole(); +// return role.equals(currentRole); +// } catch (SecurityException e) { +// return false; +// } +// } +//} \ No newline at end of file diff --git a/codin-ticketing-api/src/main/resources/application-local.yml b/codin-ticketing-api/src/main/resources/application-local.yml new file mode 100644 index 00000000..a5ae1c52 --- /dev/null +++ b/codin-ticketing-api/src/main/resources/application-local.yml @@ -0,0 +1,20 @@ +# 이름 : application-local.yml + +security: + permit-all: + urls: + - "/auth/**" + - "/v3/api/test1" + - "/ws-stomp/**" + - "/suspends" + - "/login/oauth2/code/**" + # --- Swagger / Springdoc --- + - "/swagger-ui/**" + - "/v3/api-docs/**" + - "/swagger-resources/**" + - "/webjars/**" + # --- 정적/루트/헬스체크 --- + - "/" + - "/favicon.ico" + - "/.well-known/**" + - "/actuator/health" diff --git a/codin-ticketing-api/src/main/resources/application-prod.yml b/codin-ticketing-api/src/main/resources/application-prod.yml new file mode 100644 index 00000000..61d8f33a --- /dev/null +++ b/codin-ticketing-api/src/main/resources/application-prod.yml @@ -0,0 +1,6 @@ +# 이름 : application-prod.yml + +security: + #공개 API + public-api: + urls: \ No newline at end of file diff --git a/codin-ticketing-api/src/main/resources/application.yml b/codin-ticketing-api/src/main/resources/application.yml index 410a992c..f46c8320 100644 --- a/codin-ticketing-api/src/main/resources/application.yml +++ b/codin-ticketing-api/src/main/resources/application.yml @@ -3,6 +3,9 @@ spring: name: codin-ticketing-api config: import: optional:file:./.env[.properties], optional:file:./.env.local[.properties] + profiles: + active: + local jwt: secret: ${SPRING_JWT_SECRET} diff --git a/codin-ticketing-sse/build.gradle b/codin-ticketing-sse/build.gradle index c13579ff..638c5fde 100644 --- a/codin-ticketing-sse/build.gradle +++ b/codin-ticketing-sse/build.gradle @@ -18,6 +18,9 @@ repositories { } dependencies { + implementation project(':codin-security') + implementation project(':codin-common') + implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/exception/GlobalErrorCode.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/exception/GlobalErrorCode.java deleted file mode 100644 index 3f0df833..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/exception/GlobalErrorCode.java +++ /dev/null @@ -1,8 +0,0 @@ -package inu.codin.codinticketingsse.common.exception; - -import org.springframework.http.HttpStatus; - -public interface GlobalErrorCode { - HttpStatus httpStatus(); - String message(); -} \ No newline at end of file diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/exception/GlobalException.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/exception/GlobalException.java deleted file mode 100644 index 4b8cdd03..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/exception/GlobalException.java +++ /dev/null @@ -1,15 +0,0 @@ -package inu.codin.codinticketingsse.common.exception; - -public class GlobalException extends RuntimeException{ - - private final GlobalErrorCode errorCode; - - public GlobalException(GlobalErrorCode errorCode) { - super(errorCode.message()); - this.errorCode = errorCode; - } - - public GlobalErrorCode getErrorCode() { - return errorCode; - } -} diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/exception/GlobalExceptionHandler.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/exception/GlobalExceptionHandler.java index 1f8a4b33..66cb6c0e 100644 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/exception/GlobalExceptionHandler.java +++ b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/exception/GlobalExceptionHandler.java @@ -1,8 +1,10 @@ package inu.codin.codinticketingsse.common.exception; -import inu.codin.codinticketingsse.common.response.ExceptionResponse; -import inu.codin.codinticketingsse.security.exception.SecurityErrorCode; -import inu.codin.codinticketingsse.security.exception.SecurityException; +import inu.codin.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalException; +import inu.codin.common.response.ExceptionResponse; +import inu.codin.security.exception.SecurityErrorCode; +import inu.codin.security.exception.SecurityException; import inu.codin.codinticketingsse.sse.exception.SseErrorCode; import inu.codin.codinticketingsse.sse.exception.SseException; import jakarta.validation.ConstraintViolationException; @@ -50,7 +52,7 @@ public ResponseEntity handleSseException(SseException e) { @ExceptionHandler(SecurityException.class) public ResponseEntity handleSecurityException(SecurityException e) { log.warn("[SecurityException] Class: {}, Error Message : {}", e.getClass().getSimpleName(), e.getMessage()); - SecurityErrorCode code = e.getSecurityErrorCode(); + SecurityErrorCode code = e.getErrorCode(); return ResponseEntity.status(code.httpStatus()) .body(new ExceptionResponse(e.getMessage(), code.httpStatus().value())); } diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/response/CommonResponse.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/response/CommonResponse.java deleted file mode 100644 index 2ae7b7c8..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/response/CommonResponse.java +++ /dev/null @@ -1,18 +0,0 @@ -package inu.codin.codinticketingsse.common.response; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; - -@Getter -@Schema -public class CommonResponse { - boolean success; - int code; - String message; - - public CommonResponse(boolean success, int code, String message) { - this.success = success; - this.code = code; - this.message = message; - } -} diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/response/ExceptionResponse.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/response/ExceptionResponse.java deleted file mode 100644 index a7449a7b..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/common/response/ExceptionResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package inu.codin.codinticketingsse.common.response; - -public class ExceptionResponse extends CommonResponse { - public ExceptionResponse(String message, int code) { - super(false, code, message); - } -} diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/config/SecurityConfig.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/config/SecurityConfig.java index a0e34e64..dda758d2 100644 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/config/SecurityConfig.java +++ b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/config/SecurityConfig.java @@ -1,78 +1,78 @@ -package inu.codin.codinticketingsse.config; - -import inu.codin.codinticketingsse.security.exception.CustomAccessDeniedHandler; -import inu.codin.codinticketingsse.security.filter.SecurityExceptionHandlerFilter; -import inu.codin.codinticketingsse.security.filter.TokenValidationFilter; -import inu.codin.codinticketingsse.security.jwt.JwtTokenValidator; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; -import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - -import java.util.List; - -@Configuration -@EnableWebSecurity -@EnableMethodSecurity(prePostEnabled = true) -@RequiredArgsConstructor -public class SecurityConfig { - private final JwtTokenValidator jwtTokenValidator; - private final CustomAccessDeniedHandler customAccessDeniedHandler; - - @Value("${server.domain}") - private String BASE_DOMAIN_URL; - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http, CorsConfigurationSource corsConfigurationSource) throws Exception { - return http - .cors(cors -> cors.configurationSource(corsConfigurationSource())) - .csrf(CsrfConfigurer::disable) - .formLogin(FormLoginConfigurer::disable) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests(auth -> auth - // Swagger 관련 경로 허용 - .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**").permitAll() - // 테스트 API 경로 - @PreAuthorize로 권한 제어 - .requestMatchers("/v3/api/test**").permitAll() - // Sse 구독 엔드포인트 허용 (없으면 AccessDenied 오류 생김) - .requestMatchers("/subscribe/**").permitAll() - // 모든 요청은 인증 필요, 단 특정 경로는 예외 - .requestMatchers("/public/**").permitAll() // Public endpoints - .anyRequest().hasAnyRole("USER", "MANAGER", "ADMIN") - ) - .addFilterBefore( - new TokenValidationFilter(jwtTokenValidator), UsernamePasswordAuthenticationFilter.class - ) - .addFilterBefore(new SecurityExceptionHandlerFilter(), TokenValidationFilter.class) - .exceptionHandling(exceptionHandling -> - exceptionHandling.accessDeniedHandler(customAccessDeniedHandler) - ) - .build(); - } - - @Bean - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration config = new CorsConfiguration(); - - config.setAllowCredentials(true); - config.setAllowedOrigins(List.of("http://localhost:3000", BASE_DOMAIN_URL, "https://front-end-dun-mu.vercel.app")); - config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); - config.setAllowedHeaders(List.of("*")); - config.setExposedHeaders(List.of("*")); - - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", config); - return source; - } -} +//package inu.codin.codinticketingsse.config; +// +//import inu.codin.codinticketingsse.security.exception.CustomAccessDeniedHandler; +//import inu.codin.codinticketingsse.security.filter.SecurityExceptionHandlerFilter; +//import inu.codin.codinticketingsse.security.filter.TokenValidationFilter; +//import inu.codin.codinticketingsse.security.jwt.JwtTokenValidator; +//import lombok.RequiredArgsConstructor; +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +//import org.springframework.security.config.annotation.web.builders.HttpSecurity; +//import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +//import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; +//import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; +//import org.springframework.security.config.http.SessionCreationPolicy; +//import org.springframework.security.web.SecurityFilterChain; +//import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +//import org.springframework.web.cors.CorsConfiguration; +//import org.springframework.web.cors.CorsConfigurationSource; +//import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +// +//import java.util.List; +// +//@Configuration +//@EnableWebSecurity +//@EnableMethodSecurity(prePostEnabled = true) +//@RequiredArgsConstructor +//public class SecurityConfig { +// private final JwtTokenValidator jwtTokenValidator; +// private final CustomAccessDeniedHandler customAccessDeniedHandler; +// +// @Value("${server.domain}") +// private String BASE_DOMAIN_URL; +// +// @Bean +// public SecurityFilterChain filterChain(HttpSecurity http, CorsConfigurationSource corsConfigurationSource) throws Exception { +// return http +// .cors(cors -> cors.configurationSource(corsConfigurationSource())) +// .csrf(CsrfConfigurer::disable) +// .formLogin(FormLoginConfigurer::disable) +// .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) +// .authorizeHttpRequests(auth -> auth +// // Swagger 관련 경로 허용 +// .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**").permitAll() +// // 테스트 API 경로 - @PreAuthorize로 권한 제어 +// .requestMatchers("/v3/api/test**").permitAll() +// // Sse 구독 엔드포인트 허용 (없으면 AccessDenied 오류 생김) +// .requestMatchers("/subscribe/**").permitAll() +// // 모든 요청은 인증 필요, 단 특정 경로는 예외 +// .requestMatchers("/public/**").permitAll() // Public endpoints +// .anyRequest().hasAnyRole("USER", "MANAGER", "ADMIN") +// ) +// .addFilterBefore( +// new TokenValidationFilter(jwtTokenValidator), UsernamePasswordAuthenticationFilter.class +// ) +// .addFilterBefore(new SecurityExceptionHandlerFilter(), TokenValidationFilter.class) +// .exceptionHandling(exceptionHandling -> +// exceptionHandling.accessDeniedHandler(customAccessDeniedHandler) +// ) +// .build(); +// } +// +// @Bean +// public CorsConfigurationSource corsConfigurationSource() { +// CorsConfiguration config = new CorsConfiguration(); +// +// config.setAllowCredentials(true); +// config.setAllowedOrigins(List.of("http://localhost:3000", BASE_DOMAIN_URL, "https://front-end-dun-mu.vercel.app")); +// config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); +// config.setAllowedHeaders(List.of("*")); +// config.setExposedHeaders(List.of("*")); +// +// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); +// source.registerCorsConfiguration("/**", config); +// return source; +// } +//} diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/exception/CustomAccessDeniedHandler.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/exception/CustomAccessDeniedHandler.java deleted file mode 100644 index b3623e65..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/exception/CustomAccessDeniedHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -package inu.codin.codinticketingsse.security.exception; - -import com.fasterxml.jackson.databind.ObjectMapper; -import inu.codin.codinticketingsse.common.response.ExceptionResponse; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.http.MediaType; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.stereotype.Component; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -@Component -public class CustomAccessDeniedHandler implements AccessDeniedHandler { - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { - SecurityErrorCode errorCode = SecurityErrorCode.ACCESS_DENIED; - - response.setStatus(errorCode.httpStatus().value()); - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - response.setCharacterEncoding(StandardCharsets.UTF_8.name()); - - ExceptionResponse exceptionResponse = new ExceptionResponse(errorCode.message(), errorCode.httpStatus().value()); - - String jsonResponse = objectMapper.writeValueAsString(exceptionResponse); - response.getWriter().write(jsonResponse); - } -} diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/exception/SecurityErrorCode.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/exception/SecurityErrorCode.java deleted file mode 100644 index 089eea48..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/exception/SecurityErrorCode.java +++ /dev/null @@ -1,27 +0,0 @@ -package inu.codin.codinticketingsse.security.exception; - -import inu.codin.codinticketingsse.common.exception.GlobalErrorCode; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; - -@RequiredArgsConstructor -public enum SecurityErrorCode implements GlobalErrorCode { - - INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."), - EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 토큰입니다."), - TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, "토큰이 없습니다."), - ACCESS_DENIED(HttpStatus.FORBIDDEN,"접근 권한이 없습니다."); - - private final HttpStatus httpStatus; - private final String message; - - @Override - public HttpStatus httpStatus() { - return httpStatus; - } - - @Override - public String message() { - return message; - } -} diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/exception/SecurityException.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/exception/SecurityException.java deleted file mode 100644 index 1ad3c786..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/exception/SecurityException.java +++ /dev/null @@ -1,16 +0,0 @@ -package inu.codin.codinticketingsse.security.exception; - - -import inu.codin.codinticketingsse.common.exception.GlobalException; -import lombok.Getter; - -@Getter -public class SecurityException extends GlobalException { - - private final SecurityErrorCode securityErrorCode; - - public SecurityException(SecurityErrorCode errorCode) { - super(errorCode); - this.securityErrorCode = errorCode; - } -} diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/filter/SecurityExceptionHandlerFilter.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/filter/SecurityExceptionHandlerFilter.java deleted file mode 100644 index cbcae53d..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/filter/SecurityExceptionHandlerFilter.java +++ /dev/null @@ -1,53 +0,0 @@ -package inu.codin.codinticketingsse.security.filter; - -import com.fasterxml.jackson.databind.ObjectMapper; -import inu.codin.codinticketingsse.security.exception.SecurityException; -import inu.codin.codinticketingsse.common.response.ExceptionResponse; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.MediaType; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -/** - * SecurityFilterChain에서 발생하는 예외를 처리하는 필터 - */ -@Slf4j -public class SecurityExceptionHandlerFilter extends OncePerRequestFilter { - - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - try { - filterChain.doFilter(request, response); - } catch (SecurityException e) { - log.error("[ExceptionHandlerFilter] SecurityException 발생: {}", e.getMessage()); - setErrorResponse(response, e); - } catch (Exception e) { - log.error("[ExceptionHandlerFilter] 예상치 못한 예외 발생: {}", e.getMessage(), e); - // SecurityException이 아닌 다른 예외는 기존 처리 방식으로 전달 - throw e; - } - } - - /** - * SecurityException을 ExceptionResponse로 변환하여 응답 - */ - private void setErrorResponse(HttpServletResponse response, SecurityException e) throws IOException { - response.setStatus(e.getSecurityErrorCode().httpStatus().value()); - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - response.setCharacterEncoding(StandardCharsets.UTF_8.name()); - - ExceptionResponse exceptionResponse = new ExceptionResponse(e.getSecurityErrorCode().message(), e.getSecurityErrorCode().httpStatus().value()); - - String jsonResponse = objectMapper.writeValueAsString(exceptionResponse); - response.getWriter().write(jsonResponse); - } -} diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/filter/TokenValidationFilter.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/filter/TokenValidationFilter.java deleted file mode 100644 index 62842ebb..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/filter/TokenValidationFilter.java +++ /dev/null @@ -1,72 +0,0 @@ -package inu.codin.codinticketingsse.security.filter; - - -import inu.codin.codinticketingsse.security.jwt.JwtTokenValidator; -import inu.codin.codinticketingsse.security.jwt.TokenUserDetails; -import inu.codin.codinticketingsse.security.util.TokenUtil; -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.context.SecurityContextHolder; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; - -/** - * 토큰 검증 전용 필터 - */ -@RequiredArgsConstructor -@Slf4j -public class TokenValidationFilter extends OncePerRequestFilter { - private final JwtTokenValidator jwtTokenValidator; - - private final String [] SWAGGER_AUTH_PATHS = { - "/swagger-ui/**", - "/v3/api-docs/**", - "/v3/api-docs", - "/swagger-resources/**" - }; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - String accessToken = TokenUtil.extractToken(request); - - if (accessToken != null && jwtTokenValidator.validateAccessToken(accessToken)) { - log.debug("[TokenValidationFilter] Access Token이 유효함"); - // Access Token이 있고 유효한 경우 - setAuthentication(accessToken); - } else { - log.debug("[TokenValidationFilter] Access Token이 유효하지 않음"); - SecurityContextHolder.clearContext(); - } - - filterChain.doFilter(request, response); - } - - /** - * 토큰에서 인증 정보 생성 후 SecurityContext에 설정 - */ - private void setAuthentication(String token) { - try { - String userId = jwtTokenValidator.getUserId(token); - String username = jwtTokenValidator.getUsername(token); - String role = jwtTokenValidator.getUserRole(token); - log.debug("[setAuthentication] : {}, {}", userId, role); - - TokenUserDetails userDetails = TokenUserDetails.fromTokenClaims(userId, username, role, token); - - UsernamePasswordAuthenticationToken authentication = - new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); - - SecurityContextHolder.getContext().setAuthentication(authentication); - log.info("[TokenValidationFilter] Authentication 설정 완료: userId={}, username={}, role={}", userId, username, role); - } catch (Exception e) { - log.error("[TokenValidationFilter] 인증 설정 실패: {}", e.getMessage()); - SecurityContextHolder.clearContext(); - } - } -} diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/jwt/JwtTokenValidator.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/jwt/JwtTokenValidator.java deleted file mode 100644 index eb00518f..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/jwt/JwtTokenValidator.java +++ /dev/null @@ -1,82 +0,0 @@ -package inu.codin.codinticketingsse.security.jwt; - -import inu.codin.codinticketingsse.security.exception.SecurityErrorCode; -import inu.codin.codinticketingsse.security.exception.SecurityException; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; -import jakarta.annotation.PostConstruct; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import java.security.Key; - -@Component -@Slf4j -public class JwtTokenValidator { - - @Value("${spring.jwt.secret}") - private String secret; - - private Key SECRET_KEY; - - @PostConstruct - protected void init() { - log.info("[JwtTokenValidator] JWT Secret key initialized, key: {}", secret.substring(0, 10)); - SECRET_KEY = Keys.hmacShaKeyFor(secret.getBytes()); - } - - /** - * 토큰 유효성 검사 (토큰 변조, 만료) - * @param accessToken - * @return true: 유효한 토큰 - * @throws SecurityException: 토큰 만료, 유효하지 않은 토큰 - */ - public boolean validateAccessToken(String accessToken) { - try { - Jwts.parserBuilder() - .setSigningKey(SECRET_KEY) - .setAllowedClockSkewSeconds(60) - .build() - .parseClaimsJws(accessToken); - return true; - } catch (ExpiredJwtException e) { // 토큰 만료 - log.error("[validateAccessToken] 토큰 만료 : {}", e.getMessage()); - throw new SecurityException(SecurityErrorCode.EXPIRED_TOKEN); - } catch (Exception e) { // 토큰 변조 - log.error("[validateAccessToken] 유효하지 않은 토큰 : {}", e.getMessage()); - throw new SecurityException(SecurityErrorCode.INVALID_TOKEN); - } - } - - /** - * 토큰에서 사용자 ID 추출 - */ - public String getUserId(String token) { - return getClaims(token).get("userId", String.class); - } - - /** - * 토큰에서 사용자 이메일(유저네임) 추출 - */ - public String getUsername(String token) { - return getClaims(token).getSubject(); - } - - /** - * 토큰에서 사용자 권한 추출 - */ - public String getUserRole(String token) { - return getClaims(token).get("auth", String.class); - } - - private Claims getClaims(String token) { - return Jwts.parserBuilder() - .setSigningKey(SECRET_KEY) - .build() - .parseClaimsJws(token) - .getBody(); - } -} \ No newline at end of file diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/util/SecurityUtil.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/util/SecurityUtil.java deleted file mode 100644 index e6bcd853..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/util/SecurityUtil.java +++ /dev/null @@ -1,98 +0,0 @@ -package inu.codin.codinticketingsse.security.util; - -import inu.codin.codinticketingsse.security.exception.SecurityException; -import inu.codin.codinticketingsse.security.exception.SecurityErrorCode; -import inu.codin.codinticketingsse.security.jwt.TokenUserDetails; -import io.jsonwebtoken.JwtException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; - -/** - * SecurityContext 관련 유틸리티 (토큰 검증 전용) - */ -public class SecurityUtil { - - /** - * 현재 인증된 사용자의 유저 ID 반환 - */ - public static String getUserId() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { - throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); - } - - return userDetails.getUserId(); - } - - /** - * 현재 인증된 사용자의 이메일(유저이름) 반환 - */ - public static String getUsername() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { - throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); - } - - return userDetails.getUsername(); - } - - /** - * 현재 인증된 사용자의 유저 토큰 반환 - */ - public static String getUserToken() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { - throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); - } - - return userDetails.getToken(); - } - - /** - * 현재 인증된 사용자의 권한 반환 - */ - public static String getCurrentUserRole() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if (authentication == null || !(authentication.getPrincipal() instanceof TokenUserDetails userDetails)) { - throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); - } - - return userDetails.getRole(); - } - - /** - * 현재 사용자와 주어진 사용자 ID가 같은지 검증 - */ - public static void validateUser(String userId) { - String currentUserId = getUserId(); - if (!currentUserId.equals(userId)) { - throw new SecurityException(SecurityErrorCode.ACCESS_DENIED); - } - } - - /** - * 현재 사용자가 인증되어 있는지 확인 - */ - public static boolean isAuthenticated() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - return authentication != null && - authentication.isAuthenticated() && - authentication.getPrincipal() instanceof TokenUserDetails; - } - - /** - * 현재 사용자가 특정 권한을 가지고 있는지 확인 - */ - public static boolean hasRole(String role) { - try { - String currentRole = getCurrentUserRole(); - return role.equals(currentRole); - } catch (SecurityException e) { - return false; - } - } -} \ No newline at end of file diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/util/TokenUtil.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/util/TokenUtil.java deleted file mode 100644 index bc20f3f0..00000000 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/security/util/TokenUtil.java +++ /dev/null @@ -1,43 +0,0 @@ -package inu.codin.codinticketingsse.security.util; - -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.StringUtils; - -/** - * 토큰 추출 및 파싱 유틸리티 - */ -@Slf4j -public class TokenUtil { - - /** - * 쿠키 또는 Authorization 헤더에서 Access Token 추출 - */ - public static String extractToken(HttpServletRequest request) { - String bearerToken = null; - // 1. Authorization 헤더에서 토큰 추출 (우선순위 1) - String authHeader = request.getHeader("Authorization"); - if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) { - bearerToken = authHeader.substring(7); - log.debug("[extractToken] Authorization 헤더에서 토큰 추출: 성공"); - } else { - log.debug("[extractToken] Authorization 헤더: {}", authHeader != null ? "형식 오류" : "없음"); - } - - // 2. 쿠키에서 토큰 추출 (우선순위 2) - if (!StringUtils.hasText(bearerToken)) { - if (request.getCookies() != null) { - for (Cookie cookie : request.getCookies()) { - if ("x-access-token".equals(cookie.getName())) { - bearerToken = cookie.getValue(); - break; - } - } - } - log.debug("[extractToken] Cookie에서 추출한 토큰: {}", bearerToken != null ? "존재" : "없음"); - } - - return StringUtils.hasText(bearerToken) ? bearerToken : null; - } -} \ No newline at end of file diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/controller/SseController.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/controller/SseController.java index 6b111c0d..e1857850 100644 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/controller/SseController.java +++ b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/controller/SseController.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingsse.sse.controller; -import inu.codin.codinticketingsse.security.util.SecurityUtil; +import inu.codin.security.util.SecurityUtil; import inu.codin.codinticketingsse.sse.dto.EventStockStream; import inu.codin.codinticketingsse.sse.service.SseService; import io.swagger.v3.oas.annotations.Operation; diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/exception/SseErrorCode.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/exception/SseErrorCode.java index c4c82e4f..871b8b2f 100644 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/exception/SseErrorCode.java +++ b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/exception/SseErrorCode.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingsse.sse.exception; -import inu.codin.codinticketingsse.common.exception.GlobalErrorCode; +import inu.codin.common.exception.GlobalErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; diff --git a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/exception/SseException.java b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/exception/SseException.java index 0d466d84..295daa5b 100644 --- a/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/exception/SseException.java +++ b/codin-ticketing-sse/src/main/java/inu/codin/codinticketingsse/sse/exception/SseException.java @@ -1,6 +1,6 @@ package inu.codin.codinticketingsse.sse.exception; -import inu.codin.codinticketingsse.common.exception.GlobalException; +import inu.codin.common.exception.GlobalException; import lombok.Getter; @Getter diff --git a/codin-ticketing-sse/src/main/resources/application-local.yml b/codin-ticketing-sse/src/main/resources/application-local.yml new file mode 100644 index 00000000..98b0e383 --- /dev/null +++ b/codin-ticketing-sse/src/main/resources/application-local.yml @@ -0,0 +1,22 @@ +# 이름 : application-local.yml + +security: + permit-all: + urls: + - "/auth/**" + - "/v3/api/test1" + - "/ws-stomp/**" + - "/suspends" + - "/login/oauth2/code/**" + # --- Swagger / Springdoc --- + - "/swagger-ui/**" + - "/v3/api-docs/**" + - "/swagger-resources/**" + - "/webjars/**" + # --- 정적/루트/헬스체크 --- + - "/" + - "/favicon.ico" + - "/.well-known/**" + - "/actuator/health" + # Sse 구독 엔드포인트 허용 + - "/subscribe/**" diff --git a/codin-ticketing-sse/src/main/resources/application-prod.yml b/codin-ticketing-sse/src/main/resources/application-prod.yml new file mode 100644 index 00000000..05039eb3 --- /dev/null +++ b/codin-ticketing-sse/src/main/resources/application-prod.yml @@ -0,0 +1,8 @@ +# 이름 : application-prod.yml + +security: + #공개 API + public-api: + urls: + #Sse 구독 엔드포인트 허용 + "/subscribe/**"" \ No newline at end of file diff --git a/codin-ticketing-sse/src/main/resources/application.yml b/codin-ticketing-sse/src/main/resources/application.yml index bff5d5b5..42230640 100644 --- a/codin-ticketing-sse/src/main/resources/application.yml +++ b/codin-ticketing-sse/src/main/resources/application.yml @@ -1,6 +1,9 @@ spring: application: name: codin-ticketing-sse + profiles: + active: + local jwt: secret: ${SPRING_JWT_SECRET} mvc: diff --git a/docker/local/local-compose.yml b/docker/local/local-compose.yml index 59fa6e45..24832dac 100644 --- a/docker/local/local-compose.yml +++ b/docker/local/local-compose.yml @@ -4,12 +4,12 @@ services: image: mysql:8.0 container_name: codin-mysql env_file: - - .env.local + - ../../.env.local environment: - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:rootpassword} - MYSQL_DATABASE: ${MYSQL_DATABASE:codin_db} - MYSQL_USER: ${MYSQL_USER:codin} - MYSQL_PASSWORD: ${MYSQL_PASSWORD:codinpassword} + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} ports: - "${MYSQL_PORT}:3306" volumes: @@ -24,10 +24,10 @@ services: image: redis:7-alpine container_name: codin-redis env_file: - - .env.local + - ../../.env.local ports: - "${REDIS_PORT}:6379" - command: redis-server --requirepass ${REDIS_PASSWORD:-1234} + command: redis-server --requirepass ${REDIS_PASSWORD} volumes: - ./redis/data:/data restart: unless-stopped @@ -38,11 +38,11 @@ services: image: mongo:latest container_name: codin-mongodb env_file: - - .env.local + - ../../.env.local ports: - "${MONGODB_PORT:-27017}:27017" environment: - MONGO_INITDB_DATABASE: ${MONGO_INITDB_DATABASE:-codin} + MONGO_INITDB_DATABASE: ${MONGO_INITDB_DATABASE} volumes: - mongodb_data:/data/db restart: unless-stopped @@ -54,7 +54,7 @@ services: dockerfile: Dockerfile.es container_name: codin-elasticsearch env_file: - - .env.local + - ../../.env.local environment: - discovery.type=single-node - xpack.security.enabled=false diff --git a/settings.gradle b/settings.gradle index 191265be..d9485c62 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,9 @@ rootProject.name = 'codin-backend-repo' include 'codin-core' +include 'codin-auth' +include 'codin-common' +include 'codin-security' include 'codin-ticketing-api' include 'codin-ticketing-sse' include 'codin-lecture-api' \ No newline at end of file