From 63257e7baf75b32458f8f978452be95183ed09c2 Mon Sep 17 00:00:00 2001 From: baegteun Date: Sat, 17 Sep 2022 17:48:37 +0900 Subject: [PATCH 1/5] =?UTF-8?q?:bug:=20::=20=EC=9E=90=EB=B0=94=EC=98=80?= =?UTF-8?q?=EB=8D=98=20=EB=B2=84=EA=B7=B8=EB=A5=BC=20=EC=BD=94=ED=8B=80?= =?UTF-8?q?=EB=A6=B0=EC=9C=BC=EB=A1=9C.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 0 -> 6148 bytes .gitignore | 5 +- build.gradle | 40 -------- build.gradle.kts | 52 ++++++++++ docker-compose.yml | 22 +++++ settings.gradle | 1 - settings.gradle.kts | 1 + src/.DS_Store | Bin 0 -> 6148 bytes src/main/.DS_Store | Bin 0 -> 6148 bytes .../java/com/msg/gauth/GauthApplication.java | 15 --- .../msg/gauth/domain/auth/RefreshToken.java | 28 ------ .../ExpiredRefreshTokenException.java | 10 -- .../InvalidRefreshTokenException.java | 10 -- .../exception/PasswordMismatchException.java | 10 -- .../auth/presentation/AuthController.java | 50 ---------- .../dto/request/SigninRequestDto.java | 16 --- .../dto/response/RefreshResponseDto.java | 17 ---- .../dto/response/SigninResponseDto.java | 17 ---- .../repository/RefreshTokenRepository.java | 8 -- .../domain/auth/services/LogoutService.java | 21 ---- .../domain/auth/services/RefreshService.java | 45 --------- .../domain/auth/services/SignUpService.java | 37 ------- .../domain/auth/services/SigninService.java | 44 --------- .../com/msg/gauth/domain/client/Client.java | 33 ------- .../client/presentation/ClientController.java | 7 -- .../client/presentation/dto/request/.gitkeep | 0 .../client/presentation/dto/response/.gitkeep | 0 .../gauth/domain/client/repository/.gitkeep | 0 .../msg/gauth/domain/client/services/.gitkeep | 0 .../gauth/domain/email/EmailAuthEntity.java | 37 ------- .../exception/AuthCodeExpiredException.java | 10 -- .../ManyRequestEmailAuthException.java | 10 -- .../presentation/EmailAuthController.java | 29 ------ .../dto/request/EmailSendDto.java | 12 --- .../email/repository/EmailAuthRepository.java | 7 -- .../email/services/MailSendService.java | 58 ----------- .../services/MailVerificationService.java | 26 ----- .../java/com/msg/gauth/domain/user/User.java | 47 --------- .../msg/gauth/domain/user/enums/Gender.java | 5 - .../msg/gauth/domain/user/enums/UserRole.java | 12 --- .../gauth/domain/user/enums/UserState.java | 5 - .../exception/EmailNotVerifiedException.java | 10 -- .../user/exception/UserNotFoundException.java | 10 -- .../presentation/dto/request/SignUpDto.java | 37 ------- .../user/presentation/dto/response/.gitkeep | 0 .../user/repository/UserRepository.java | 11 --- .../msg/gauth/domain/user/services/.gitkeep | 0 .../msg/gauth/domain/user/utils/UserUtil.java | 32 ------ .../annotation/logger/InjectionLogger.java | 43 --------- .../gauth/global/annotation/logger/log4k.java | 12 --- .../gauth/global/exception/ErrorResponse.java | 15 --- .../exception/exceptions/BasicException.java | 15 --- .../exceptions/DuplicateEmailException.java | 9 -- .../exceptions/MessageSendFailException.java | 9 -- .../handler/GlobalExceptionHandler.java | 28 ------ .../msg/gauth/global/redis/RedisConfig.java | 40 -------- .../redis/properties/RedisProperties.java | 15 --- .../CustomAuthenticationEntryPoint.java | 35 ------- .../gauth/global/security/SecurityConfig.java | 55 ----------- .../global/security/auth/AuthDetails.java | 50 ---------- .../security/auth/AuthDetailsService.java | 25 ----- .../global/security/config/FilterConfig.java | 26 ----- .../exception/ExpiredTokenException.java | 11 --- .../exception/InvalidTokenException.java | 10 -- .../security/filter/ExceptionFilter.java | 44 --------- .../security/filter/JwtTokenFilter.java | 32 ------ .../global/security/jwt/JwtProperties.java | 15 --- .../global/security/jwt/JwtTokenProvider.java | 91 ------------------ .../com/msg/gauth/GauthBackendApplication.kt | 13 +++ .../com/msg/gauth/domain/auth/RefreshToken.kt | 23 +++++ .../exception/ExpiredRefreshTokenException.kt | 6 ++ .../exception/InvalidRefreshTokenException.kt | 6 ++ .../exception/PasswordMismatchException.kt | 6 ++ .../auth/presentation/AuthController.kt | 46 +++++++++ .../dto/request/SigninRequestDto.kt | 13 +++ .../dto/request/SignupRequestDto.kt | 26 +++++ .../dto/response/RefreshResponseDto.kt | 16 +++ .../dto/response/SigninResponseDto.kt | 16 +++ .../auth/repository/RefreshTokenRepository.kt | 8 ++ .../domain/auth/services/LogoutService.kt | 19 ++++ .../domain/auth/services/RefreshService.kt | 36 +++++++ .../domain/auth/services/SignInService.kt | 36 +++++++ .../domain/auth/services/SignUpService.kt | 31 ++++++ .../com/msg/gauth/domain/client/Client.kt | 18 ++++ .../msg/gauth/domain/email/EmailAuthEntity.kt | 32 ++++++ .../exception/AuthCodeExpiredException.kt | 6 ++ .../ManyRequestEmailAuthException.kt | 6 ++ .../email/presentation/EmailAuthController.kt | 26 +++++ .../email/presentation/dto/EmailSendDto.kt | 3 + .../email/repository/EmailAuthRepository.kt | 6 ++ .../domain/email/services/MailSendService.kt | 56 +++++++++++ .../email/services/MailVerificationService.kt | 21 ++++ .../kotlin/com/msg/gauth/domain/user/User.kt | 37 +++++++ .../com/msg/gauth/domain/user/enums/Gender.kt | 5 + .../msg/gauth/domain/user/enums/UserRole.kt | 10 ++ .../msg/gauth/domain/user/enums/UserState.kt | 5 + .../exception/EmailNotVerifiedException.kt | 6 ++ .../user/exception/UserNotFoundException.kt | 6 ++ .../domain/user/repository/UserRepository.kt | 9 ++ .../msg/gauth/domain/user/utils/UserUtil.kt | 27 ++++++ .../annotation/logger/InjectionLogger.kt | 37 +++++++ .../gauth/global/annotation/logger/log4k.kt | 6 ++ .../msg/gauth/global/entity/BaseIdEntity.kt | 13 +++ .../msg/gauth/global/exception/ErrorCode.kt} | 22 ++--- .../gauth/global/exception/ErrorResponse.kt | 11 +++ .../exception/exceptions/BasicException.kt | 5 + .../exceptions/DuplicateEmailException.kt | 5 + .../exceptions/MessageSendFailException.kt | 5 + .../handler/GlobalExceptionHandler.kt | 25 +++++ .../msg/gauth/global/jackson/JacksonConfig.kt | 18 ++++ .../com/msg/gauth/global/mail/MailConfig.kt | 31 ++++++ .../global/mail/properties/MailProperties.kt | 14 +++ .../com/msg/gauth/global/redis/RedisConfig.kt | 34 +++++++ .../redis/properties/RedisProperties.kt | 11 +++ .../CustomAuthenticationEntryPoint.kt | 29 ++++++ .../gauth/global/security/SecurityConfig.kt | 66 +++++++++++++ .../gauth/global/security/auth/AuthDetails.kt | 32 ++++++ .../security/auth/AuthDetailsService.kt | 20 ++++ .../global/security/config/FilterConfig.kt | 23 +++++ .../exception/ExpiredTokenException.kt | 6 ++ .../exception/InvalidTokenException.kt | 6 ++ .../global/security/filter/ExceptionFilter.kt | 40 ++++++++ .../global/security/filter/JwtTokenFilter.kt | 29 ++++++ .../global/security/jwt/JwtProperties.kt | 21 ++++ .../global/security/jwt/JwtTokenProvider.kt | 84 ++++++++++++++++ .../com/msg/gauth/GauthApplicationTests.java | 13 --- .../email/services/MailSendServiceTest.java | 24 ----- .../user/services/SignUpServiceTest.java | 86 ----------------- .../msg/gauth/GauthBackendApplicationTests.kt | 13 +++ 129 files changed, 1251 insertions(+), 1485 deletions(-) create mode 100644 .DS_Store delete mode 100644 build.gradle create mode 100644 build.gradle.kts create mode 100644 docker-compose.yml delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts create mode 100644 src/.DS_Store create mode 100644 src/main/.DS_Store delete mode 100644 src/main/java/com/msg/gauth/GauthApplication.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/RefreshToken.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/exception/ExpiredRefreshTokenException.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/exception/InvalidRefreshTokenException.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/exception/PasswordMismatchException.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/presentation/AuthController.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/presentation/dto/request/SigninRequestDto.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/presentation/dto/response/RefreshResponseDto.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/presentation/dto/response/SigninResponseDto.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/repository/RefreshTokenRepository.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/services/LogoutService.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/services/RefreshService.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/services/SignUpService.java delete mode 100644 src/main/java/com/msg/gauth/domain/auth/services/SigninService.java delete mode 100644 src/main/java/com/msg/gauth/domain/client/Client.java delete mode 100644 src/main/java/com/msg/gauth/domain/client/presentation/ClientController.java delete mode 100644 src/main/java/com/msg/gauth/domain/client/presentation/dto/request/.gitkeep delete mode 100644 src/main/java/com/msg/gauth/domain/client/presentation/dto/response/.gitkeep delete mode 100644 src/main/java/com/msg/gauth/domain/client/repository/.gitkeep delete mode 100644 src/main/java/com/msg/gauth/domain/client/services/.gitkeep delete mode 100644 src/main/java/com/msg/gauth/domain/email/EmailAuthEntity.java delete mode 100644 src/main/java/com/msg/gauth/domain/email/exception/AuthCodeExpiredException.java delete mode 100644 src/main/java/com/msg/gauth/domain/email/exception/ManyRequestEmailAuthException.java delete mode 100644 src/main/java/com/msg/gauth/domain/email/presentation/EmailAuthController.java delete mode 100644 src/main/java/com/msg/gauth/domain/email/presentation/dto/request/EmailSendDto.java delete mode 100644 src/main/java/com/msg/gauth/domain/email/repository/EmailAuthRepository.java delete mode 100644 src/main/java/com/msg/gauth/domain/email/services/MailSendService.java delete mode 100644 src/main/java/com/msg/gauth/domain/email/services/MailVerificationService.java delete mode 100644 src/main/java/com/msg/gauth/domain/user/User.java delete mode 100644 src/main/java/com/msg/gauth/domain/user/enums/Gender.java delete mode 100644 src/main/java/com/msg/gauth/domain/user/enums/UserRole.java delete mode 100644 src/main/java/com/msg/gauth/domain/user/enums/UserState.java delete mode 100644 src/main/java/com/msg/gauth/domain/user/exception/EmailNotVerifiedException.java delete mode 100644 src/main/java/com/msg/gauth/domain/user/exception/UserNotFoundException.java delete mode 100644 src/main/java/com/msg/gauth/domain/user/presentation/dto/request/SignUpDto.java delete mode 100644 src/main/java/com/msg/gauth/domain/user/presentation/dto/response/.gitkeep delete mode 100644 src/main/java/com/msg/gauth/domain/user/repository/UserRepository.java delete mode 100644 src/main/java/com/msg/gauth/domain/user/services/.gitkeep delete mode 100644 src/main/java/com/msg/gauth/domain/user/utils/UserUtil.java delete mode 100644 src/main/java/com/msg/gauth/global/annotation/logger/InjectionLogger.java delete mode 100644 src/main/java/com/msg/gauth/global/annotation/logger/log4k.java delete mode 100644 src/main/java/com/msg/gauth/global/exception/ErrorResponse.java delete mode 100644 src/main/java/com/msg/gauth/global/exception/exceptions/BasicException.java delete mode 100644 src/main/java/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.java delete mode 100644 src/main/java/com/msg/gauth/global/exception/exceptions/MessageSendFailException.java delete mode 100644 src/main/java/com/msg/gauth/global/exception/handler/GlobalExceptionHandler.java delete mode 100644 src/main/java/com/msg/gauth/global/redis/RedisConfig.java delete mode 100644 src/main/java/com/msg/gauth/global/redis/properties/RedisProperties.java delete mode 100644 src/main/java/com/msg/gauth/global/security/CustomAuthenticationEntryPoint.java delete mode 100644 src/main/java/com/msg/gauth/global/security/SecurityConfig.java delete mode 100644 src/main/java/com/msg/gauth/global/security/auth/AuthDetails.java delete mode 100644 src/main/java/com/msg/gauth/global/security/auth/AuthDetailsService.java delete mode 100644 src/main/java/com/msg/gauth/global/security/config/FilterConfig.java delete mode 100644 src/main/java/com/msg/gauth/global/security/exception/ExpiredTokenException.java delete mode 100644 src/main/java/com/msg/gauth/global/security/exception/InvalidTokenException.java delete mode 100644 src/main/java/com/msg/gauth/global/security/filter/ExceptionFilter.java delete mode 100644 src/main/java/com/msg/gauth/global/security/filter/JwtTokenFilter.java delete mode 100644 src/main/java/com/msg/gauth/global/security/jwt/JwtProperties.java delete mode 100644 src/main/java/com/msg/gauth/global/security/jwt/JwtTokenProvider.java create mode 100644 src/main/kotlin/com/msg/gauth/GauthBackendApplication.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/RefreshToken.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/exception/ExpiredRefreshTokenException.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/exception/InvalidRefreshTokenException.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/exception/PasswordMismatchException.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/presentation/AuthController.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/request/SigninRequestDto.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/request/SignupRequestDto.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/response/RefreshResponseDto.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/response/SigninResponseDto.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/repository/RefreshTokenRepository.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/services/LogoutService.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/services/RefreshService.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/services/SignInService.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/auth/services/SignUpService.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/client/Client.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/email/EmailAuthEntity.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/email/exception/AuthCodeExpiredException.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/email/exception/ManyRequestEmailAuthException.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/email/presentation/EmailAuthController.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/email/presentation/dto/EmailSendDto.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/email/repository/EmailAuthRepository.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/email/services/MailSendService.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/email/services/MailVerificationService.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/user/User.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/user/enums/Gender.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/user/enums/UserRole.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/user/enums/UserState.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/user/exception/EmailNotVerifiedException.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/user/exception/UserNotFoundException.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/user/repository/UserRepository.kt create mode 100644 src/main/kotlin/com/msg/gauth/domain/user/utils/UserUtil.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/annotation/logger/InjectionLogger.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/annotation/logger/log4k.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/entity/BaseIdEntity.kt rename src/main/{java/com/msg/gauth/global/exception/ErrorCode.java => kotlin/com/msg/gauth/global/exception/ErrorCode.kt} (77%) create mode 100644 src/main/kotlin/com/msg/gauth/global/exception/ErrorResponse.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/exception/exceptions/BasicException.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/exception/exceptions/MessageSendFailException.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/exception/handler/GlobalExceptionHandler.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/jackson/JacksonConfig.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/mail/MailConfig.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/mail/properties/MailProperties.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/redis/RedisConfig.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/redis/properties/RedisProperties.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/security/CustomAuthenticationEntryPoint.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/security/SecurityConfig.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/security/auth/AuthDetails.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/security/auth/AuthDetailsService.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/security/config/FilterConfig.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/security/exception/ExpiredTokenException.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/security/exception/InvalidTokenException.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/security/filter/ExceptionFilter.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/security/filter/JwtTokenFilter.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/security/jwt/JwtProperties.kt create mode 100644 src/main/kotlin/com/msg/gauth/global/security/jwt/JwtTokenProvider.kt delete mode 100644 src/test/java/com/msg/gauth/GauthApplicationTests.java delete mode 100644 src/test/java/com/msg/gauth/domain/email/services/MailSendServiceTest.java delete mode 100644 src/test/java/com/msg/gauth/domain/user/services/SignUpServiceTest.java create mode 100644 src/test/kotlin/com/msg/gauth/GauthBackendApplicationTests.kt diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e8d69ecd48d60ef4e022ee5897af431db2cd5734 GIT binary patch literal 6148 zcmeHKy-ve05I(ml3Zg<069a=^p%ViV3RQT5z5ujAr6^S@Q1-kFFTl*qJFp?~0u>V@ z;ye4GbwkBQD|9E>pL0LH%NNUziOBS;yhoG}QH;je+C}$+u$@~&TB7L!jhiDc#=XI) zoX#4)s@P5j_}g`8NEJ<}pz!;3KjxuzT4vd>D2LF-$?NgM%gxi-sz?5d$LxA(g&eqr zvO{CKrAx|D!Bx0|=0})6urJr}xZk<2Ca#7RpG6hxe1bZ#dLeDPLmi{Ag6ETkWjXih zd-RoC;-=*yYttI!YrnaK+9?CdfHJTl2K=r^@!p2=Yg%PM8Q5M1cz>{HjHzJl(QO^* z+!X*ALG1)%zDsb76-)(dkFY?D4+Z*ABUcRL!=bn0mkQP%eK;Apd>F}W` zJDJp@_R4@V5HisAj|rdu=j-qPVURv41Ioa^V!%Y%AnW6mf#{Ebqd_}m;7=L&1o;zX^Z)<= literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore index 0f4dc992..164c7940 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,7 @@ out/ ### VS Code ### .vscode/ -/src/main/resources \ No newline at end of file +/src/main/resources + +### docker ##3 +docker-compose.yml diff --git a/build.gradle b/build.gradle deleted file mode 100644 index cf746acb..00000000 --- a/build.gradle +++ /dev/null @@ -1,40 +0,0 @@ -plugins { - id 'org.springframework.boot' version '2.7.2' - id 'io.spring.dependency-management' version '1.0.12.RELEASE' - id 'java' -} - -group = 'com.msg' -version = '0.0.1-SNAPSHOT' -sourceCompatibility = '11' - -configurations { - compileOnly { - extendsFrom annotationProcessor - } -} - -repositories { - mavenCentral() -} - -dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-data-redis' - implementation 'io.jsonwebtoken:jjwt:0.9.1' - compileOnly 'org.projectlombok:lombok' - runtimeOnly 'com.h2database:h2' - runtimeOnly 'mysql:mysql-connector-java' - annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' - annotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.springframework.security:spring-security-test' - implementation 'org.springframework.boot:spring-boot-starter-mail' -} - -tasks.named('test') { - useJUnitPlatform() -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..b17b6974 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,52 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("org.springframework.boot") version "2.7.3" + id("io.spring.dependency-management") version "1.0.13.RELEASE" + kotlin("jvm") version "1.6.21" + kotlin("plugin.spring") version "1.6.21" + kotlin("plugin.jpa") version "1.6.21" +} + +group = "com.msg" +version = "0.0.1-SNAPSHOT" +java.sourceCompatibility = JavaVersion.VERSION_11 + +configurations { + compileOnly { + extendsFrom(configurations.annotationProcessor.get()) + } +} + +repositories { + mavenCentral() +} + +dependencies { + 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-security") + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-mail") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.boot:spring-boot-starter-validation") + implementation("io.jsonwebtoken:jjwt-gson:0.11.5") + implementation("org.jetbrains.kotlin:kotlin-reflect") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + runtimeOnly("com.h2database:h2") + runtimeOnly("mysql:mysql-connector-java") + annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.springframework.security:spring-security-test") +} + +tasks.withType { + kotlinOptions { + freeCompilerArgs = listOf("-Xjsr305=strict") + jvmTarget = "11" + } +} + +tasks.withType { + useJUnitPlatform() +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..66da328b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ + +version: '2.2' + +services: + db: + image: mysql + environment: + MYSQL_USER: baek + MYSQL_PASSWORD: 1234 + MYSQL_DATABASE: gauth + MYSQL_ROOT_PASSWORD: 1234 + depends_on: + - redis + ports: + - 3306:3306 + redis: + image: redis + environment: + - ALLOW_EMPTY_PASSWORD=yes + - REDIS_DISABLE_COMMANDS=FLUSHDB,FLUSHALL + ports: + - 6379:6379 \ No newline at end of file diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 2334a143..00000000 --- a/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'gauth' diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..fc10a5d8 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "gauth" diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c26928450e0764f75666d57e96c5691197510c4e GIT binary patch literal 6148 zcmeHKT}s115T3CS0=^V{@iABE4PuEW=mFGP(L!Siw(q%1?@@g45FWs52!8XU&En#t zh|a+7w>#g?PV$xRWQmBU+r@-vOhhG`Aj=?Q;?7b>zF0_>Jyv&<*-h83Gn@QYmppqx zEnU-+9@6LEUk1%Pw>#=Qj$eZJny!ty!>ab8C6bJ=Efq$$3YPMK;Vip9;wNP@xIdj=iBC z9q9B504(5E!&vST5>q+Wj=dom7+WaNLfNkv?4PJ?;oxgu-y2#uv1Z0Tp84bQ!kQia zN|zJYhS7%tp+Ks@$fjed{}=enWH0$?NX$ZkP~eX#z(q4_rg$l@tzTYGYHfvfg(fEJ rdKthl)=~`AQjCe5BWFq)U&$DJ?bsX2F5=g4V0;LuAkl>aKcK)nT%kmQ literal 0 HcmV?d00001 diff --git a/src/main/.DS_Store b/src/main/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..58077c786bd0a110126fbaa25e13d4ebf88a5f5a GIT binary patch literal 6148 zcmeHKOHRW;4D}=hC1TSh%iI9<1cWM_plc-57C}oxDj@qDfeWyKd$3}~DL4wxAC-m# z*dbKelIKl4_Kfo;i8DkL#`SzeG$f(~${3|+dW6SWI}(|N2ZPM9pqtg@XfmtHb;sKc z|B(USyM4N*itcGeo&9@y{5oD%MKN7fQ`nQ^Xz#gRyqw~{b~gb z$9Qi3jiQEAkd-lytsLxx5@dDQGjXSoD|+h;I0J14V!a%4{XhNw{NE1pD`&tN*eM3W zpqLb6+>+JS&dqVH&7kK{7WQitHzDXqDTXhX;zOti>=}1}xnZLS3&ei}ga&V%fj?#7 E14dv_Q2+n{ literal 0 HcmV?d00001 diff --git a/src/main/java/com/msg/gauth/GauthApplication.java b/src/main/java/com/msg/gauth/GauthApplication.java deleted file mode 100644 index b4410555..00000000 --- a/src/main/java/com/msg/gauth/GauthApplication.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.msg.gauth; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.ConfigurationPropertiesScan; - -@SpringBootApplication -@ConfigurationPropertiesScan -public class GauthApplication { - - public static void main(String[] args) { - SpringApplication.run(GauthApplication.class, args); - } - -} diff --git a/src/main/java/com/msg/gauth/domain/auth/RefreshToken.java b/src/main/java/com/msg/gauth/domain/auth/RefreshToken.java deleted file mode 100644 index 9a2a482d..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/RefreshToken.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.msg.gauth.domain.auth; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.data.annotation.Id; -import org.springframework.data.redis.core.RedisHash; -import org.springframework.data.redis.core.TimeToLive; -import org.springframework.data.redis.core.index.Indexed; - - -@RedisHash -@AllArgsConstructor -@Getter -public class RefreshToken { - @Id - private Long userId; - - @Indexed - private String token; - - @TimeToLive - private Long timeToLive; - - public void updateToken(String token, Long timeToLive) { - this.token = token; - this.timeToLive = timeToLive; - } -} diff --git a/src/main/java/com/msg/gauth/domain/auth/exception/ExpiredRefreshTokenException.java b/src/main/java/com/msg/gauth/domain/auth/exception/ExpiredRefreshTokenException.java deleted file mode 100644 index 487d17e1..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/exception/ExpiredRefreshTokenException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.msg.gauth.domain.auth.exception; - -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.exceptions.BasicException; - -public class ExpiredRefreshTokenException extends BasicException { - public ExpiredRefreshTokenException() { - super(ErrorCode.EXPIRED_REFRESH_TOKEN); - } -} diff --git a/src/main/java/com/msg/gauth/domain/auth/exception/InvalidRefreshTokenException.java b/src/main/java/com/msg/gauth/domain/auth/exception/InvalidRefreshTokenException.java deleted file mode 100644 index 96ac2b2c..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/exception/InvalidRefreshTokenException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.msg.gauth.domain.auth.exception; - -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.exceptions.BasicException; - -public class InvalidRefreshTokenException extends BasicException { - public InvalidRefreshTokenException() { - super(ErrorCode.INVALID_REFRESH_TOKEN); - } -} diff --git a/src/main/java/com/msg/gauth/domain/auth/exception/PasswordMismatchException.java b/src/main/java/com/msg/gauth/domain/auth/exception/PasswordMismatchException.java deleted file mode 100644 index b229edd2..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/exception/PasswordMismatchException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.msg.gauth.domain.auth.exception; - -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.exceptions.BasicException; - -public class PasswordMismatchException extends BasicException { - public PasswordMismatchException() { - super(ErrorCode.PASSWORD_MISMATCH); - } -} diff --git a/src/main/java/com/msg/gauth/domain/auth/presentation/AuthController.java b/src/main/java/com/msg/gauth/domain/auth/presentation/AuthController.java deleted file mode 100644 index 24573848..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/presentation/AuthController.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.msg.gauth.domain.auth.presentation; - -import com.msg.gauth.domain.auth.presentation.dto.request.SigninRequestDto; -import com.msg.gauth.domain.auth.presentation.dto.response.RefreshResponseDto; -import com.msg.gauth.domain.auth.presentation.dto.response.SigninResponseDto; -import com.msg.gauth.domain.auth.services.LogoutService; -import com.msg.gauth.domain.auth.services.RefreshService; -import com.msg.gauth.domain.auth.services.SigninService; -import com.msg.gauth.domain.user.presentation.dto.request.SignUpDto; -import com.msg.gauth.domain.auth.services.SignUpService; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; -import java.net.URI; -import java.net.URISyntaxException; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/auth") -public class AuthController { - private final RefreshService refreshService; - private final LogoutService logoutService; - private final SigninService signinService; - private final SignUpService signUpService; - - @PatchMapping - public ResponseEntity refresh(@RequestHeader String refreshToken){ - return ResponseEntity.ok(refreshService.execute(refreshToken)); - } - - @DeleteMapping - public ResponseEntity logout(){ - logoutService.execute(); - return ResponseEntity.noContent().build(); - } - - @PostMapping - public ResponseEntity signin(@RequestBody @Valid SigninRequestDto signinRequestDto){ - return ResponseEntity.ok(signinService.execute(signinRequestDto)); - } - - @PostMapping("/signup") - public ResponseEntity signUpMember(@RequestBody @Valid SignUpDto signUpDto) throws URISyntaxException { - signUpService.execute(signUpDto); - return new ResponseEntity<>(HttpStatus.CREATED); - } -} diff --git a/src/main/java/com/msg/gauth/domain/auth/presentation/dto/request/SigninRequestDto.java b/src/main/java/com/msg/gauth/domain/auth/presentation/dto/request/SigninRequestDto.java deleted file mode 100644 index 5703312c..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/presentation/dto/request/SigninRequestDto.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.msg.gauth.domain.auth.presentation.dto.request; - -import lombok.Getter; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Pattern; - -@Getter -public class SigninRequestDto { - @NotBlank - @Pattern(regexp = "^[a-zA-Z0-9]+@gsm.hs.kr$") - String email; - - @NotBlank - String password; -} diff --git a/src/main/java/com/msg/gauth/domain/auth/presentation/dto/response/RefreshResponseDto.java b/src/main/java/com/msg/gauth/domain/auth/presentation/dto/response/RefreshResponseDto.java deleted file mode 100644 index 0047613f..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/presentation/dto/response/RefreshResponseDto.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.msg.gauth.domain.auth.presentation.dto.response; - -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; - -import java.time.ZonedDateTime; - -@AllArgsConstructor -public class RefreshResponseDto { - @JsonProperty - String accessToken; - @JsonProperty - String refreshToken; - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") - ZonedDateTime expiresAt; -} diff --git a/src/main/java/com/msg/gauth/domain/auth/presentation/dto/response/SigninResponseDto.java b/src/main/java/com/msg/gauth/domain/auth/presentation/dto/response/SigninResponseDto.java deleted file mode 100644 index 9659b156..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/presentation/dto/response/SigninResponseDto.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.msg.gauth.domain.auth.presentation.dto.response; - -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; - -import java.time.ZonedDateTime; - -@AllArgsConstructor -public class SigninResponseDto { - @JsonProperty - String accessToken; - @JsonProperty - String refreshToken; - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") - ZonedDateTime expiresAt; -} diff --git a/src/main/java/com/msg/gauth/domain/auth/repository/RefreshTokenRepository.java b/src/main/java/com/msg/gauth/domain/auth/repository/RefreshTokenRepository.java deleted file mode 100644 index eeabef1b..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/repository/RefreshTokenRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.msg.gauth.domain.auth.repository; - -import com.msg.gauth.domain.auth.RefreshToken; -import org.springframework.data.repository.CrudRepository; - -public interface RefreshTokenRepository extends CrudRepository { - -} diff --git a/src/main/java/com/msg/gauth/domain/auth/services/LogoutService.java b/src/main/java/com/msg/gauth/domain/auth/services/LogoutService.java deleted file mode 100644 index 5d78de93..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/services/LogoutService.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.msg.gauth.domain.auth.services; - -import com.msg.gauth.domain.auth.RefreshToken; -import com.msg.gauth.domain.auth.repository.RefreshTokenRepository; -import com.msg.gauth.domain.user.User; -import com.msg.gauth.domain.user.utils.UserUtil; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class LogoutService { - private final UserUtil userUtil; - private final RefreshTokenRepository refreshTokenRepository; - - public void execute() { - User currentUser = userUtil.fetchCurrentUser(); - refreshTokenRepository.findById(currentUser.getId()) - .ifPresent(refreshTokenRepository::delete); - } -} diff --git a/src/main/java/com/msg/gauth/domain/auth/services/RefreshService.java b/src/main/java/com/msg/gauth/domain/auth/services/RefreshService.java deleted file mode 100644 index 086add80..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/services/RefreshService.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.msg.gauth.domain.auth.services; - -import com.msg.gauth.domain.auth.RefreshToken; -import com.msg.gauth.domain.auth.exception.ExpiredRefreshTokenException; -import com.msg.gauth.domain.auth.exception.InvalidRefreshTokenException; -import com.msg.gauth.domain.auth.presentation.dto.response.RefreshResponseDto; -import com.msg.gauth.domain.auth.repository.RefreshTokenRepository; -import com.msg.gauth.domain.user.User; -import com.msg.gauth.domain.user.exception.UserNotFoundException; -import com.msg.gauth.domain.user.repository.UserRepository; -import com.msg.gauth.global.security.jwt.JwtTokenProvider; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.ZonedDateTime; -import java.util.Objects; - -@Service -@RequiredArgsConstructor -public class RefreshService { - private final JwtTokenProvider jwtTokenProvider; - private final UserRepository userRepository; - private final RefreshTokenRepository refreshTokenRepository; - - @Transactional - public RefreshResponseDto execute(String refreshToken) { - String email = jwtTokenProvider.exactEmailFromRefreshToken(refreshToken); - User user = userRepository.findByEmail(email) - .orElseThrow(UserNotFoundException::new); - RefreshToken redisRefreshToken = refreshTokenRepository.findById(user.getId()) - .orElseThrow(ExpiredRefreshTokenException::new); - if (!Objects.equals(redisRefreshToken.getToken(), refreshToken)) { - throw new InvalidRefreshTokenException(); - } - String access = jwtTokenProvider.generateAccessToken(email); - String refresh = jwtTokenProvider.generateRefreshToken(email); - ZonedDateTime expiresAt = jwtTokenProvider.getAccessExpiredTime(); - - redisRefreshToken.updateToken(refresh, jwtTokenProvider.getRefreshTimeToLive()); - refreshTokenRepository.save(redisRefreshToken); - - return new RefreshResponseDto(access, refresh, expiresAt); - } -} diff --git a/src/main/java/com/msg/gauth/domain/auth/services/SignUpService.java b/src/main/java/com/msg/gauth/domain/auth/services/SignUpService.java deleted file mode 100644 index 5b7b1289..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/services/SignUpService.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.msg.gauth.domain.auth.services; - -import com.msg.gauth.domain.email.EmailAuthEntity; -import com.msg.gauth.domain.email.repository.EmailAuthRepository; -import com.msg.gauth.domain.user.User; -import com.msg.gauth.domain.user.exception.EmailNotVerifiedException; -import com.msg.gauth.domain.user.presentation.dto.request.SignUpDto; -import com.msg.gauth.domain.user.repository.UserRepository; -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.exceptions.DuplicateEmailException; -import lombok.RequiredArgsConstructor; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -public class SignUpService { - - private final UserRepository userRepository; - private final PasswordEncoder passwordEncoder; - private final EmailAuthRepository emailAuthRepository; - - @Transactional - public Long execute(SignUpDto signUpDto){ - if(userRepository.existsByEmail(signUpDto.getEmail())){ - throw new DuplicateEmailException(ErrorCode.DUPLICATE_EMAIL); - } - String password = signUpDto.getPassword(); - User user = signUpDto.toEntity(passwordEncoder.encode(password)); - EmailAuthEntity emailAuth = emailAuthRepository.findById(signUpDto.getEmail()) - .orElseThrow(EmailNotVerifiedException::new); - if (!emailAuth.getAuthentication()) - throw new EmailNotVerifiedException(); - return userRepository.save(user).getId(); - } -} diff --git a/src/main/java/com/msg/gauth/domain/auth/services/SigninService.java b/src/main/java/com/msg/gauth/domain/auth/services/SigninService.java deleted file mode 100644 index 18532471..00000000 --- a/src/main/java/com/msg/gauth/domain/auth/services/SigninService.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.msg.gauth.domain.auth.services; - -import com.msg.gauth.domain.auth.RefreshToken; -import com.msg.gauth.domain.auth.exception.PasswordMismatchException; -import com.msg.gauth.domain.auth.presentation.dto.request.SigninRequestDto; -import com.msg.gauth.domain.auth.presentation.dto.response.SigninResponseDto; -import com.msg.gauth.domain.auth.repository.RefreshTokenRepository; -import com.msg.gauth.domain.user.User; -import com.msg.gauth.domain.user.exception.UserNotFoundException; -import com.msg.gauth.domain.user.repository.UserRepository; -import com.msg.gauth.global.security.jwt.JwtTokenProvider; -import lombok.RequiredArgsConstructor; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.ZonedDateTime; - -@Service -@RequiredArgsConstructor -public class SigninService { - private final JwtTokenProvider jwtTokenProvider; - private final UserRepository userRepository; - private final RefreshTokenRepository refreshTokenRepository; - private final PasswordEncoder passwordEncoder; - - @Transactional - public SigninResponseDto execute(SigninRequestDto dto) { - User user = userRepository.findByEmail(dto.getEmail()) - .orElseThrow(UserNotFoundException::new); - - if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) { - throw new PasswordMismatchException(); - } - - String access = jwtTokenProvider.generateAccessToken(dto.getEmail()); - String refresh = jwtTokenProvider.generateRefreshToken(dto.getEmail()); - ZonedDateTime expiresAt = jwtTokenProvider.getAccessExpiredTime(); - - refreshTokenRepository.save(new RefreshToken(user.getId(), refresh, jwtTokenProvider.getRefreshTimeToLive())); - - return new SigninResponseDto(access, refresh, expiresAt); - } -} diff --git a/src/main/java/com/msg/gauth/domain/client/Client.java b/src/main/java/com/msg/gauth/domain/client/Client.java deleted file mode 100644 index f248b932..00000000 --- a/src/main/java/com/msg/gauth/domain/client/Client.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.msg.gauth.domain.client; - -import com.msg.gauth.domain.user.User; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import javax.persistence.*; - -@Entity -@Getter -@AllArgsConstructor -@NoArgsConstructor -public class Client { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String clientId; - - @Column(length = 60) - private String clientSecret; - - private String redirectUri; - - private String serviceName; - - private String serviceUri; - - @ManyToOne - @JoinColumn(name = "user_id", nullable = false) - private User createdBy; -} diff --git a/src/main/java/com/msg/gauth/domain/client/presentation/ClientController.java b/src/main/java/com/msg/gauth/domain/client/presentation/ClientController.java deleted file mode 100644 index 23d2e8ea..00000000 --- a/src/main/java/com/msg/gauth/domain/client/presentation/ClientController.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.msg.gauth.domain.client.presentation; - -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class ClientController { -} diff --git a/src/main/java/com/msg/gauth/domain/client/presentation/dto/request/.gitkeep b/src/main/java/com/msg/gauth/domain/client/presentation/dto/request/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/com/msg/gauth/domain/client/presentation/dto/response/.gitkeep b/src/main/java/com/msg/gauth/domain/client/presentation/dto/response/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/com/msg/gauth/domain/client/repository/.gitkeep b/src/main/java/com/msg/gauth/domain/client/repository/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/com/msg/gauth/domain/client/services/.gitkeep b/src/main/java/com/msg/gauth/domain/client/services/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/com/msg/gauth/domain/email/EmailAuthEntity.java b/src/main/java/com/msg/gauth/domain/email/EmailAuthEntity.java deleted file mode 100644 index 127f1b7c..00000000 --- a/src/main/java/com/msg/gauth/domain/email/EmailAuthEntity.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.msg.gauth.domain.email; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.ColumnDefault; -import org.hibernate.validator.constraints.Length; -import org.springframework.data.annotation.Id; -import org.springframework.data.redis.core.RedisHash; - -@Getter -@Builder -@AllArgsConstructor -@NoArgsConstructor -@RedisHash(value = "emailAuth", timeToLive = 60 * 15) -public class EmailAuthEntity { - @Id - private String email; - - @Length(max = 36) - private String randomValue; - private Boolean authentication; - - @ColumnDefault("1") - private Integer attemptCount; - - public void updateAuthentication(Boolean authentication) { - this.authentication = authentication; - } - public void updateRandomValue(String uuid) { - this.randomValue = uuid; - } - public void increaseAttemptCount() { - this.attemptCount += 1; - } -} diff --git a/src/main/java/com/msg/gauth/domain/email/exception/AuthCodeExpiredException.java b/src/main/java/com/msg/gauth/domain/email/exception/AuthCodeExpiredException.java deleted file mode 100644 index 5d8b8868..00000000 --- a/src/main/java/com/msg/gauth/domain/email/exception/AuthCodeExpiredException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.msg.gauth.domain.email.exception; - -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.exceptions.BasicException; - -public class AuthCodeExpiredException extends BasicException { - public AuthCodeExpiredException() { - super(ErrorCode.AUTH_CODE_EXPIRED); - } -} diff --git a/src/main/java/com/msg/gauth/domain/email/exception/ManyRequestEmailAuthException.java b/src/main/java/com/msg/gauth/domain/email/exception/ManyRequestEmailAuthException.java deleted file mode 100644 index 14c644eb..00000000 --- a/src/main/java/com/msg/gauth/domain/email/exception/ManyRequestEmailAuthException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.msg.gauth.domain.email.exception; - -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.exceptions.BasicException; - -public class ManyRequestEmailAuthException extends BasicException { - public ManyRequestEmailAuthException() { - super(ErrorCode.MANY_REQUEST_EMAIL_AUTH); - } -} diff --git a/src/main/java/com/msg/gauth/domain/email/presentation/EmailAuthController.java b/src/main/java/com/msg/gauth/domain/email/presentation/EmailAuthController.java deleted file mode 100644 index 7c8fd7b8..00000000 --- a/src/main/java/com/msg/gauth/domain/email/presentation/EmailAuthController.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.msg.gauth.domain.email.presentation; - -import com.msg.gauth.domain.email.presentation.dto.request.EmailSendDto; -import com.msg.gauth.domain.email.services.MailSendService; -import com.msg.gauth.domain.email.services.MailVerificationService; -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/email") -@RequiredArgsConstructor -public class EmailAuthController { - private final MailSendService mailSendService; - private final MailVerificationService mailVerificationService; - - @PostMapping - public ResponseEntity emailSend(@RequestBody EmailSendDto emailSendDto){ - mailSendService.execute(emailSendDto); - return ResponseEntity.noContent().build(); - } - - @GetMapping("/authentication") - public ResponseEntity emailVerification(@RequestParam String email, @RequestParam String uuid){ - mailVerificationService.execute(email, uuid); - return ResponseEntity.noContent().build(); - } - -} diff --git a/src/main/java/com/msg/gauth/domain/email/presentation/dto/request/EmailSendDto.java b/src/main/java/com/msg/gauth/domain/email/presentation/dto/request/EmailSendDto.java deleted file mode 100644 index a6b1edd3..00000000 --- a/src/main/java/com/msg/gauth/domain/email/presentation/dto/request/EmailSendDto.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.msg.gauth.domain.email.presentation.dto.request; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@AllArgsConstructor -@NoArgsConstructor -public class EmailSendDto { - private String email; -} diff --git a/src/main/java/com/msg/gauth/domain/email/repository/EmailAuthRepository.java b/src/main/java/com/msg/gauth/domain/email/repository/EmailAuthRepository.java deleted file mode 100644 index 355f9ba0..00000000 --- a/src/main/java/com/msg/gauth/domain/email/repository/EmailAuthRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.msg.gauth.domain.email.repository; - -import com.msg.gauth.domain.email.EmailAuthEntity; -import org.springframework.data.repository.CrudRepository; - -public interface EmailAuthRepository extends CrudRepository { -} diff --git a/src/main/java/com/msg/gauth/domain/email/services/MailSendService.java b/src/main/java/com/msg/gauth/domain/email/services/MailSendService.java deleted file mode 100644 index 71175a86..00000000 --- a/src/main/java/com/msg/gauth/domain/email/services/MailSendService.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.msg.gauth.domain.email.services; - -import javax.mail.Message.RecipientType; -import com.msg.gauth.domain.email.EmailAuthEntity; -import com.msg.gauth.domain.email.exception.ManyRequestEmailAuthException; -import com.msg.gauth.domain.email.presentation.dto.request.EmailSendDto; -import com.msg.gauth.domain.email.repository.EmailAuthRepository; -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.exceptions.MessageSendFailException; -import lombok.RequiredArgsConstructor; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import javax.mail.MessagingException; -import javax.mail.internet.MimeMessage; -import java.util.UUID; - -@Service -@EnableAsync -@RequiredArgsConstructor -public class MailSendService { - private final JavaMailSender mailSender; - private final EmailAuthRepository emailAuthRepository; - - @Async - @Transactional - public void execute(EmailSendDto emailSendDto){ - String email = emailSendDto.getEmail(); - String value = UUID.randomUUID().toString(); - EmailAuthEntity authEntity = emailAuthRepository.findById(email) - .orElse(EmailAuthEntity.builder() - .authentication(false) - .randomValue(value) - .email(email) - .attemptCount(0) - .build()); - - if (authEntity.getAttemptCount() >= 3) - throw new ManyRequestEmailAuthException(); - authEntity.updateRandomValue(value); - authEntity.increaseAttemptCount(); - - emailAuthRepository.save(authEntity); - try{ - MimeMessage message = mailSender.createMimeMessage(); - String msg = "
"; - message.addRecipients(RecipientType.TO,emailSendDto.getEmail()); - message.setSubject("[Gauth] 이메일 인증"); - message.setText(msg, "utf-8", "html"); - mailSender.send(message); - }catch (MessagingException ex){ - throw new MessageSendFailException(ErrorCode.MAIL_SEND_FAIL); - } - } -} diff --git a/src/main/java/com/msg/gauth/domain/email/services/MailVerificationService.java b/src/main/java/com/msg/gauth/domain/email/services/MailVerificationService.java deleted file mode 100644 index b1174b9d..00000000 --- a/src/main/java/com/msg/gauth/domain/email/services/MailVerificationService.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.msg.gauth.domain.email.services; - -import com.msg.gauth.domain.email.EmailAuthEntity; -import com.msg.gauth.domain.email.exception.AuthCodeExpiredException; -import com.msg.gauth.domain.email.repository.EmailAuthRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Objects; - -@Service -@RequiredArgsConstructor -public class MailVerificationService { - private final EmailAuthRepository emailAuthRepository; - - @Transactional - public void execute(String email, String uuid) { - EmailAuthEntity emailAuth = emailAuthRepository.findById(email) - .orElseThrow(AuthCodeExpiredException::new); - if (!Objects.equals(emailAuth.getRandomValue(), uuid)) - throw new AuthCodeExpiredException(); - emailAuth.updateAuthentication(true); - emailAuthRepository.save(emailAuth); - } -} diff --git a/src/main/java/com/msg/gauth/domain/user/User.java b/src/main/java/com/msg/gauth/domain/user/User.java deleted file mode 100644 index 3ea61122..00000000 --- a/src/main/java/com/msg/gauth/domain/user/User.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.msg.gauth.domain.user; - -import com.msg.gauth.domain.user.enums.Gender; -import com.msg.gauth.domain.user.enums.UserRole; -import com.msg.gauth.domain.user.enums.UserState; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import javax.persistence.*; -import java.util.ArrayList; -import java.util.List; - -@Entity -@Builder -@AllArgsConstructor -@NoArgsConstructor -@Getter -public class User { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private Integer grade; - - private Integer classNum; - - private Integer num; - - @Column(unique = true) - private String email; - - @Column(length = 60) - private String password; - - @Enumerated(EnumType.STRING) - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "UserRole", joinColumns = @JoinColumn(name = "id")) - private List roles = new ArrayList<>(); - - @Enumerated(EnumType.STRING) - private UserState state; - - @Enumerated(EnumType.STRING) - private Gender gender; -} diff --git a/src/main/java/com/msg/gauth/domain/user/enums/Gender.java b/src/main/java/com/msg/gauth/domain/user/enums/Gender.java deleted file mode 100644 index 6617af6c..00000000 --- a/src/main/java/com/msg/gauth/domain/user/enums/Gender.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.msg.gauth.domain.user.enums; - -public enum Gender { - MALE, FEMALE -} diff --git a/src/main/java/com/msg/gauth/domain/user/enums/UserRole.java b/src/main/java/com/msg/gauth/domain/user/enums/UserRole.java deleted file mode 100644 index d5b85b59..00000000 --- a/src/main/java/com/msg/gauth/domain/user/enums/UserRole.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.msg.gauth.domain.user.enums; - -import org.springframework.security.core.GrantedAuthority; - -public enum UserRole implements GrantedAuthority { - ROLE_STUDENT, ROLE_TEACHER, ROLE_ADMIN; - - @Override - public String getAuthority() { - return name(); - } -} diff --git a/src/main/java/com/msg/gauth/domain/user/enums/UserState.java b/src/main/java/com/msg/gauth/domain/user/enums/UserState.java deleted file mode 100644 index 6502e938..00000000 --- a/src/main/java/com/msg/gauth/domain/user/enums/UserState.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.msg.gauth.domain.user.enums; - -public enum UserState { - PENDING, CREATED -} diff --git a/src/main/java/com/msg/gauth/domain/user/exception/EmailNotVerifiedException.java b/src/main/java/com/msg/gauth/domain/user/exception/EmailNotVerifiedException.java deleted file mode 100644 index 096bb6df..00000000 --- a/src/main/java/com/msg/gauth/domain/user/exception/EmailNotVerifiedException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.msg.gauth.domain.user.exception; - -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.exceptions.BasicException; - -public class EmailNotVerifiedException extends BasicException { - public EmailNotVerifiedException() { - super(ErrorCode.EMAIL_NOT_VERIFIED); - } -} diff --git a/src/main/java/com/msg/gauth/domain/user/exception/UserNotFoundException.java b/src/main/java/com/msg/gauth/domain/user/exception/UserNotFoundException.java deleted file mode 100644 index 5333cb55..00000000 --- a/src/main/java/com/msg/gauth/domain/user/exception/UserNotFoundException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.msg.gauth.domain.user.exception; - -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.exceptions.BasicException; - -public class UserNotFoundException extends BasicException { - public UserNotFoundException() { - super(ErrorCode.USER_NOT_FOUND); - } -} \ No newline at end of file diff --git a/src/main/java/com/msg/gauth/domain/user/presentation/dto/request/SignUpDto.java b/src/main/java/com/msg/gauth/domain/user/presentation/dto/request/SignUpDto.java deleted file mode 100644 index 97f26e13..00000000 --- a/src/main/java/com/msg/gauth/domain/user/presentation/dto/request/SignUpDto.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.msg.gauth.domain.user.presentation.dto.request; - -import com.msg.gauth.domain.user.User; -import com.msg.gauth.domain.user.enums.Gender; -import com.msg.gauth.domain.user.enums.UserRole; -import com.msg.gauth.domain.user.enums.UserState; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import javax.persistence.*; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; -import java.util.Collections; - -@Getter -@AllArgsConstructor @NoArgsConstructor -public class SignUpDto { - - @NotBlank - @Pattern(regexp = "^[a-zA-Z0-9]+@gsm.hs.kr$") - private String email; - - @NotBlank - @Size(min = 8, max = 72) - private String password; - - public User toEntity(String password){ - return User.builder() - .email(email) - .password(password) - .roles(Collections.singletonList(UserRole.ROLE_STUDENT)) - .state(UserState.PENDING) - .build(); - } -} diff --git a/src/main/java/com/msg/gauth/domain/user/presentation/dto/response/.gitkeep b/src/main/java/com/msg/gauth/domain/user/presentation/dto/response/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/com/msg/gauth/domain/user/repository/UserRepository.java b/src/main/java/com/msg/gauth/domain/user/repository/UserRepository.java deleted file mode 100644 index c8138d86..00000000 --- a/src/main/java/com/msg/gauth/domain/user/repository/UserRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.msg.gauth.domain.user.repository; - -import com.msg.gauth.domain.user.User; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.Optional; - -public interface UserRepository extends JpaRepository { - Optional findByEmail(String email); - Boolean existsByEmail(String email); -} diff --git a/src/main/java/com/msg/gauth/domain/user/services/.gitkeep b/src/main/java/com/msg/gauth/domain/user/services/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/com/msg/gauth/domain/user/utils/UserUtil.java b/src/main/java/com/msg/gauth/domain/user/utils/UserUtil.java deleted file mode 100644 index b9656453..00000000 --- a/src/main/java/com/msg/gauth/domain/user/utils/UserUtil.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.msg.gauth.domain.user.utils; - -import com.msg.gauth.domain.user.User; -import com.msg.gauth.domain.user.exception.UserNotFoundException; -import com.msg.gauth.domain.user.repository.UserRepository; -import com.msg.gauth.global.security.auth.AuthDetails; -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class UserUtil { - private final UserRepository userRepository; - - public User fetchCurrentUser() { - String email; - Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - if(principal instanceof UserDetails){ - email = ((AuthDetails) principal).getUsername(); - } else { - email = principal.toString(); - } - return fetchUserByEmail(email); - } - - public User fetchUserByEmail(String email) { - return userRepository.findByEmail(email) - .orElseThrow(UserNotFoundException::new); - } -} diff --git a/src/main/java/com/msg/gauth/global/annotation/logger/InjectionLogger.java b/src/main/java/com/msg/gauth/global/annotation/logger/InjectionLogger.java deleted file mode 100644 index d1a18230..00000000 --- a/src/main/java/com/msg/gauth/global/annotation/logger/InjectionLogger.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.msg.gauth.global.annotation.logger; - -import java.lang.reflect.Field; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.context.event.ApplicationStartedEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.stereotype.Component; - -@Component -public class InjectionLogger implements ApplicationListener { - - @Override - public void onApplicationEvent(ApplicationStartedEvent event) { - ConfigurableApplicationContext ac = event.getApplicationContext(); - String[] beanDefinitionNames = ac.getBeanDefinitionNames(); - for (String name : beanDefinitionNames) { - Object bean = ac.getBean(name); - String classPath = bean.getClass().getName(); - Logger log = LoggerFactory.getLogger(classPath); - try { - if(!classPath.contains("gauth")){ - continue; - } - Class beanClass = Class.forName(classPath); - Field[] fields = beanClass.getDeclaredFields(); - for (Field field : fields) { - log4k log4k = field.getDeclaredAnnotation(log4k.class); - if(log4k != null){ - field.setAccessible(true); - field.set(bean, log); - } - } - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - } -} diff --git a/src/main/java/com/msg/gauth/global/annotation/logger/log4k.java b/src/main/java/com/msg/gauth/global/annotation/logger/log4k.java deleted file mode 100644 index d0198a13..00000000 --- a/src/main/java/com/msg/gauth/global/annotation/logger/log4k.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.msg.gauth.global.annotation.logger; - - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface log4k { -} diff --git a/src/main/java/com/msg/gauth/global/exception/ErrorResponse.java b/src/main/java/com/msg/gauth/global/exception/ErrorResponse.java deleted file mode 100644 index 6606bf43..00000000 --- a/src/main/java/com/msg/gauth/global/exception/ErrorResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.msg.gauth.global.exception; - -import lombok.Getter; - -@Getter -public class ErrorResponse { - - private String msg; - private Integer code; - - public ErrorResponse(ErrorCode errorCode){ - this.msg=errorCode.getMsg(); - this.code=errorCode.getCode(); - } -} diff --git a/src/main/java/com/msg/gauth/global/exception/exceptions/BasicException.java b/src/main/java/com/msg/gauth/global/exception/exceptions/BasicException.java deleted file mode 100644 index 411ba51c..00000000 --- a/src/main/java/com/msg/gauth/global/exception/exceptions/BasicException.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.msg.gauth.global.exception.exceptions; - -import com.msg.gauth.global.exception.ErrorCode; -import lombok.Getter; - -@Getter -public class BasicException extends RuntimeException{ - - private ErrorCode errorCode; - - public BasicException(ErrorCode errorCode){ - super(errorCode.getMsg()); - this.errorCode=errorCode; - } -} diff --git a/src/main/java/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.java b/src/main/java/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.java deleted file mode 100644 index 3c64466a..00000000 --- a/src/main/java/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.msg.gauth.global.exception.exceptions; - -import com.msg.gauth.global.exception.ErrorCode; - -public class DuplicateEmailException extends BasicException{ - public DuplicateEmailException(ErrorCode errorCode){ - super(errorCode); - } -} diff --git a/src/main/java/com/msg/gauth/global/exception/exceptions/MessageSendFailException.java b/src/main/java/com/msg/gauth/global/exception/exceptions/MessageSendFailException.java deleted file mode 100644 index fbbcdf03..00000000 --- a/src/main/java/com/msg/gauth/global/exception/exceptions/MessageSendFailException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.msg.gauth.global.exception.exceptions; - -import com.msg.gauth.global.exception.ErrorCode; - -public class MessageSendFailException extends BasicException{ - public MessageSendFailException(ErrorCode errorCode) { - super(errorCode); - } -} diff --git a/src/main/java/com/msg/gauth/global/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/msg/gauth/global/exception/handler/GlobalExceptionHandler.java deleted file mode 100644 index b53f6cea..00000000 --- a/src/main/java/com/msg/gauth/global/exception/handler/GlobalExceptionHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.msg.gauth.global.exception.handler; - -import com.msg.gauth.global.annotation.logger.log4k; -import com.msg.gauth.global.exception.ErrorResponse; -import com.msg.gauth.global.exception.exceptions.BasicException; -import org.slf4j.Logger; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -import javax.servlet.http.HttpServletRequest; - -@RestControllerAdvice -public class GlobalExceptionHandler { - @log4k - Logger log; - - @ExceptionHandler(BasicException.class) - public ResponseEntity BasicExceptionHandler(HttpServletRequest request, BasicException ex){ - log.error(request.getRequestURI()); - log.error(ex.getMessage()); - ex.printStackTrace(); - ErrorResponse errorResponse = new ErrorResponse(ex.getErrorCode()); - return new ResponseEntity<>(errorResponse, HttpStatus.valueOf(ex.getErrorCode().getCode())); - } - -} diff --git a/src/main/java/com/msg/gauth/global/redis/RedisConfig.java b/src/main/java/com/msg/gauth/global/redis/RedisConfig.java deleted file mode 100644 index fcb37934..00000000 --- a/src/main/java/com/msg/gauth/global/redis/RedisConfig.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.msg.gauth.global.redis; - -import lombok.RequiredArgsConstructor; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.ZSetOperations; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.RedisSerializer; - -@Configuration -@RequiredArgsConstructor -public class RedisConfig { - private final RedisProperties redisProperties; - - @Bean - @ConditionalOnMissingBean(RedisConnectionFactory.class) - RedisConnectionFactory redisConnectionFactory() { - return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort()); - } - - @Bean - RedisTemplate redisTemplate() { - RedisTemplate redisTemplate = new RedisTemplate(); - redisTemplate.setConnectionFactory(redisConnectionFactory()); - redisTemplate.setKeySerializer(RedisSerializer.string()); - redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); - - return redisTemplate; - } - - @Bean - ZSetOperations zSetOperation() { - return redisTemplate().opsForZSet(); - } -} diff --git a/src/main/java/com/msg/gauth/global/redis/properties/RedisProperties.java b/src/main/java/com/msg/gauth/global/redis/properties/RedisProperties.java deleted file mode 100644 index 0a8d578a..00000000 --- a/src/main/java/com/msg/gauth/global/redis/properties/RedisProperties.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.msg.gauth.global.redis.properties; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.ConstructorBinding; - -@Getter -@ConstructorBinding -@RequiredArgsConstructor -@ConfigurationProperties(prefix = "spring.redis") -public class RedisProperties { - private final String host; - private final Integer port; -} diff --git a/src/main/java/com/msg/gauth/global/security/CustomAuthenticationEntryPoint.java b/src/main/java/com/msg/gauth/global/security/CustomAuthenticationEntryPoint.java deleted file mode 100644 index e1ef17d3..00000000 --- a/src/main/java/com/msg/gauth/global/security/CustomAuthenticationEntryPoint.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.msg.gauth.global.security; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.ErrorResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.http.MediaType; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -@Component -@RequiredArgsConstructor -public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { - private final ObjectMapper objectMapper; - - @Override - public void commence( - HttpServletRequest request, - HttpServletResponse response, - AuthenticationException authException - ) throws IOException, ServletException { - ErrorCode errorCode = ErrorCode.UNAUTHORIZED; - String responseString = objectMapper.writeValueAsString(new ErrorResponse(errorCode)); - - response.setStatus(errorCode.getCode()); - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - response.getWriter().write(responseString); - } -} diff --git a/src/main/java/com/msg/gauth/global/security/SecurityConfig.java b/src/main/java/com/msg/gauth/global/security/SecurityConfig.java deleted file mode 100644 index 233f9553..00000000 --- a/src/main/java/com/msg/gauth/global/security/SecurityConfig.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.msg.gauth.global.security; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.msg.gauth.global.security.config.FilterConfig; -import com.msg.gauth.global.security.jwt.JwtTokenProvider; -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.web.cors.CorsUtils; - -@Configuration -@EnableWebSecurity -@RequiredArgsConstructor -public class SecurityConfig { - private final JwtTokenProvider jwtTokenProvider; - private final ObjectMapper objectMapper; - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - return http - .cors().and() - .csrf().disable() - .formLogin().disable() - .httpBasic().disable() - - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - - .and() - .authorizeRequests() - .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() - - .and() - .exceptionHandling() - .authenticationEntryPoint(new CustomAuthenticationEntryPoint(objectMapper)) - - .and() - .apply(new FilterConfig(jwtTokenProvider, objectMapper)) - - .and() - .build(); - } - - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } -} diff --git a/src/main/java/com/msg/gauth/global/security/auth/AuthDetails.java b/src/main/java/com/msg/gauth/global/security/auth/AuthDetails.java deleted file mode 100644 index 9fc2ed06..00000000 --- a/src/main/java/com/msg/gauth/global/security/auth/AuthDetails.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.msg.gauth.global.security.auth; - -import com.msg.gauth.domain.user.User; -import com.msg.gauth.domain.user.enums.UserState; -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; - -import java.util.Collection; - -@RequiredArgsConstructor -public class AuthDetails implements UserDetails { - private final User user; - - - @Override - public Collection getAuthorities() { - return user.getRoles(); - } - - @Override - public String getPassword() { - return null; - } - - @Override - public String getUsername() { - return user.getEmail(); - } - - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return user.getState().equals(UserState.CREATED); - } - - @Override - public boolean isCredentialsNonExpired() { - return true; - } - - @Override - public boolean isEnabled() { - return isAccountNonExpired() && isAccountNonLocked() && isCredentialsNonExpired(); - } -} diff --git a/src/main/java/com/msg/gauth/global/security/auth/AuthDetailsService.java b/src/main/java/com/msg/gauth/global/security/auth/AuthDetailsService.java deleted file mode 100644 index de0a62b0..00000000 --- a/src/main/java/com/msg/gauth/global/security/auth/AuthDetailsService.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.msg.gauth.global.security.auth; - -import com.msg.gauth.domain.user.exception.UserNotFoundException; -import com.msg.gauth.domain.user.repository.UserRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - - -@Service -@Transactional(readOnly = true) -@RequiredArgsConstructor -public class AuthDetailsService implements UserDetailsService { - private final UserRepository userRepository; - - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - return userRepository.findByEmail(username) - .map(AuthDetails::new) - .orElseThrow(UserNotFoundException::new); - } -} diff --git a/src/main/java/com/msg/gauth/global/security/config/FilterConfig.java b/src/main/java/com/msg/gauth/global/security/config/FilterConfig.java deleted file mode 100644 index 6c4f2ff8..00000000 --- a/src/main/java/com/msg/gauth/global/security/config/FilterConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.msg.gauth.global.security.config; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.msg.gauth.global.security.filter.ExceptionFilter; -import com.msg.gauth.global.security.filter.JwtTokenFilter; -import com.msg.gauth.global.security.jwt.JwtTokenProvider; -import lombok.RequiredArgsConstructor; -import org.springframework.security.config.annotation.SecurityConfigurerAdapter; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.web.DefaultSecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -@RequiredArgsConstructor -public class FilterConfig extends SecurityConfigurerAdapter { - private final JwtTokenProvider jwtTokenProvider; - private final ObjectMapper objectMapper; - - @Override - public void configure(HttpSecurity builder) { - JwtTokenFilter jwtTokenFilter = new JwtTokenFilter(jwtTokenProvider); - ExceptionFilter exceptionFilter = new ExceptionFilter(objectMapper); - - builder.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class); - builder.addFilterBefore(exceptionFilter, UsernamePasswordAuthenticationFilter.class); - } -} diff --git a/src/main/java/com/msg/gauth/global/security/exception/ExpiredTokenException.java b/src/main/java/com/msg/gauth/global/security/exception/ExpiredTokenException.java deleted file mode 100644 index 43b2d3e1..00000000 --- a/src/main/java/com/msg/gauth/global/security/exception/ExpiredTokenException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.msg.gauth.global.security.exception; - -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.exceptions.BasicException; - -public class ExpiredTokenException extends BasicException { - - public ExpiredTokenException() { - super(ErrorCode.EXPIRED_TOKEN); - } -} diff --git a/src/main/java/com/msg/gauth/global/security/exception/InvalidTokenException.java b/src/main/java/com/msg/gauth/global/security/exception/InvalidTokenException.java deleted file mode 100644 index 5cf40aa2..00000000 --- a/src/main/java/com/msg/gauth/global/security/exception/InvalidTokenException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.msg.gauth.global.security.exception; - -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.exceptions.BasicException; - -public class InvalidTokenException extends BasicException { - public InvalidTokenException() { - super(ErrorCode.INVALID_TOKEN); - } -} diff --git a/src/main/java/com/msg/gauth/global/security/filter/ExceptionFilter.java b/src/main/java/com/msg/gauth/global/security/filter/ExceptionFilter.java deleted file mode 100644 index 4a0aa320..00000000 --- a/src/main/java/com/msg/gauth/global/security/filter/ExceptionFilter.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.msg.gauth.global.security.filter; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.msg.gauth.global.exception.ErrorCode; -import com.msg.gauth.global.exception.ErrorResponse; -import com.msg.gauth.global.exception.exceptions.BasicException; -import lombok.RequiredArgsConstructor; -import org.springframework.http.MediaType; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -@RequiredArgsConstructor -public class ExceptionFilter extends OncePerRequestFilter { - private final ObjectMapper objectMapper; - - @Override - protected void doFilterInternal( - HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain - ) throws ServletException, IOException { - try { - filterChain.doFilter(request, response); - } catch (BasicException e) { - sendError(response, e.getErrorCode()); - } catch (Exception e) { - sendError(response, ErrorCode.INTERNAL_SERVER_ERROR); - } - } - - private void sendError(HttpServletResponse res, ErrorCode errorCode) throws IOException { - ErrorResponse errorResponse = new ErrorResponse(errorCode); - String responseString = objectMapper.writeValueAsString(errorResponse); - - res.setStatus(errorCode.getCode()); - res.setContentType(MediaType.APPLICATION_JSON_VALUE); - res.getWriter().write(responseString); - } -} diff --git a/src/main/java/com/msg/gauth/global/security/filter/JwtTokenFilter.java b/src/main/java/com/msg/gauth/global/security/filter/JwtTokenFilter.java deleted file mode 100644 index 8a7117b3..00000000 --- a/src/main/java/com/msg/gauth/global/security/filter/JwtTokenFilter.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.msg.gauth.global.security.filter; - -import com.msg.gauth.global.security.jwt.JwtTokenProvider; -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -@RequiredArgsConstructor -public class JwtTokenFilter extends OncePerRequestFilter { - private final JwtTokenProvider jwtTokenProvider; - - @Override - protected void doFilterInternal( - HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain - ) throws ServletException, IOException { - String token = jwtTokenProvider.resolveToken(request); - if (token != null) { - Authentication authentication = jwtTokenProvider.authentication(token); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - filterChain.doFilter(request, response); - } -} diff --git a/src/main/java/com/msg/gauth/global/security/jwt/JwtProperties.java b/src/main/java/com/msg/gauth/global/security/jwt/JwtProperties.java deleted file mode 100644 index 9391511b..00000000 --- a/src/main/java/com/msg/gauth/global/security/jwt/JwtProperties.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.msg.gauth.global.security.jwt; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.ConstructorBinding; - -@Getter -@ConstructorBinding -@ConfigurationProperties(prefix = "jwt") -@RequiredArgsConstructor -public class JwtProperties { - private final String accessSecret; - private final String refreshSecret; -} diff --git a/src/main/java/com/msg/gauth/global/security/jwt/JwtTokenProvider.java b/src/main/java/com/msg/gauth/global/security/jwt/JwtTokenProvider.java deleted file mode 100644 index ba72d3b3..00000000 --- a/src/main/java/com/msg/gauth/global/security/jwt/JwtTokenProvider.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.msg.gauth.global.security.jwt; - -import com.msg.gauth.global.security.auth.AuthDetailsService; -import com.msg.gauth.global.security.exception.ExpiredTokenException; -import com.msg.gauth.global.security.exception.InvalidTokenException; -import io.jsonwebtoken.*; -import lombok.RequiredArgsConstructor; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; -import java.time.ZonedDateTime; -import java.util.Date; - -@Component -@RequiredArgsConstructor -public class JwtTokenProvider { - private final JwtProperties jwtProperties; - private final AuthDetailsService authDetailsService; - - static final String ACCESS_TYPE = "access"; - static final String REFRESH_TYPE = "refresh"; - static final Long ACCESS_EXP = 60L * 15; // 15 min - static final Long REFRESH_EXP = 60L * 60 * 24 * 7; // 1 weeks - static final String TOKEN_PREFIX = "Bearer "; - - public String generateAccessToken(String email) { - return generateToken(email, ACCESS_TYPE, jwtProperties.getAccessSecret(), ACCESS_EXP); - } - - public String generateRefreshToken(String email) { - return generateToken(email, REFRESH_TYPE, jwtProperties.getRefreshSecret(), REFRESH_EXP); - } - - public String resolveToken(HttpServletRequest req) { - String token = req.getHeader("Authorization"); - return parseToken(token); - } - - public String exactEmailFromRefreshToken(String refresh) { - return getTokenSubject(refresh, jwtProperties.getRefreshSecret()); - } - - public Authentication authentication(String token) { - UserDetails userDetails = authDetailsService.loadUserByUsername(getTokenSubject(token, jwtProperties.getAccessSecret())); - return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); - } - - public String parseToken(String token) { - if (token != null && token.startsWith(TOKEN_PREFIX)) - return token.replace(TOKEN_PREFIX, ""); - return null; - } - - public ZonedDateTime getAccessExpiredTime() { - return ZonedDateTime.now().plusSeconds(ACCESS_EXP); - } - - public Long getRefreshTimeToLive() { - return REFRESH_EXP; - } - - public String generateToken(String sub, String type, String secret, Long exp) { - return Jwts.builder() - .signWith(SignatureAlgorithm.HS256, secret) - .setSubject(sub) - .claim("type", type) - .setIssuedAt(new Date()) - .setExpiration(new Date(System.currentTimeMillis() + exp * 1000)) - .compact(); - } - - private Claims getTokenBody(String token, String secret) { - try { - return Jwts.parser() - .setSigningKey(secret) - .parseClaimsJws(token) - .getBody(); - } catch (ExpiredJwtException e) { - throw new ExpiredTokenException(); - } catch (Exception e) { - throw new InvalidTokenException(); - } - } - - private String getTokenSubject(String token, String secret) { - return getTokenBody(token, secret).getSubject(); - } -} diff --git a/src/main/kotlin/com/msg/gauth/GauthBackendApplication.kt b/src/main/kotlin/com/msg/gauth/GauthBackendApplication.kt new file mode 100644 index 00000000..89fbc00f --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/GauthBackendApplication.kt @@ -0,0 +1,13 @@ +package com.msg.gauth + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.context.properties.ConfigurationPropertiesScan +import org.springframework.boot.runApplication + +@SpringBootApplication +@ConfigurationPropertiesScan +class GauthBackendApplication + +fun main(args: Array) { + runApplication(*args) +} diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/RefreshToken.kt b/src/main/kotlin/com/msg/gauth/domain/auth/RefreshToken.kt new file mode 100644 index 00000000..0ade40bb --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/RefreshToken.kt @@ -0,0 +1,23 @@ +package com.msg.gauth.domain.auth + +import org.springframework.data.annotation.Id +import org.springframework.data.redis.core.RedisHash +import org.springframework.data.redis.core.TimeToLive +import org.springframework.data.redis.core.index.Indexed + +@RedisHash +class RefreshToken( + @Id + val userId: Long, + + @Indexed + var token: String, + + @TimeToLive + var timeToLive: Long +) { + fun updateToken(token: String, timeToLive: Long) { + this.token = token + this.timeToLive = timeToLive + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/exception/ExpiredRefreshTokenException.kt b/src/main/kotlin/com/msg/gauth/domain/auth/exception/ExpiredRefreshTokenException.kt new file mode 100644 index 00000000..da7c509b --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/exception/ExpiredRefreshTokenException.kt @@ -0,0 +1,6 @@ +package com.msg.gauth.domain.auth.exception + +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.exceptions.BasicException + +class ExpiredRefreshTokenException: BasicException(ErrorCode.EXPIRED_REFRESH_TOKEN) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/exception/InvalidRefreshTokenException.kt b/src/main/kotlin/com/msg/gauth/domain/auth/exception/InvalidRefreshTokenException.kt new file mode 100644 index 00000000..33117801 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/exception/InvalidRefreshTokenException.kt @@ -0,0 +1,6 @@ +package com.msg.gauth.domain.auth.exception + +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.exceptions.BasicException + +class InvalidRefreshTokenException: BasicException(ErrorCode.INVALID_REFRESH_TOKEN) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/exception/PasswordMismatchException.kt b/src/main/kotlin/com/msg/gauth/domain/auth/exception/PasswordMismatchException.kt new file mode 100644 index 00000000..0f13e25d --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/exception/PasswordMismatchException.kt @@ -0,0 +1,6 @@ +package com.msg.gauth.domain.auth.exception + +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.exceptions.BasicException + +class PasswordMismatchException: BasicException(ErrorCode.PASSWORD_MISMATCH) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/presentation/AuthController.kt b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/AuthController.kt new file mode 100644 index 00000000..3cd92ab7 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/AuthController.kt @@ -0,0 +1,46 @@ +package com.msg.gauth.domain.auth.presentation + +import com.msg.gauth.domain.auth.presentation.dto.request.SignUpDto +import com.msg.gauth.domain.auth.presentation.dto.request.SigninRequestDto +import com.msg.gauth.domain.auth.presentation.dto.response.RefreshResponseDto +import com.msg.gauth.domain.auth.presentation.dto.response.SigninResponseDto +import com.msg.gauth.domain.auth.services.LogoutService +import com.msg.gauth.domain.auth.services.RefreshService +import com.msg.gauth.domain.auth.services.SignUpService +import com.msg.gauth.domain.auth.services.SignInService +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* +import javax.validation.Valid + +@RestController +@RequestMapping("/auth") +class AuthController( + private val refreshService: RefreshService, + private val logoutService: LogoutService, + private val signInService: SignInService, + private val signUpService: SignUpService +) { + + @PatchMapping + fun refresh(@RequestHeader refreshToken: String): ResponseEntity { + return ResponseEntity.ok(refreshService.execute(refreshToken)) + } + + @DeleteMapping + fun logout(): ResponseEntity { + logoutService.execute() + return ResponseEntity.noContent().build() + } + + @PostMapping + fun signin(@Valid @RequestBody signinRequestDto: SigninRequestDto): ResponseEntity { + return ResponseEntity.ok(signInService.execute(signinRequestDto)) + } + + @PostMapping("/signup") + fun signUpMember(@Valid @RequestBody signUpDto: SignUpDto): ResponseEntity { + signUpService.execute(signUpDto) + return ResponseEntity(HttpStatus.CREATED) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/request/SigninRequestDto.kt b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/request/SigninRequestDto.kt new file mode 100644 index 00000000..a21e5957 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/request/SigninRequestDto.kt @@ -0,0 +1,13 @@ +package com.msg.gauth.domain.auth.presentation.dto.request + +import javax.validation.constraints.NotBlank +import javax.validation.constraints.Pattern + +data class SigninRequestDto( + @field:NotBlank + @field:Pattern(regexp = "^[a-zA-Z0-9]+@gsm.hs.kr$") + val email: String, + + @field:NotBlank + val password: String +) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/request/SignupRequestDto.kt b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/request/SignupRequestDto.kt new file mode 100644 index 00000000..e6a1fa3c --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/request/SignupRequestDto.kt @@ -0,0 +1,26 @@ +package com.msg.gauth.domain.auth.presentation.dto.request + +import com.msg.gauth.domain.user.User +import com.msg.gauth.domain.user.enums.UserRole +import com.msg.gauth.domain.user.enums.UserState +import javax.validation.constraints.NotBlank +import javax.validation.constraints.Pattern +import javax.validation.constraints.Size + +data class SignUpDto( + @field:NotBlank + @field:Pattern(regexp = "^[a-zA-Z0-9]+@gsm.hs.kr$") + val email: String, + + @field:NotBlank + @field:Size(min = 8, max = 72) + val password: String +) { + fun toEntity(password: String): User = + User( + email = email, + password = password, + roles = mutableListOf(UserRole.ROLE_STUDENT), + state = UserState.PENDING + ) +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/response/RefreshResponseDto.kt b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/response/RefreshResponseDto.kt new file mode 100644 index 00000000..f59757fd --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/response/RefreshResponseDto.kt @@ -0,0 +1,16 @@ +package com.msg.gauth.domain.auth.presentation.dto.response + +import com.fasterxml.jackson.annotation.JsonFormat +import com.fasterxml.jackson.annotation.JsonProperty +import java.time.ZonedDateTime + +data class RefreshResponseDto( + @JsonProperty + val accessToken: String, + + @JsonProperty + val refreshToken: String, + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + val expiresAt: ZonedDateTime +) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/response/SigninResponseDto.kt b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/response/SigninResponseDto.kt new file mode 100644 index 00000000..dbd96e60 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/dto/response/SigninResponseDto.kt @@ -0,0 +1,16 @@ +package com.msg.gauth.domain.auth.presentation.dto.response + +import com.fasterxml.jackson.annotation.JsonFormat +import com.fasterxml.jackson.annotation.JsonProperty +import java.time.ZonedDateTime + +data class SigninResponseDto( + @JsonProperty + val accessToken: String, + + @JsonProperty + val refreshToken: String, + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + val expiresAt: ZonedDateTime +) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/repository/RefreshTokenRepository.kt b/src/main/kotlin/com/msg/gauth/domain/auth/repository/RefreshTokenRepository.kt new file mode 100644 index 00000000..ff345751 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/repository/RefreshTokenRepository.kt @@ -0,0 +1,8 @@ +package com.msg.gauth.domain.auth.repository + +import com.msg.gauth.domain.auth.RefreshToken +import org.springframework.data.repository.CrudRepository + +interface RefreshTokenRepository: CrudRepository { + fun findByUserId(userId: Long): RefreshToken? +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/services/LogoutService.kt b/src/main/kotlin/com/msg/gauth/domain/auth/services/LogoutService.kt new file mode 100644 index 00000000..66117b79 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/services/LogoutService.kt @@ -0,0 +1,19 @@ +package com.msg.gauth.domain.auth.services + +import com.msg.gauth.domain.auth.RefreshToken +import com.msg.gauth.domain.auth.repository.RefreshTokenRepository +import com.msg.gauth.domain.user.utils.UserUtil +import org.springframework.stereotype.Service + +@Service +class LogoutService( + private val userUtil: UserUtil, + private val refreshTokenRepository: RefreshTokenRepository +) { + fun execute() { + val currentUser = userUtil.fetchCurrentUser() + refreshTokenRepository.findByUserId(currentUser.id)?.let { + refreshTokenRepository.delete(it) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/services/RefreshService.kt b/src/main/kotlin/com/msg/gauth/domain/auth/services/RefreshService.kt new file mode 100644 index 00000000..a0797c30 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/services/RefreshService.kt @@ -0,0 +1,36 @@ +package com.msg.gauth.domain.auth.services + +import com.msg.gauth.domain.auth.exception.ExpiredRefreshTokenException +import com.msg.gauth.domain.auth.exception.InvalidRefreshTokenException +import com.msg.gauth.domain.auth.presentation.dto.response.RefreshResponseDto +import com.msg.gauth.domain.auth.repository.RefreshTokenRepository +import com.msg.gauth.domain.user.User +import com.msg.gauth.domain.user.exception.UserNotFoundException +import com.msg.gauth.domain.user.repository.UserRepository +import com.msg.gauth.global.security.jwt.JwtTokenProvider +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class RefreshService( + private val jwtTokenProvider: JwtTokenProvider, + private val userRepository: UserRepository, + private val refreshTokenRepository: RefreshTokenRepository +) { + @Transactional + fun execute(refreshToken: String): RefreshResponseDto { + val email = jwtTokenProvider.exactEmailFromRefreshToken(refreshToken!!) + val user: User = userRepository.findByEmail(email) ?: throw UserNotFoundException() + val redisRefreshToken = refreshTokenRepository.findById(user.id) + .orElseThrow { ExpiredRefreshTokenException() } + if (redisRefreshToken.token != refreshToken) + throw InvalidRefreshTokenException() + + val access = jwtTokenProvider.generateAccessToken(email) + val refresh = jwtTokenProvider.generateRefreshToken(email) + val expiresAt = jwtTokenProvider.accessExpiredTime + redisRefreshToken.updateToken(refresh, JwtTokenProvider.REFRESH_EXP) + refreshTokenRepository.save(redisRefreshToken) + return RefreshResponseDto(access, refresh, expiresAt) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/services/SignInService.kt b/src/main/kotlin/com/msg/gauth/domain/auth/services/SignInService.kt new file mode 100644 index 00000000..355605ee --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/services/SignInService.kt @@ -0,0 +1,36 @@ +package com.msg.gauth.domain.auth.services + +import com.msg.gauth.domain.auth.RefreshToken +import com.msg.gauth.domain.auth.exception.PasswordMismatchException +import com.msg.gauth.domain.auth.presentation.dto.request.SigninRequestDto +import com.msg.gauth.domain.auth.presentation.dto.response.SigninResponseDto +import com.msg.gauth.domain.auth.repository.RefreshTokenRepository +import com.msg.gauth.domain.user.User +import com.msg.gauth.domain.user.exception.UserNotFoundException +import com.msg.gauth.domain.user.repository.UserRepository +import com.msg.gauth.global.security.jwt.JwtTokenProvider +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class SignInService( + private val jwtTokenProvider: JwtTokenProvider, + private val userRepository: UserRepository, + private val refreshTokenRepository: RefreshTokenRepository, + private val passwordEncoder: PasswordEncoder +) { + @Transactional + fun execute(dto: SigninRequestDto): SigninResponseDto { + val user: User = userRepository.findByEmail(dto.email) ?: throw UserNotFoundException() + if (!passwordEncoder.matches(dto.password, user.password)) + throw PasswordMismatchException() + + val access = jwtTokenProvider.generateAccessToken(dto.email) + val refresh = jwtTokenProvider.generateRefreshToken(dto.email) + val expiresAt = jwtTokenProvider.accessExpiredTime + refreshTokenRepository.save(RefreshToken(user.id, refresh, JwtTokenProvider.REFRESH_EXP)) + + return SigninResponseDto(access, refresh, expiresAt) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/services/SignUpService.kt b/src/main/kotlin/com/msg/gauth/domain/auth/services/SignUpService.kt new file mode 100644 index 00000000..436a9f85 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/auth/services/SignUpService.kt @@ -0,0 +1,31 @@ +package com.msg.gauth.domain.auth.services + +import com.msg.gauth.domain.auth.presentation.dto.request.SignUpDto +import com.msg.gauth.domain.email.repository.EmailAuthRepository +import com.msg.gauth.domain.user.User +import com.msg.gauth.domain.user.exception.EmailNotVerifiedException +import com.msg.gauth.domain.user.repository.UserRepository +import com.msg.gauth.global.exception.exceptions.DuplicateEmailException +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class SignUpService( + private val userRepository: UserRepository, + private val passwordEncoder: PasswordEncoder, + private val emailAuthRepository: EmailAuthRepository +) { + @Transactional + fun execute(signUpDto: SignUpDto): Long { + if (userRepository.existsByEmail(signUpDto.email)) { + throw DuplicateEmailException() + } + val password: String = signUpDto.password + val user: User = signUpDto.toEntity(passwordEncoder.encode(password)) + val emailAuth = emailAuthRepository.findById(signUpDto.email) + .orElseThrow { EmailNotVerifiedException() } + if (!emailAuth.authentication) throw EmailNotVerifiedException() + return userRepository.save(user).id + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/client/Client.kt b/src/main/kotlin/com/msg/gauth/domain/client/Client.kt new file mode 100644 index 00000000..8461ebc5 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/client/Client.kt @@ -0,0 +1,18 @@ +package com.msg.gauth.domain.client + +import com.msg.gauth.domain.user.User +import com.msg.gauth.global.entity.BaseIdEntity +import javax.persistence.* + +@Entity +class Client( + val clientId: String, + val clientSecret: String, + val redirectUri: String, + val serviceName: String, + val serviceUri: String, + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + val createdBy: User +): BaseIdEntity() \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/email/EmailAuthEntity.kt b/src/main/kotlin/com/msg/gauth/domain/email/EmailAuthEntity.kt new file mode 100644 index 00000000..6e69a3d8 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/email/EmailAuthEntity.kt @@ -0,0 +1,32 @@ +package com.msg.gauth.domain.email + +import org.hibernate.annotations.ColumnDefault +import org.hibernate.validator.constraints.Length +import org.springframework.data.annotation.Id +import org.springframework.data.redis.core.RedisHash + +@RedisHash(value = "emailAuth", timeToLive = 60 * 15) +class EmailAuthEntity( + @Id + val email: String, + + var randomValue: @Length(max = 36) String, + + + var authentication: Boolean, + + @ColumnDefault("1") + var attemptCount: Int +) { + fun updateAuthentication(authentication: Boolean) { + this.authentication = authentication + } + + fun updateRandomValue(uuid: String) { + randomValue = uuid + } + + fun increaseAttemptCount() { + attemptCount += 1 + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/email/exception/AuthCodeExpiredException.kt b/src/main/kotlin/com/msg/gauth/domain/email/exception/AuthCodeExpiredException.kt new file mode 100644 index 00000000..7e6d26d7 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/email/exception/AuthCodeExpiredException.kt @@ -0,0 +1,6 @@ +package com.msg.gauth.domain.email.exception + +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.exceptions.BasicException + +class AuthCodeExpiredException: BasicException(ErrorCode.AUTH_CODE_EXPIRED) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/email/exception/ManyRequestEmailAuthException.kt b/src/main/kotlin/com/msg/gauth/domain/email/exception/ManyRequestEmailAuthException.kt new file mode 100644 index 00000000..ce5caac5 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/email/exception/ManyRequestEmailAuthException.kt @@ -0,0 +1,6 @@ +package com.msg.gauth.domain.email.exception + +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.exceptions.BasicException + +class ManyRequestEmailAuthException: BasicException(ErrorCode.MANY_REQUEST_EMAIL_AUTH) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/email/presentation/EmailAuthController.kt b/src/main/kotlin/com/msg/gauth/domain/email/presentation/EmailAuthController.kt new file mode 100644 index 00000000..93e3a3d2 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/email/presentation/EmailAuthController.kt @@ -0,0 +1,26 @@ +package com.msg.gauth.domain.email.presentation + +import com.msg.gauth.domain.email.presentation.dto.EmailSendDto +import com.msg.gauth.domain.email.services.MailSendService +import com.msg.gauth.domain.email.services.MailVerificationService +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping("/email") +class EmailAuthController( + private val mailSendService: MailSendService, + private val mailVerificationService: MailVerificationService +) { + @PostMapping + fun emailSend(@RequestBody emailSendDto: EmailSendDto): ResponseEntity { + mailSendService.execute(emailSendDto) + return ResponseEntity.noContent().build() + } + + @GetMapping("/authentication") + fun emailVerification(@RequestParam email: String, @RequestParam uuid: String): ResponseEntity { + mailVerificationService.execute(email, uuid) + return ResponseEntity.noContent().build() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/email/presentation/dto/EmailSendDto.kt b/src/main/kotlin/com/msg/gauth/domain/email/presentation/dto/EmailSendDto.kt new file mode 100644 index 00000000..b625c0ef --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/email/presentation/dto/EmailSendDto.kt @@ -0,0 +1,3 @@ +package com.msg.gauth.domain.email.presentation.dto + +data class EmailSendDto(val email: String) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/email/repository/EmailAuthRepository.kt b/src/main/kotlin/com/msg/gauth/domain/email/repository/EmailAuthRepository.kt new file mode 100644 index 00000000..0e99eda7 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/email/repository/EmailAuthRepository.kt @@ -0,0 +1,6 @@ +package com.msg.gauth.domain.email.repository + +import com.msg.gauth.domain.email.EmailAuthEntity +import org.springframework.data.repository.CrudRepository + +interface EmailAuthRepository: CrudRepository \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/email/services/MailSendService.kt b/src/main/kotlin/com/msg/gauth/domain/email/services/MailSendService.kt new file mode 100644 index 00000000..557ca36d --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/email/services/MailSendService.kt @@ -0,0 +1,56 @@ +package com.msg.gauth.domain.email.services + +import com.msg.gauth.domain.email.EmailAuthEntity +import com.msg.gauth.domain.email.exception.ManyRequestEmailAuthException +import com.msg.gauth.domain.email.presentation.dto.EmailSendDto +import com.msg.gauth.domain.email.repository.EmailAuthRepository +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.exceptions.MessageSendFailException +import org.springframework.mail.javamail.JavaMailSender +import org.springframework.mail.javamail.JavaMailSenderImpl +import org.springframework.scheduling.annotation.Async +import org.springframework.scheduling.annotation.EnableAsync +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.util.* +import javax.mail.Message +import javax.mail.MessagingException + +@Service +@EnableAsync +class MailSendService( + private val mailSender: JavaMailSender, + private val emailAuthRepository: EmailAuthRepository +) { + + @Async + @Transactional + fun execute(emailSendDto: EmailSendDto) { + val email: String = emailSendDto.email + val value = UUID.randomUUID().toString() + val authEntity = emailAuthRepository.findById(email) + .orElse( + EmailAuthEntity( + authentication = false, + randomValue = value, + email = email, + attemptCount = 0 + ) + ) + if (authEntity.attemptCount >= 3) throw ManyRequestEmailAuthException() + authEntity.updateRandomValue(value) + authEntity.increaseAttemptCount() + emailAuthRepository.save(authEntity) + try { + val message = mailSender.createMimeMessage() + val msg = + "
" + message.addRecipients(Message.RecipientType.TO, emailSendDto.email) + message.subject = "[Gauth] 이메일 인증" + message.setText(msg, "utf-8", "html") + mailSender.send(message) + } catch (ex: MessagingException) { + throw MessageSendFailException() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/email/services/MailVerificationService.kt b/src/main/kotlin/com/msg/gauth/domain/email/services/MailVerificationService.kt new file mode 100644 index 00000000..995d3ed2 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/email/services/MailVerificationService.kt @@ -0,0 +1,21 @@ +package com.msg.gauth.domain.email.services + +import com.msg.gauth.domain.email.exception.AuthCodeExpiredException +import com.msg.gauth.domain.email.repository.EmailAuthRepository +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class MailVerificationService( + private val emailAuthRepository: EmailAuthRepository +) { + + @Transactional + fun execute(email: String, uuid: String) { + val emailAuth = emailAuthRepository.findById(email) + .orElseThrow { AuthCodeExpiredException() } + if (emailAuth.randomValue != uuid) throw AuthCodeExpiredException() + emailAuth.updateAuthentication(true) + emailAuthRepository.save(emailAuth) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/user/User.kt b/src/main/kotlin/com/msg/gauth/domain/user/User.kt new file mode 100644 index 00000000..b149678e --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/user/User.kt @@ -0,0 +1,37 @@ +package com.msg.gauth.domain.user + +import com.msg.gauth.domain.user.enums.Gender +import com.msg.gauth.domain.user.enums.UserRole +import com.msg.gauth.domain.user.enums.UserState +import com.msg.gauth.global.entity.BaseIdEntity +import javax.persistence.* +import javax.validation.constraints.Size + +@Entity +class User( + @Column(nullable = true) + var grade: Int? = null, + + @Column(nullable = true) + var classNum: Int? = null, + + @Column(nullable = true) + var num: Int? = null, + + @Column(unique = true) + val email: String, + + @field:Size(max = 60) + var password: String, + + @Enumerated(EnumType.STRING) + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "UserRole", joinColumns = [JoinColumn(name = "id")]) + var roles: MutableList = mutableListOf(), + + @Enumerated(EnumType.STRING) + var state: UserState, + + @Enumerated(EnumType.STRING) + var gender: Gender? = null +): BaseIdEntity() \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/user/enums/Gender.kt b/src/main/kotlin/com/msg/gauth/domain/user/enums/Gender.kt new file mode 100644 index 00000000..ceb6f9e5 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/user/enums/Gender.kt @@ -0,0 +1,5 @@ +package com.msg.gauth.domain.user.enums + +enum class Gender { + MALE, FEMALE +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/user/enums/UserRole.kt b/src/main/kotlin/com/msg/gauth/domain/user/enums/UserRole.kt new file mode 100644 index 00000000..74b46f90 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/user/enums/UserRole.kt @@ -0,0 +1,10 @@ +package com.msg.gauth.domain.user.enums + +import org.springframework.security.core.GrantedAuthority + +enum class UserRole : GrantedAuthority { + ROLE_STUDENT, ROLE_TEACHER, ROLE_ADMIN; + + override fun getAuthority(): String = + name +} diff --git a/src/main/kotlin/com/msg/gauth/domain/user/enums/UserState.kt b/src/main/kotlin/com/msg/gauth/domain/user/enums/UserState.kt new file mode 100644 index 00000000..525548f1 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/user/enums/UserState.kt @@ -0,0 +1,5 @@ +package com.msg.gauth.domain.user.enums + +enum class UserState { + PENDING, CREATED +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/user/exception/EmailNotVerifiedException.kt b/src/main/kotlin/com/msg/gauth/domain/user/exception/EmailNotVerifiedException.kt new file mode 100644 index 00000000..fe7aeea0 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/user/exception/EmailNotVerifiedException.kt @@ -0,0 +1,6 @@ +package com.msg.gauth.domain.user.exception + +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.exceptions.BasicException + +class EmailNotVerifiedException: BasicException(ErrorCode.EMAIL_NOT_VERIFIED) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/user/exception/UserNotFoundException.kt b/src/main/kotlin/com/msg/gauth/domain/user/exception/UserNotFoundException.kt new file mode 100644 index 00000000..a5359213 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/user/exception/UserNotFoundException.kt @@ -0,0 +1,6 @@ +package com.msg.gauth.domain.user.exception + +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.exceptions.BasicException + +class UserNotFoundException: BasicException(ErrorCode.USER_NOT_FOUND) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/user/repository/UserRepository.kt b/src/main/kotlin/com/msg/gauth/domain/user/repository/UserRepository.kt new file mode 100644 index 00000000..ed5ed24b --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/user/repository/UserRepository.kt @@ -0,0 +1,9 @@ +package com.msg.gauth.domain.user.repository + +import com.msg.gauth.domain.user.User +import org.springframework.data.jpa.repository.JpaRepository + +interface UserRepository: JpaRepository { + fun findByEmail(email: String): User? + fun existsByEmail(email: String): Boolean +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/domain/user/utils/UserUtil.kt b/src/main/kotlin/com/msg/gauth/domain/user/utils/UserUtil.kt new file mode 100644 index 00000000..ea02693c --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/domain/user/utils/UserUtil.kt @@ -0,0 +1,27 @@ +package com.msg.gauth.domain.user.utils + +import com.msg.gauth.domain.user.User +import com.msg.gauth.domain.user.exception.UserNotFoundException +import com.msg.gauth.domain.user.repository.UserRepository +import com.msg.gauth.global.security.auth.AuthDetails +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.stereotype.Component + +@Component +class UserUtil( + private val userRepository: UserRepository +) { + fun fetchCurrentUser(): User { + val principal = SecurityContextHolder.getContext().authentication.principal + val email = if (principal is UserDetails) { + (principal as AuthDetails).username + } else { + principal.toString() + } + return fetchUserByEmail(email) + } + + fun fetchUserByEmail(email: String): User = + userRepository.findByEmail(email) ?: throw UserNotFoundException() +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/annotation/logger/InjectionLogger.kt b/src/main/kotlin/com/msg/gauth/global/annotation/logger/InjectionLogger.kt new file mode 100644 index 00000000..9dc84dc7 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/annotation/logger/InjectionLogger.kt @@ -0,0 +1,37 @@ +package com.msg.gauth.global.annotation.logger + +import org.slf4j.LoggerFactory +import org.springframework.boot.context.event.ApplicationStartedEvent +import org.springframework.context.ApplicationListener +import org.springframework.stereotype.Component + +@Component +class InjectionLogger: ApplicationListener { + override fun onApplicationEvent(event: ApplicationStartedEvent) { + val ac = event.applicationContext + val beanDefinitionNames = ac.beanDefinitionNames + for (name in beanDefinitionNames) { + val bean = ac.getBean(name!!) + val classPath = bean.javaClass.name + val log = LoggerFactory.getLogger(classPath) + try { + if (!classPath.contains("gauth")) { + continue + } + val beanClass = Class.forName(classPath) + val fields = beanClass.declaredFields + for (field in fields) { + val log4k = field.getDeclaredAnnotation(log4k::class.java) + if (log4k != null) { + field.isAccessible = true + field[bean] = log + } + } + } catch (e: ClassNotFoundException) { + throw RuntimeException(e) + } catch (e: IllegalAccessException) { + throw RuntimeException(e) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/annotation/logger/log4k.kt b/src/main/kotlin/com/msg/gauth/global/annotation/logger/log4k.kt new file mode 100644 index 00000000..d91e7ea6 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/annotation/logger/log4k.kt @@ -0,0 +1,6 @@ +package com.msg.gauth.global.annotation.logger + + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FIELD) +annotation class log4k \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/entity/BaseIdEntity.kt b/src/main/kotlin/com/msg/gauth/global/entity/BaseIdEntity.kt new file mode 100644 index 00000000..e7496171 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/entity/BaseIdEntity.kt @@ -0,0 +1,13 @@ +package com.msg.gauth.global.entity + +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id +import javax.persistence.MappedSuperclass + +@MappedSuperclass +abstract class BaseIdEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long = 0 +} \ No newline at end of file diff --git a/src/main/java/com/msg/gauth/global/exception/ErrorCode.java b/src/main/kotlin/com/msg/gauth/global/exception/ErrorCode.kt similarity index 77% rename from src/main/java/com/msg/gauth/global/exception/ErrorCode.java rename to src/main/kotlin/com/msg/gauth/global/exception/ErrorCode.kt index db6fcb8b..74100625 100644 --- a/src/main/java/com/msg/gauth/global/exception/ErrorCode.java +++ b/src/main/kotlin/com/msg/gauth/global/exception/ErrorCode.kt @@ -1,12 +1,9 @@ -package com.msg.gauth.global.exception; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public enum ErrorCode { +package com.msg.gauth.global.exception +enum class ErrorCode( + val msg: String, + val code: Int +) { BAD_REQUEST("잘못된 요청", 400), PASSWORD_MISMATCH("비밀번호가 일치하지 않습니다.", 400), @@ -20,15 +17,12 @@ public enum ErrorCode { NOT_FOUND("리소스를 찾을수 없음", 404), USER_NOT_FOUND("해당 유저를 찾을 수 없습니다.", 404), - + DUPLICATE_EMAIL("중복되는 이메일입니다.", 409), MANY_REQUEST_EMAIL_AUTH("15분에 최대 3번 이메일 인증을 요청할 수 있습니다.", 429), MAIL_SEND_FAIL("메일을 보내는데 실패했습니다.", 500), - INTERNAL_SERVER_ERROR("서버 내부 에러", 500), + INTERNAL_SERVER_ERROR("서버 내부 에러", 500) ; - - private String msg; - private Integer code; -} +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/exception/ErrorResponse.kt b/src/main/kotlin/com/msg/gauth/global/exception/ErrorResponse.kt new file mode 100644 index 00000000..9e413f3f --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/exception/ErrorResponse.kt @@ -0,0 +1,11 @@ +package com.msg.gauth.global.exception + +class ErrorResponse(errorCode: ErrorCode) { + val msg: String + val code: Int + + init { + msg = errorCode.msg + code = errorCode.code + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/exception/exceptions/BasicException.kt b/src/main/kotlin/com/msg/gauth/global/exception/exceptions/BasicException.kt new file mode 100644 index 00000000..a2c5769b --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/exception/exceptions/BasicException.kt @@ -0,0 +1,5 @@ +package com.msg.gauth.global.exception.exceptions + +import com.msg.gauth.global.exception.ErrorCode + +open class BasicException(val errorCode: ErrorCode): RuntimeException(errorCode.msg) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.kt b/src/main/kotlin/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.kt new file mode 100644 index 00000000..f96551e3 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.kt @@ -0,0 +1,5 @@ +package com.msg.gauth.global.exception.exceptions + +import com.msg.gauth.global.exception.ErrorCode + +class DuplicateEmailException(): BasicException(ErrorCode.DUPLICATE_EMAIL) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/exception/exceptions/MessageSendFailException.kt b/src/main/kotlin/com/msg/gauth/global/exception/exceptions/MessageSendFailException.kt new file mode 100644 index 00000000..a20d2673 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/exception/exceptions/MessageSendFailException.kt @@ -0,0 +1,5 @@ +package com.msg.gauth.global.exception.exceptions + +import com.msg.gauth.global.exception.ErrorCode + +class MessageSendFailException(): BasicException(ErrorCode.MAIL_SEND_FAIL) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/exception/handler/GlobalExceptionHandler.kt b/src/main/kotlin/com/msg/gauth/global/exception/handler/GlobalExceptionHandler.kt new file mode 100644 index 00000000..6628072f --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/exception/handler/GlobalExceptionHandler.kt @@ -0,0 +1,25 @@ +package com.msg.gauth.global.exception.handler + +import com.msg.gauth.global.annotation.logger.log4k +import com.msg.gauth.global.exception.ErrorResponse +import com.msg.gauth.global.exception.exceptions.BasicException +import org.slf4j.Logger +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.bind.annotation.RestControllerAdvice +import javax.servlet.http.HttpServletRequest + +@RestControllerAdvice +class GlobalExceptionHandler { + @log4k + val log: Logger? = null + + @ExceptionHandler(BasicException::class) + fun basicExceptionHandler(request: HttpServletRequest, ex: BasicException): ResponseEntity { + log?.error(request.requestURI) + log?.error(ex.message) + val errorResponse = ErrorResponse(ex.errorCode) + return ResponseEntity(errorResponse, HttpStatus.valueOf(ex.errorCode.code)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/jackson/JacksonConfig.kt b/src/main/kotlin/com/msg/gauth/global/jackson/JacksonConfig.kt new file mode 100644 index 00000000..b832bb1d --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/jackson/JacksonConfig.kt @@ -0,0 +1,18 @@ +package com.msg.gauth.global.jackson + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class JacksonConfig { + + @Bean + fun objectMapper(): ObjectMapper = + jacksonObjectMapper().apply { + this.registerModule(JavaTimeModule()) + } +} + diff --git a/src/main/kotlin/com/msg/gauth/global/mail/MailConfig.kt b/src/main/kotlin/com/msg/gauth/global/mail/MailConfig.kt new file mode 100644 index 00000000..60d979b9 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/mail/MailConfig.kt @@ -0,0 +1,31 @@ +package com.msg.gauth.global.mail + +import com.msg.gauth.global.mail.properties.MailProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.mail.javamail.JavaMailSender +import org.springframework.mail.javamail.JavaMailSenderImpl + +@Configuration +class MailConfig( + private val mailProperties: MailProperties +) { + @Bean + fun getJavaMailSender(): JavaMailSender { + var sender = JavaMailSenderImpl() + sender.host = mailProperties.host + sender.port = mailProperties.port + sender.username = mailProperties.username + sender.password = mailProperties.password + + sender.javaMailProperties["mail.smtp.auth"] = true + sender.javaMailProperties["mail.smtp.connectiontimeout"] = 5000 + sender.javaMailProperties["mail.smtp.timeout"] = 5000 + sender.javaMailProperties["mail.smtp.writetimeout"] = 5000 + sender.javaMailProperties["mail.transport.protocol"] = "smtp" + sender.javaMailProperties["mail.smtp.starttls.enable"] = true + sender.javaMailProperties["mail.smtp.starttls.required"] = true + + return sender + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/mail/properties/MailProperties.kt b/src/main/kotlin/com/msg/gauth/global/mail/properties/MailProperties.kt new file mode 100644 index 00000000..2a38bd22 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/mail/properties/MailProperties.kt @@ -0,0 +1,14 @@ +package com.msg.gauth.global.mail.properties + +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.context.properties.ConstructorBinding + +@ConstructorBinding +@ConfigurationProperties(prefix = "spring.mail") +data class MailProperties( + val host: String, + val port: Int, + val username: String, + val password: String +) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/redis/RedisConfig.kt b/src/main/kotlin/com/msg/gauth/global/redis/RedisConfig.kt new file mode 100644 index 00000000..cc342e7f --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/redis/RedisConfig.kt @@ -0,0 +1,34 @@ +package com.msg.gauth.global.redis + +import com.msg.gauth.global.redis.properties.RedisProperties +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.connection.RedisConnectionFactory +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory +import org.springframework.data.redis.core.RedisTemplate +import org.springframework.data.redis.core.ZSetOperations +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer +import org.springframework.data.redis.serializer.RedisSerializer + +@Configuration +class RedisConfig( + private val redisProperties: RedisProperties +) { + @Bean + @ConditionalOnMissingBean(RedisConnectionFactory::class) + fun redisConnectionFactory(): RedisConnectionFactory = + LettuceConnectionFactory(redisProperties.host, redisProperties.port) + + @Bean + fun redisTemplate(): RedisTemplate = + RedisTemplate().apply { + this.setConnectionFactory(redisConnectionFactory()) + this.keySerializer = RedisSerializer.string() + this.valueSerializer = GenericJackson2JsonRedisSerializer() + } + + @Bean + fun zSetOperation(): ZSetOperations = + redisTemplate().opsForZSet() +} diff --git a/src/main/kotlin/com/msg/gauth/global/redis/properties/RedisProperties.kt b/src/main/kotlin/com/msg/gauth/global/redis/properties/RedisProperties.kt new file mode 100644 index 00000000..d858cb0f --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/redis/properties/RedisProperties.kt @@ -0,0 +1,11 @@ +package com.msg.gauth.global.redis.properties + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.context.properties.ConstructorBinding + +@ConstructorBinding +@ConfigurationProperties(prefix = "spring.redis") +data class RedisProperties( + val host: String, + val port: Int +) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/security/CustomAuthenticationEntryPoint.kt b/src/main/kotlin/com/msg/gauth/global/security/CustomAuthenticationEntryPoint.kt new file mode 100644 index 00000000..842a87fa --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/security/CustomAuthenticationEntryPoint.kt @@ -0,0 +1,29 @@ +package com.msg.gauth.global.security + +import com.fasterxml.jackson.databind.ObjectMapper +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.ErrorResponse +import org.springframework.http.MediaType +import org.springframework.security.core.AuthenticationException +import org.springframework.security.web.AuthenticationEntryPoint +import org.springframework.stereotype.Component +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + +@Component +class CustomAuthenticationEntryPoint( + private val objectMapper: ObjectMapper +): AuthenticationEntryPoint { + + override fun commence( + request: HttpServletRequest, + response: HttpServletResponse, + authException: AuthenticationException + ) { + val errorCode = ErrorCode.UNAUTHORIZED + val responseString = objectMapper.writeValueAsString(ErrorResponse(errorCode)) + response.status = errorCode.code + response.contentType = MediaType.APPLICATION_JSON_VALUE + response.writer.write(responseString) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/security/SecurityConfig.kt b/src/main/kotlin/com/msg/gauth/global/security/SecurityConfig.kt new file mode 100644 index 00000000..d80c3021 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/security/SecurityConfig.kt @@ -0,0 +1,66 @@ +package com.msg.gauth.global.security + +import com.fasterxml.jackson.databind.ObjectMapper +import com.msg.gauth.global.security.config.FilterConfig +import com.msg.gauth.global.security.jwt.JwtTokenProvider +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.http.HttpMethod +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +import org.springframework.security.config.http.SessionCreationPolicy +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.security.web.SecurityFilterChain +import org.springframework.security.web.util.matcher.RequestMatcher +import org.springframework.web.cors.CorsUtils +import javax.servlet.http.HttpServletRequest + +@Configuration +@EnableWebSecurity +class SecurityConfig( + private val jwtTokenProvider: JwtTokenProvider, + private val objectMapper: ObjectMapper +) { + @Bean + fun filterChain(http: HttpSecurity): SecurityFilterChain { + return http + .cors().and() + .csrf().disable() + .formLogin().disable() + .httpBasic().disable() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + + .and() + .authorizeRequests() + .requestMatchers(RequestMatcher { request -> + CorsUtils.isPreFlightRequest(request) + }).permitAll() + + // Auth + .antMatchers(HttpMethod.POST, "/auth").permitAll() + .antMatchers(HttpMethod.PATCH, "/auth").permitAll() + .antMatchers(HttpMethod.DELETE, "/auth").authenticated() + .antMatchers(HttpMethod.POST, "/auth/signup").permitAll() + + // Email + .antMatchers(HttpMethod.POST, "/email").authenticated() + .antMatchers(HttpMethod.GET, "/email/authentication").permitAll() + + .anyRequest().denyAll() + .and() + .exceptionHandling() + .authenticationEntryPoint(CustomAuthenticationEntryPoint(objectMapper)) + + .and() + .apply(FilterConfig(jwtTokenProvider, objectMapper)) + + .and() + .build() + } + + @Bean + fun passwordEncoder(): PasswordEncoder = + BCryptPasswordEncoder() +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/security/auth/AuthDetails.kt b/src/main/kotlin/com/msg/gauth/global/security/auth/AuthDetails.kt new file mode 100644 index 00000000..974d1677 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/security/auth/AuthDetails.kt @@ -0,0 +1,32 @@ +package com.msg.gauth.global.security.auth + +import com.msg.gauth.domain.user.User +import com.msg.gauth.domain.user.enums.UserState +import org.springframework.security.core.GrantedAuthority +import org.springframework.security.core.userdetails.UserDetails + +class AuthDetails( + private val user: User +): UserDetails { + override fun getAuthorities(): Collection = + user.roles + + override fun getPassword(): String? = + null + + override fun getUsername(): String = + user.email + + override fun isAccountNonExpired(): Boolean = + true + + override fun isAccountNonLocked(): Boolean = + user.state.equals(UserState.CREATED) + + + override fun isCredentialsNonExpired(): Boolean = + true + + override fun isEnabled(): Boolean = + isAccountNonExpired && isAccountNonLocked && isCredentialsNonExpired +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/security/auth/AuthDetailsService.kt b/src/main/kotlin/com/msg/gauth/global/security/auth/AuthDetailsService.kt new file mode 100644 index 00000000..25b62519 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/security/auth/AuthDetailsService.kt @@ -0,0 +1,20 @@ +package com.msg.gauth.global.security.auth + +import com.msg.gauth.domain.user.exception.UserNotFoundException +import com.msg.gauth.domain.user.repository.UserRepository +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +@Transactional(readOnly = true) +class AuthDetailsService( + private val userRepository: UserRepository +): UserDetailsService { + override fun loadUserByUsername(username: String): UserDetails { + val user = userRepository.findByEmail(username) ?: throw UserNotFoundException() + return AuthDetails(user) + } + +} diff --git a/src/main/kotlin/com/msg/gauth/global/security/config/FilterConfig.kt b/src/main/kotlin/com/msg/gauth/global/security/config/FilterConfig.kt new file mode 100644 index 00000000..2c5a4530 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/security/config/FilterConfig.kt @@ -0,0 +1,23 @@ +package com.msg.gauth.global.security.config + +import com.fasterxml.jackson.databind.ObjectMapper +import com.msg.gauth.global.security.filter.ExceptionFilter +import com.msg.gauth.global.security.filter.JwtTokenFilter +import com.msg.gauth.global.security.jwt.JwtTokenProvider +import org.springframework.security.config.annotation.SecurityConfigurerAdapter +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.web.DefaultSecurityFilterChain +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter + +class FilterConfig( + private val jwtTokenProvider: JwtTokenProvider, + private val objectMapper: ObjectMapper +): SecurityConfigurerAdapter() { + + override fun configure(builder: HttpSecurity) { + val jwtTokenFilter = JwtTokenFilter(jwtTokenProvider!!) + val exceptionFilter = ExceptionFilter(objectMapper!!) + builder.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter::class.java) + builder.addFilterBefore(exceptionFilter, UsernamePasswordAuthenticationFilter::class.java) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/security/exception/ExpiredTokenException.kt b/src/main/kotlin/com/msg/gauth/global/security/exception/ExpiredTokenException.kt new file mode 100644 index 00000000..b90cec2e --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/security/exception/ExpiredTokenException.kt @@ -0,0 +1,6 @@ +package com.msg.gauth.global.security.exception + +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.exceptions.BasicException + +class ExpiredTokenException: BasicException(ErrorCode.EXPIRED_TOKEN) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/security/exception/InvalidTokenException.kt b/src/main/kotlin/com/msg/gauth/global/security/exception/InvalidTokenException.kt new file mode 100644 index 00000000..053164f1 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/security/exception/InvalidTokenException.kt @@ -0,0 +1,6 @@ +package com.msg.gauth.global.security.exception + +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.exceptions.BasicException + +class InvalidTokenException: BasicException(ErrorCode.INVALID_TOKEN) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/security/filter/ExceptionFilter.kt b/src/main/kotlin/com/msg/gauth/global/security/filter/ExceptionFilter.kt new file mode 100644 index 00000000..5ca0056c --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/security/filter/ExceptionFilter.kt @@ -0,0 +1,40 @@ +package com.msg.gauth.global.security.filter + +import com.fasterxml.jackson.databind.ObjectMapper +import com.msg.gauth.global.exception.ErrorCode +import com.msg.gauth.global.exception.ErrorResponse +import com.msg.gauth.global.exception.exceptions.BasicException +import org.springframework.http.MediaType +import org.springframework.web.filter.OncePerRequestFilter +import java.io.IOException +import javax.servlet.FilterChain +import javax.servlet.ServletException +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + +class ExceptionFilter( + private val objectMapper: ObjectMapper +): OncePerRequestFilter() { + + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + try { + filterChain.doFilter(request, response) + } catch (e: BasicException) { + sendError(response, e.errorCode) + } catch (e: Exception) { + sendError(response, ErrorCode.INTERNAL_SERVER_ERROR) + } + } + + private fun sendError(res: HttpServletResponse, errorCode: ErrorCode) { + val errorResponse = ErrorResponse(errorCode) + val responseString = objectMapper!!.writeValueAsString(errorResponse) + res.status = errorCode.code + res.contentType = MediaType.APPLICATION_JSON_VALUE + res.writer.write(responseString) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/security/filter/JwtTokenFilter.kt b/src/main/kotlin/com/msg/gauth/global/security/filter/JwtTokenFilter.kt new file mode 100644 index 00000000..cd393bbf --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/security/filter/JwtTokenFilter.kt @@ -0,0 +1,29 @@ +package com.msg.gauth.global.security.filter + +import com.msg.gauth.global.security.jwt.JwtTokenProvider +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.web.filter.OncePerRequestFilter +import java.io.IOException +import javax.servlet.FilterChain +import javax.servlet.ServletException +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + +class JwtTokenFilter( + private val jwtTokenProvider: JwtTokenProvider +): OncePerRequestFilter() { + + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + val token: String? = jwtTokenProvider.resolveToken(request) + token?.let { + print(it) + val authentication = jwtTokenProvider.authentication(token) + SecurityContextHolder.getContext().authentication = authentication + } + filterChain.doFilter(request, response) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/security/jwt/JwtProperties.kt b/src/main/kotlin/com/msg/gauth/global/security/jwt/JwtProperties.kt new file mode 100644 index 00000000..7faeb87d --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/security/jwt/JwtProperties.kt @@ -0,0 +1,21 @@ +package com.msg.gauth.global.security.jwt + +import io.jsonwebtoken.security.Keys +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.context.properties.ConstructorBinding +import java.security.Key + +@ConstructorBinding +@ConfigurationProperties(prefix = "jwt") +class JwtProperties( + accessSecret: String, + refreshSecret: String +) { + val accessSecret: Key + val refreshSecret: Key + + init { + this.accessSecret = Keys.hmacShaKeyFor(accessSecret.toByteArray()) + this.refreshSecret = Keys.hmacShaKeyFor(refreshSecret.toByteArray()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/security/jwt/JwtTokenProvider.kt b/src/main/kotlin/com/msg/gauth/global/security/jwt/JwtTokenProvider.kt new file mode 100644 index 00000000..65829f70 --- /dev/null +++ b/src/main/kotlin/com/msg/gauth/global/security/jwt/JwtTokenProvider.kt @@ -0,0 +1,84 @@ +package com.msg.gauth.global.security.jwt + +import com.msg.gauth.global.security.auth.AuthDetailsService +import com.msg.gauth.global.security.exception.ExpiredTokenException +import com.msg.gauth.global.security.exception.InvalidTokenException +import io.jsonwebtoken.Claims +import io.jsonwebtoken.ExpiredJwtException +import io.jsonwebtoken.Jwts +import io.jsonwebtoken.SignatureAlgorithm +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.Authentication +import org.springframework.stereotype.Component +import java.security.Key +import java.time.ZonedDateTime +import java.util.* +import javax.servlet.http.HttpServletRequest + +@Component +class JwtTokenProvider( + private val jwtProperties: JwtProperties, + private val authDetailsService: AuthDetailsService +) { + companion object { + const val ACCESS_TYPE = "access" + const val REFRESH_TYPE = "refresh" + const val ACCESS_EXP = 60L * 15 // 15 min + const val REFRESH_EXP = 60L * 60 * 24 * 7 // 1 weeks + const val TOKEN_PREFIX = "Bearer " + } + + fun generateAccessToken(email: String): String = + generateToken(email, ACCESS_TYPE, jwtProperties.accessSecret, ACCESS_EXP) + + fun generateRefreshToken(email: String): String = + generateToken(email, REFRESH_TYPE, jwtProperties.refreshSecret, REFRESH_EXP) + + fun resolveToken(req: HttpServletRequest): String? { + val token = req.getHeader("Authorization") ?: return null + return parseToken(token) + } + + fun exactEmailFromRefreshToken(refresh: String): String { + return getTokenSubject(refresh, jwtProperties.refreshSecret) + } + + fun authentication(token: String): Authentication { + val userDetails = authDetailsService.loadUserByUsername(getTokenSubject(token, jwtProperties.accessSecret)) + return UsernamePasswordAuthenticationToken(userDetails, "", userDetails.authorities) + } + + fun parseToken(token: String): String? = + if (token.startsWith(TOKEN_PREFIX)) token.replace(TOKEN_PREFIX, "") else null + + val accessExpiredTime: ZonedDateTime + get() = ZonedDateTime.now().plusSeconds(ACCESS_EXP) + + + fun generateToken(sub: String, type: String, secret: Key, exp: Long): String { + return Jwts.builder() + .signWith(secret, SignatureAlgorithm.HS256) + .setSubject(sub) + .claim("type", type) + .setIssuedAt(Date()) + .setExpiration(Date(System.currentTimeMillis() + exp * 1000)) + .compact() + } + + private fun getTokenBody(token: String, secret: Key): Claims { + return try { + Jwts.parserBuilder() + .setSigningKey(secret) + .build() + .parseClaimsJws(token) + .body + } catch (e: ExpiredJwtException) { + throw ExpiredTokenException() + } catch (e: Exception) { + throw InvalidTokenException() + } + } + + private fun getTokenSubject(token: String, secret: Key): String = + getTokenBody(token, secret).subject +} \ No newline at end of file diff --git a/src/test/java/com/msg/gauth/GauthApplicationTests.java b/src/test/java/com/msg/gauth/GauthApplicationTests.java deleted file mode 100644 index 1ccda816..00000000 --- a/src/test/java/com/msg/gauth/GauthApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.msg.gauth; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class GauthApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/src/test/java/com/msg/gauth/domain/email/services/MailSendServiceTest.java b/src/test/java/com/msg/gauth/domain/email/services/MailSendServiceTest.java deleted file mode 100644 index 8d882f44..00000000 --- a/src/test/java/com/msg/gauth/domain/email/services/MailSendServiceTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.msg.gauth.domain.email.services; - -import com.msg.gauth.domain.email.repository.EmailAuthRepository; -import com.msg.gauth.domain.email.presentation.dto.request.EmailSendDto; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import javax.mail.MessagingException; - -@SpringBootTest -class MailSendServiceTest { - @Autowired - MailSendService mailSendService; - @Autowired - EmailAuthRepository emailAuthRepository; - - @Test - public void test() throws MessagingException { - EmailSendDto emailSendDto = new EmailSendDto("baegteun@gmail.com"); - mailSendService.execute(emailSendDto); - emailAuthRepository.deleteById(emailSendDto.getEmail()); - } -} \ No newline at end of file diff --git a/src/test/java/com/msg/gauth/domain/user/services/SignUpServiceTest.java b/src/test/java/com/msg/gauth/domain/user/services/SignUpServiceTest.java deleted file mode 100644 index 0f4cd53e..00000000 --- a/src/test/java/com/msg/gauth/domain/user/services/SignUpServiceTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.msg.gauth.domain.user.services; - -import com.msg.gauth.domain.user.User; -import com.msg.gauth.domain.user.exception.UserNotFoundException; -import com.msg.gauth.domain.user.presentation.dto.request.SignUpDto; -import com.msg.gauth.domain.user.repository.UserRepository; -import com.msg.gauth.domain.auth.services.SignUpService; -import com.msg.gauth.global.exception.exceptions.DuplicateEmailException; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.security.crypto.password.PasswordEncoder; - -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; -import java.util.Set; - - -@SpringBootTest -public class SignUpServiceTest { - - @Autowired - private PasswordEncoder passwordEncoder; - @Autowired - private SignUpService signUpService; - @Autowired - private UserRepository userRepository; - private static ValidatorFactory validatorFactory; - private static Validator validatorFromFactory; - - @BeforeAll - public static void init() { - validatorFactory = Validation.buildDefaultValidatorFactory(); - validatorFromFactory = validatorFactory.getValidator(); - } - - @AfterEach - public void deleteTestData(){ - userRepository.deleteAll(); - } - - @Test - public void SignUp(){ - //given - SignUpDto signUpDto = new SignUpDto("s21053@gsm.hs.kr", "123456789999"); - - //when - Long userId = signUpService.execute(signUpDto); - - //then - Assertions.assertThat(userRepository.existsById(userId)).isTrue(); - User user = userRepository.findById(userId) - .orElseThrow(() -> new UserNotFoundException()); - Assertions.assertThat(user.getEmail()).isEqualTo(signUpDto.getEmail()); - org.junit.jupiter.api.Assertions.assertThrows(DuplicateEmailException.class, () -> signUpService.execute(signUpDto)); - } - - @Test - public void PasswordValidationTest(){ - SignUpDto signUpDto = new SignUpDto("s21053@gsm.hs.kr", "123"); - Set> validate = validatorFromFactory.validate(signUpDto); - Assertions.assertThat(validate).isNotEmpty(); - } - - @Test - public void EmailValidationTest(){ - SignUpDto signUpDto = new SignUpDto("s21053hs.kr", "123219081"); - Set> validate = validatorFromFactory.validate(signUpDto); - Assertions.assertThat(validate).isNotEmpty(); - } - - @Test - public void PasswordEncodeTest(){ - SignUpDto signUpDto = new SignUpDto("s21053@gsm.hs.kr", "123456789999"); - Long id = signUpService.execute(signUpDto); - User user = userRepository.findById(id) - .orElseThrow(() -> new UserNotFoundException()); - Assertions.assertThat(user.getPassword()).isNotEqualTo(signUpDto.getPassword()); - Assertions.assertThat(passwordEncoder.matches(signUpDto.getPassword(), user.getPassword())).isTrue(); - } -} diff --git a/src/test/kotlin/com/msg/gauth/GauthBackendApplicationTests.kt b/src/test/kotlin/com/msg/gauth/GauthBackendApplicationTests.kt new file mode 100644 index 00000000..23e6038b --- /dev/null +++ b/src/test/kotlin/com/msg/gauth/GauthBackendApplicationTests.kt @@ -0,0 +1,13 @@ +package com.msg.gauth + +import org.junit.jupiter.api.Test +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest +class GauthBackendApplicationTests { + + @Test + fun contextLoads() { + } + +} From de699706c96f9a08987cb4b59eba6c3c8cd03992 Mon Sep 17 00:00:00 2001 From: baegteun Date: Sat, 17 Sep 2022 17:49:44 +0900 Subject: [PATCH 2/5] =?UTF-8?q?:pencil2:=20::=20gitignore=20=EC=98=A4?= =?UTF-8?q?=ED=83=80=20=E3=85=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 164c7940..a13931d7 100644 --- a/.gitignore +++ b/.gitignore @@ -38,5 +38,5 @@ out/ /src/main/resources -### docker ##3 +### docker ### docker-compose.yml From 554451c5a5bb1ca711c5354daf1f644dd6a6d4d0 Mon Sep 17 00:00:00 2001 From: baegteun Date: Sat, 17 Sep 2022 18:00:01 +0900 Subject: [PATCH 3/5] =?UTF-8?q?:recycle:=20::=20apply=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/msg/gauth/global/mail/MailConfig.kt | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/com/msg/gauth/global/mail/MailConfig.kt b/src/main/kotlin/com/msg/gauth/global/mail/MailConfig.kt index 60d979b9..40cece23 100644 --- a/src/main/kotlin/com/msg/gauth/global/mail/MailConfig.kt +++ b/src/main/kotlin/com/msg/gauth/global/mail/MailConfig.kt @@ -11,21 +11,20 @@ class MailConfig( private val mailProperties: MailProperties ) { @Bean - fun getJavaMailSender(): JavaMailSender { - var sender = JavaMailSenderImpl() - sender.host = mailProperties.host - sender.port = mailProperties.port - sender.username = mailProperties.username - sender.password = mailProperties.password + fun getJavaMailSender(): JavaMailSender = + JavaMailSenderImpl().apply { + this.host = mailProperties.host + this.port = mailProperties.port + this.username = mailProperties.username + this.password = mailProperties.password - sender.javaMailProperties["mail.smtp.auth"] = true - sender.javaMailProperties["mail.smtp.connectiontimeout"] = 5000 - sender.javaMailProperties["mail.smtp.timeout"] = 5000 - sender.javaMailProperties["mail.smtp.writetimeout"] = 5000 - sender.javaMailProperties["mail.transport.protocol"] = "smtp" - sender.javaMailProperties["mail.smtp.starttls.enable"] = true - sender.javaMailProperties["mail.smtp.starttls.required"] = true + this.javaMailProperties["mail.smtp.auth"] = true + this.javaMailProperties["mail.smtp.connectiontimeout"] = 5000 + this.javaMailProperties["mail.smtp.timeout"] = 5000 + this.javaMailProperties["mail.smtp.writetimeout"] = 5000 + this.javaMailProperties["mail.transport.protocol"] = "smtp" + this.javaMailProperties["mail.smtp.starttls.enable"] = true + this.javaMailProperties["mail.smtp.starttls.required"] = true + } - return sender - } } \ No newline at end of file From 7cd01fa653557d1ebbb119a02b6021958ff9b66b Mon Sep 17 00:00:00 2001 From: baegteun Date: Sat, 17 Sep 2022 18:01:15 +0900 Subject: [PATCH 4/5] =?UTF-8?q?:fire:=20::=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=20=EC=96=B8=EB=9E=98=ED=95=91=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/msg/gauth/global/security/config/FilterConfig.kt | 4 ++-- .../com/msg/gauth/global/security/filter/JwtTokenFilter.kt | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/msg/gauth/global/security/config/FilterConfig.kt b/src/main/kotlin/com/msg/gauth/global/security/config/FilterConfig.kt index 2c5a4530..dde08c7c 100644 --- a/src/main/kotlin/com/msg/gauth/global/security/config/FilterConfig.kt +++ b/src/main/kotlin/com/msg/gauth/global/security/config/FilterConfig.kt @@ -15,8 +15,8 @@ class FilterConfig( ): SecurityConfigurerAdapter() { override fun configure(builder: HttpSecurity) { - val jwtTokenFilter = JwtTokenFilter(jwtTokenProvider!!) - val exceptionFilter = ExceptionFilter(objectMapper!!) + val jwtTokenFilter = JwtTokenFilter(jwtTokenProvider) + val exceptionFilter = ExceptionFilter(objectMapper) builder.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter::class.java) builder.addFilterBefore(exceptionFilter, UsernamePasswordAuthenticationFilter::class.java) } diff --git a/src/main/kotlin/com/msg/gauth/global/security/filter/JwtTokenFilter.kt b/src/main/kotlin/com/msg/gauth/global/security/filter/JwtTokenFilter.kt index cd393bbf..400aeba6 100644 --- a/src/main/kotlin/com/msg/gauth/global/security/filter/JwtTokenFilter.kt +++ b/src/main/kotlin/com/msg/gauth/global/security/filter/JwtTokenFilter.kt @@ -20,7 +20,6 @@ class JwtTokenFilter( ) { val token: String? = jwtTokenProvider.resolveToken(request) token?.let { - print(it) val authentication = jwtTokenProvider.authentication(token) SecurityContextHolder.getContext().authentication = authentication } From 014ace1b2089e305ea4e74f49bdaa9f932250f10 Mon Sep 17 00:00:00 2001 From: baegteun Date: Sat, 17 Sep 2022 18:12:50 +0900 Subject: [PATCH 5/5] =?UTF-8?q?:recycle:=20::=20=EC=82=AC=EC=86=8C?= =?UTF-8?q?=ED=95=9C=20refactoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gauth/domain/auth/presentation/AuthController.kt | 10 ++++------ .../domain/email/presentation/dto/EmailSendDto.kt | 4 +++- .../exception/exceptions/DuplicateEmailException.kt | 2 +- .../exception/exceptions/MessageSendFailException.kt | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/com/msg/gauth/domain/auth/presentation/AuthController.kt b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/AuthController.kt index 3cd92ab7..25f96196 100644 --- a/src/main/kotlin/com/msg/gauth/domain/auth/presentation/AuthController.kt +++ b/src/main/kotlin/com/msg/gauth/domain/auth/presentation/AuthController.kt @@ -23,9 +23,8 @@ class AuthController( ) { @PatchMapping - fun refresh(@RequestHeader refreshToken: String): ResponseEntity { - return ResponseEntity.ok(refreshService.execute(refreshToken)) - } + fun refresh(@RequestHeader refreshToken: String): ResponseEntity = + ResponseEntity.ok(refreshService.execute(refreshToken)) @DeleteMapping fun logout(): ResponseEntity { @@ -34,9 +33,8 @@ class AuthController( } @PostMapping - fun signin(@Valid @RequestBody signinRequestDto: SigninRequestDto): ResponseEntity { - return ResponseEntity.ok(signInService.execute(signinRequestDto)) - } + fun signin(@Valid @RequestBody signinRequestDto: SigninRequestDto): ResponseEntity = + ResponseEntity.ok(signInService.execute(signinRequestDto)) @PostMapping("/signup") fun signUpMember(@Valid @RequestBody signUpDto: SignUpDto): ResponseEntity { diff --git a/src/main/kotlin/com/msg/gauth/domain/email/presentation/dto/EmailSendDto.kt b/src/main/kotlin/com/msg/gauth/domain/email/presentation/dto/EmailSendDto.kt index b625c0ef..0d3b2954 100644 --- a/src/main/kotlin/com/msg/gauth/domain/email/presentation/dto/EmailSendDto.kt +++ b/src/main/kotlin/com/msg/gauth/domain/email/presentation/dto/EmailSendDto.kt @@ -1,3 +1,5 @@ package com.msg.gauth.domain.email.presentation.dto -data class EmailSendDto(val email: String) \ No newline at end of file +data class EmailSendDto( + val email: String +) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.kt b/src/main/kotlin/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.kt index f96551e3..6dd20b51 100644 --- a/src/main/kotlin/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.kt +++ b/src/main/kotlin/com/msg/gauth/global/exception/exceptions/DuplicateEmailException.kt @@ -2,4 +2,4 @@ package com.msg.gauth.global.exception.exceptions import com.msg.gauth.global.exception.ErrorCode -class DuplicateEmailException(): BasicException(ErrorCode.DUPLICATE_EMAIL) \ No newline at end of file +class DuplicateEmailException: BasicException(ErrorCode.DUPLICATE_EMAIL) \ No newline at end of file diff --git a/src/main/kotlin/com/msg/gauth/global/exception/exceptions/MessageSendFailException.kt b/src/main/kotlin/com/msg/gauth/global/exception/exceptions/MessageSendFailException.kt index a20d2673..40312967 100644 --- a/src/main/kotlin/com/msg/gauth/global/exception/exceptions/MessageSendFailException.kt +++ b/src/main/kotlin/com/msg/gauth/global/exception/exceptions/MessageSendFailException.kt @@ -2,4 +2,4 @@ package com.msg.gauth.global.exception.exceptions import com.msg.gauth.global.exception.ErrorCode -class MessageSendFailException(): BasicException(ErrorCode.MAIL_SEND_FAIL) \ No newline at end of file +class MessageSendFailException: BasicException(ErrorCode.MAIL_SEND_FAIL) \ No newline at end of file