diff --git a/kotlin-web-spring-boot-3/build.gradle.kts b/kotlin-web-spring-boot-3/build.gradle.kts index 3b25241..15d2164 100644 --- a/kotlin-web-spring-boot-3/build.gradle.kts +++ b/kotlin-web-spring-boot-3/build.gradle.kts @@ -38,6 +38,9 @@ dependencies { implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.0") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.2.RELEASE") + implementation("io.jsonwebtoken:jjwt-api:0.12.5") + implementation("io.jsonwebtoken:jjwt-impl:0.12.5") + implementation("io.jsonwebtoken:jjwt-jackson:0.12.5") developmentOnly("org.springframework.boot:spring-boot-devtools:3.2.4") runtimeOnly("org.postgresql:postgresql:42.7.3") testImplementation("org.springframework.boot:spring-boot-starter-test:3.2.4") diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/AuthReqDto.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/AuthReqDto.kt new file mode 100644 index 0000000..04322fa --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/AuthReqDto.kt @@ -0,0 +1,6 @@ +package com.vndevteam.kotlinwebspringboot3.application.auth + +data class AuthReqDto( + val email: String, + val password: String, +) diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/AuthResDto.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/AuthResDto.kt new file mode 100644 index 0000000..1f0ad24 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/AuthResDto.kt @@ -0,0 +1,6 @@ +package com.vndevteam.kotlinwebspringboot3.application.auth + +data class AuthResDto( + val jwtToken: String, + val refreshToken: String, +) diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/AuthRestController.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/AuthRestController.kt new file mode 100644 index 0000000..c744e8c --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/AuthRestController.kt @@ -0,0 +1,26 @@ +package com.vndevteam.kotlinwebspringboot3.application.auth + +import com.vndevteam.kotlinwebspringboot3.domain.auth.AuthService +import org.springframework.http.HttpStatus +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.server.ResponseStatusException + +@RestController +@RequestMapping("/auth") +class AuthController( + private val authService: AuthService, +) { + @PostMapping("/login") + fun authenticate(@RequestBody authenticationReqDto: AuthReqDto): AuthResDto = + authService.authentication(authenticationReqDto) + + @PostMapping("/refresh") + fun refreshAccessToken(@RequestBody request: RefreshTokenReqDto): TokenResDto = + authService.refreshAccessToken(request.jwtToken)?.toTokenResDto() + ?: throw ResponseStatusException(HttpStatus.FORBIDDEN, "Invalid refresh token.") + + private fun String.toTokenResDto(): TokenResDto = TokenResDto(jwtToken = this) +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/RefreshTokenReqDto.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/RefreshTokenReqDto.kt new file mode 100644 index 0000000..55243b9 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/RefreshTokenReqDto.kt @@ -0,0 +1,5 @@ +package com.vndevteam.kotlinwebspringboot3.application.auth + +data class RefreshTokenReqDto( + val jwtToken: String, +) diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/TokenResDto.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/TokenResDto.kt new file mode 100644 index 0000000..cbc9bc6 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/auth/TokenResDto.kt @@ -0,0 +1,5 @@ +package com.vndevteam.kotlinwebspringboot3.application.auth + +data class TokenResDto( + val jwtToken: String, +) diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/demo/DemoRestController.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/demo/DemoRestController.kt index 428ac6e..de9ac42 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/demo/DemoRestController.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/demo/DemoRestController.kt @@ -1,7 +1,7 @@ package com.vndevteam.kotlinwebspringboot3.application.demo -import com.vndevteam.kotlinwebspringboot3.domain.enums.MESSAGE -import com.vndevteam.kotlinwebspringboot3.infrastructure.util.MsgUtils +import com.vndevteam.kotlinwebspringboot3.common.enums.Message +import com.vndevteam.kotlinwebspringboot3.util.MsgUtils import jakarta.validation.Valid import jakarta.validation.constraints.NotBlank import org.springframework.context.i18n.LocaleContextHolder @@ -17,7 +17,7 @@ class DemoRestController { fun getLocaleMessage(): Map { return mapOf( "request_locale" to LocaleContextHolder.getLocale(), - "msg" to MsgUtils.getMessage(MESSAGE.MSG_1) + "msg" to MsgUtils.getMessage(Message.MSG_1) ) } diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/post/PostEntity.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/post/PostEntity.kt new file mode 100644 index 0000000..cdfca6c --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/post/PostEntity.kt @@ -0,0 +1,9 @@ +package com.vndevteam.kotlinwebspringboot3.application.post + +import java.util.UUID + +data class PostEntity( + val id: UUID, + val title: String, + val content: String, +) diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/post/PostResDto.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/post/PostResDto.kt index 4e3aaef..203a4fd 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/post/PostResDto.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/post/PostResDto.kt @@ -1,3 +1,9 @@ package com.vndevteam.kotlinwebspringboot3.application.post -class PostResDto {} +import java.util.UUID + +data class PostResDto( + val id: UUID, + val title: String, + val content: String, +) diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/post/PostRestController.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/post/PostRestController.kt index 8d39ab9..0b2bf56 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/post/PostRestController.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/post/PostRestController.kt @@ -1,6 +1,13 @@ package com.vndevteam.kotlinwebspringboot3.application.post +import com.vndevteam.kotlinwebspringboot3.domain.post.PostService +import com.vndevteam.kotlinwebspringboot3.util.toPostResDto +import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -@RestController @RequestMapping("/posts") class PostRestController {} +@RestController +@RequestMapping("/posts") +class PostRestController(private val postService: PostService) { + @GetMapping fun listAll(): List = postService.findAll().map { it.toPostResDto() } +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserEntity.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserEntity.kt new file mode 100644 index 0000000..0dc2bbc --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserEntity.kt @@ -0,0 +1,6 @@ +package com.vndevteam.kotlinwebspringboot3.application.user + +import com.vndevteam.kotlinwebspringboot3.common.enums.UserRole +import java.util.UUID + +data class UserEntity(val id: UUID, val email: String, val password: String, val role: UserRole) diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserReqDto.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserReqDto.kt index 3b7cc94..fae1bfb 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserReqDto.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserReqDto.kt @@ -1,3 +1,6 @@ package com.vndevteam.kotlinwebspringboot3.application.user -class UserReqDto {} +data class UserReqDto( + val email: String, + val password: String, +) diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserResDto.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserResDto.kt index bf324df..6f8d3aa 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserResDto.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserResDto.kt @@ -1,3 +1,8 @@ package com.vndevteam.kotlinwebspringboot3.application.user -class UserResDto {} +import java.util.UUID + +data class UserResDto( + val uuid: UUID, + val email: String, +) diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserRestController.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserRestController.kt index 5f5c3f6..7f538c7 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserRestController.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/application/user/UserRestController.kt @@ -1,6 +1,40 @@ package com.vndevteam.kotlinwebspringboot3.application.user +import com.vndevteam.kotlinwebspringboot3.domain.user.UserService +import com.vndevteam.kotlinwebspringboot3.util.toUserEntity +import com.vndevteam.kotlinwebspringboot3.util.toUserResDto +import java.util.UUID +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController +import org.springframework.web.server.ResponseStatusException -@RestController @RequestMapping("/users") class UserRestController {} +@RestController +@RequestMapping("/users") +class UserRestController(private val userService: UserService) { + @PostMapping + fun create(@RequestBody userReqDto: UserReqDto): UserResDto = + userService.createUser(userReqDto.toUserEntity())?.toUserResDto() + ?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot create user.") + + @GetMapping fun listAll(): List = userService.findAll().map { it.toUserResDto() } + + @GetMapping("/{uuid}") + fun findByUUID(@PathVariable uuid: UUID): UserResDto = + userService.findByUUID(uuid)?.toUserResDto() + ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "User not found.") + + @DeleteMapping("/{uuid}") + fun deleteByUUID(@PathVariable uuid: UUID): ResponseEntity { + val success = userService.deleteByUUID(uuid) + + return if (success) ResponseEntity.noContent().build() + else throw ResponseStatusException(HttpStatus.NOT_FOUND, "User not found.") + } +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/ErrorConstants.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/constants/ErrorConstants.kt similarity index 80% rename from kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/ErrorConstants.kt rename to kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/constants/ErrorConstants.kt index 4cbec27..b4c274d 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/ErrorConstants.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/constants/ErrorConstants.kt @@ -1,4 +1,4 @@ -package com.vndevteam.kotlinwebspringboot3.infrastructure.exception +package com.vndevteam.kotlinwebspringboot3.common.constants class ErrorConstants { companion object { diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/constants/SecurityConstants.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/constants/SecurityConstants.kt new file mode 100644 index 0000000..ee78374 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/constants/SecurityConstants.kt @@ -0,0 +1,8 @@ +package com.vndevteam.kotlinwebspringboot3.common.constants + +class SecurityConstants { + companion object { + const val BEARER = "Bearer " + const val HEADER_ACCESS_TOKEN = "access-token" + } +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/enums/Message.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/enums/Message.kt new file mode 100644 index 0000000..1070731 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/enums/Message.kt @@ -0,0 +1,7 @@ +package com.vndevteam.kotlinwebspringboot3.common.enums + +enum class Message(val code: String) { + MSG_1("msg-1"), + MSG_2("msg-2"), + ERR_1("err-1") +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/enums/UserRole.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/enums/UserRole.kt new file mode 100644 index 0000000..9f08834 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/enums/UserRole.kt @@ -0,0 +1,6 @@ +package com.vndevteam.kotlinwebspringboot3.common.enums + +enum class UserRole { + USER, + ADMIN +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/error/ErrorDetail.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/error/ErrorDetail.kt similarity index 70% rename from kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/error/ErrorDetail.kt rename to kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/error/ErrorDetail.kt index 8932e41..95bddf7 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/error/ErrorDetail.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/error/ErrorDetail.kt @@ -1,4 +1,4 @@ -package com.vndevteam.kotlinwebspringboot3.infrastructure.exception.error +package com.vndevteam.kotlinwebspringboot3.common.error import com.fasterxml.jackson.annotation.JsonInclude diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/error/ErrorResponseDto.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/error/ErrorResponseDto.kt similarity index 80% rename from kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/error/ErrorResponseDto.kt rename to kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/error/ErrorResponseDto.kt index f59f410..2f4564a 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/error/ErrorResponseDto.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/common/error/ErrorResponseDto.kt @@ -1,4 +1,4 @@ -package com.vndevteam.kotlinwebspringboot3.infrastructure.exception.error +package com.vndevteam.kotlinwebspringboot3.common.error import com.fasterxml.jackson.annotation.JsonFormat import java.time.LocalDateTime diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/AuthService.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/AuthService.kt new file mode 100644 index 0000000..7b55f4f --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/AuthService.kt @@ -0,0 +1,68 @@ +package com.vndevteam.kotlinwebspringboot3.domain.auth + +import com.vndevteam.kotlinwebspringboot3.application.auth.AuthReqDto +import com.vndevteam.kotlinwebspringboot3.application.auth.AuthResDto +import com.vndevteam.kotlinwebspringboot3.infrastructure.config.JwtProperties +import com.vndevteam.kotlinwebspringboot3.util.DateTimeUtils +import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.stereotype.Service + +@Service +class AuthService( + private val authManager: AuthenticationManager, + private val userDetailsService: CustomUserDetailsService, + private val tokenService: TokenService, + private val jwtProperties: JwtProperties, + private val refreshTokenRepository: RefreshTokenRepository, +) { + fun authentication(authenticationRequest: AuthReqDto): AuthResDto { + authManager.authenticate( + UsernamePasswordAuthenticationToken( + authenticationRequest.email, + authenticationRequest.password, + ) + ) + + val user = userDetailsService.loadUserByUsername(authenticationRequest.email) + + return AuthResDto( + jwtToken = createJwtToken(user), + refreshToken = createRefreshToken(user), + ) + } + + fun refreshAccessToken(refreshToken: String): String? { + val extractedEmail = tokenService.extractEmail(refreshToken) + return extractedEmail?.let { email -> + val currentUserDetails = userDetailsService.loadUserByUsername(email) + val refreshTokenUserDetails = + refreshTokenRepository.findUserDetailsByToken(refreshToken) + if ( + !tokenService.isExpired(refreshToken) && + refreshTokenUserDetails?.username == currentUserDetails.username + ) + createJwtToken(currentUserDetails) + else null + } + } + + private fun createJwtToken(user: UserDetails) = + tokenService.generate( + userDetails = user, + expirationDate = + DateTimeUtils.getDateNow( + DateTimeUtils.getNow().plusMinutes(jwtProperties.jwtTokenExpiration) + ) + ) + + private fun createRefreshToken(user: UserDetails) = + tokenService.generate( + userDetails = user, + expirationDate = + DateTimeUtils.getDateNow( + DateTimeUtils.getNow().plusMinutes(jwtProperties.refreshTokenExpiration) + ) + ) +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/CustomUserDetailsService.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/CustomUserDetailsService.kt new file mode 100644 index 0000000..1298164 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/CustomUserDetailsService.kt @@ -0,0 +1,15 @@ +package com.vndevteam.kotlinwebspringboot3.domain.auth + +import com.vndevteam.kotlinwebspringboot3.domain.user.UserRepository +import com.vndevteam.kotlinwebspringboot3.util.toUserDetails +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 + +@Service +class CustomUserDetailsService(private val userRepository: UserRepository) : UserDetailsService { + override fun loadUserByUsername(username: String): UserDetails = + userRepository.findByEmail(username)?.toUserDetails() + ?: throw UsernameNotFoundException("Not found!") +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/RefreshTokenRepository.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/RefreshTokenRepository.kt new file mode 100644 index 0000000..905aa7e --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/RefreshTokenRepository.kt @@ -0,0 +1,15 @@ +package com.vndevteam.kotlinwebspringboot3.domain.auth + +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.stereotype.Component + +@Component +class RefreshTokenRepository { + private val tokens = mutableMapOf() + + fun findUserDetailsByToken(token: String): UserDetails? = tokens[token] + + fun save(token: String, userDetails: UserDetails) { + tokens[token] = userDetails + } +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/TokenService.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/TokenService.kt new file mode 100644 index 0000000..c5db521 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/auth/TokenService.kt @@ -0,0 +1,46 @@ +package com.vndevteam.kotlinwebspringboot3.domain.auth + +import com.vndevteam.kotlinwebspringboot3.infrastructure.config.JwtProperties +import com.vndevteam.kotlinwebspringboot3.util.DateTimeUtils +import io.jsonwebtoken.Claims +import io.jsonwebtoken.Jwts +import io.jsonwebtoken.security.Keys +import java.util.Date +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.stereotype.Service + +@Service +class TokenService(jwtProperties: JwtProperties) { + private val secretKey = Keys.hmacShaKeyFor(jwtProperties.key.toByteArray()) + + fun generate( + userDetails: UserDetails, + expirationDate: Date, + additionalClaims: Map = emptyMap() + ): String = + Jwts.builder() + .claims() + .subject(userDetails.username) + .issuedAt(DateTimeUtils.getDateNow()) + .expiration(expirationDate) + .add(additionalClaims) + .and() + .signWith(secretKey) + .compact() + + fun isValid(token: String, userDetails: UserDetails): Boolean { + val email = extractEmail(token) + return userDetails.username == email && !isExpired(token) + } + + fun extractEmail(token: String): String? = getAllClaims(token).subject + + fun isExpired(token: String): Boolean = + getAllClaims(token).expiration.before(DateTimeUtils.getDateNow()) + + private fun getAllClaims(token: String): Claims { + val parser = Jwts.parser().verifyWith(secretKey).build() + + return parser.parseSignedClaims(token).payload + } +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/enums/MESSAGE.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/enums/MESSAGE.kt deleted file mode 100644 index 9798d14..0000000 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/enums/MESSAGE.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.vndevteam.kotlinwebspringboot3.domain.enums - -enum class MESSAGE(val code: String) { - MSG_1("msg-1"), - MSG_2("msg-2"), - ERR_1("err-1") -} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/post/PostRepository.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/post/PostRepository.kt new file mode 100644 index 0000000..df18ff4 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/post/PostRepository.kt @@ -0,0 +1,16 @@ +package com.vndevteam.kotlinwebspringboot3.domain.post + +import com.vndevteam.kotlinwebspringboot3.application.post.PostEntity +import java.util.UUID +import org.springframework.stereotype.Repository + +@Repository +class PostRepository { + private val posts = + listOf( + PostEntity(id = UUID.randomUUID(), title = "Post 1", content = "Content 1"), + PostEntity(id = UUID.randomUUID(), title = "Post 2", content = "Content 2"), + ) + + fun findAll(): List = posts +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/post/PostService.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/post/PostService.kt index 105b77c..59973b7 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/post/PostService.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/post/PostService.kt @@ -1,5 +1,9 @@ package com.vndevteam.kotlinwebspringboot3.domain.post +import com.vndevteam.kotlinwebspringboot3.application.post.PostEntity import org.springframework.stereotype.Service -@Service class PostService {} +@Service +class PostService(private val postRepository: PostRepository) { + fun findAll(): List = postRepository.findAll() +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/user/UserRepository.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/user/UserRepository.kt index 702f210..bb9f669 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/user/UserRepository.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/user/UserRepository.kt @@ -1,3 +1,50 @@ package com.vndevteam.kotlinwebspringboot3.domain.user -class UserRepository {} +import com.vndevteam.kotlinwebspringboot3.application.user.UserEntity +import com.vndevteam.kotlinwebspringboot3.common.enums.UserRole +import java.util.UUID +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.stereotype.Repository + +@Repository +class UserRepository(private val encoder: PasswordEncoder) { + + private val users = + mutableSetOf( + UserEntity( + id = UUID.randomUUID(), + email = "email-1@example.com", + password = encoder.encode("pass1"), + role = UserRole.USER, + ), + UserEntity( + id = UUID.randomUUID(), + email = "email-2@example.com", + password = encoder.encode("pass2"), + role = UserRole.ADMIN, + ), + UserEntity( + id = UUID.randomUUID(), + email = "email-3@example.com", + password = encoder.encode("pass3"), + role = UserRole.USER, + ), + ) + + fun save(user: UserEntity): Boolean { + val updated = user.copy(password = encoder.encode(user.password)) + + return users.add(updated) + } + + fun findByEmail(email: String): UserEntity? = users.firstOrNull { it.email == email } + + fun findAll(): Set = users + + fun findByUUID(uuid: UUID): UserEntity? = users.firstOrNull { it.id == uuid } + + fun deleteByUUID(uuid: UUID): Boolean { + val foundUser = findByUUID(uuid) + return foundUser?.let { users.removeIf { it.id == uuid } } ?: false + } +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/user/UserService.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/user/UserService.kt index 128db7c..9e8ba0e 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/user/UserService.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/domain/user/UserService.kt @@ -1,5 +1,22 @@ package com.vndevteam.kotlinwebspringboot3.domain.user +import com.vndevteam.kotlinwebspringboot3.application.user.UserEntity +import java.util.UUID import org.springframework.stereotype.Service -@Service class UserService {} +@Service +class UserService(private val userRepository: UserRepository) { + fun createUser(user: UserEntity): UserEntity? { + val found = userRepository.findByEmail(user.email) + return if (found == null) { + userRepository.save(user) + user + } else null + } + + fun findByUUID(uuid: UUID): UserEntity? = userRepository.findByUUID(uuid) + + fun findAll(): List = userRepository.findAll().toList() + + fun deleteByUUID(uuid: UUID): Boolean = userRepository.deleteByUUID(uuid) +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/JwtAuthFilter.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/JwtAuthFilter.kt new file mode 100644 index 0000000..e6f1a69 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/JwtAuthFilter.kt @@ -0,0 +1,49 @@ +package com.vndevteam.kotlinwebspringboot3.infrastructure.config + +import com.vndevteam.kotlinwebspringboot3.common.constants.SecurityConstants +import com.vndevteam.kotlinwebspringboot3.domain.auth.CustomUserDetailsService +import com.vndevteam.kotlinwebspringboot3.domain.auth.TokenService +import com.vndevteam.kotlinwebspringboot3.util.doesNotContainBearerToken +import com.vndevteam.kotlinwebspringboot3.util.extractTokenValue +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource +import org.springframework.stereotype.Component +import org.springframework.web.filter.OncePerRequestFilter + +@Component +class JwtAuthFilter( + private val userDetailsService: CustomUserDetailsService, + private val tokenService: TokenService +) : OncePerRequestFilter() { + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + val authHeader: String? = request.getHeader(SecurityConstants.HEADER_ACCESS_TOKEN) + if (authHeader.doesNotContainBearerToken()) { + filterChain.doFilter(request, response) + return + } + + val jwtToken = authHeader!!.extractTokenValue() + val email = tokenService.extractEmail(jwtToken) + if (email != null && SecurityContextHolder.getContext().authentication == null) { + val foundUser = userDetailsService.loadUserByUsername(email) + if (tokenService.isValid(jwtToken, foundUser)) updateContext(foundUser, request) + + filterChain.doFilter(request, response) + } + } + + private fun updateContext(foundUser: UserDetails, request: HttpServletRequest) { + val authToken = UsernamePasswordAuthenticationToken(foundUser, null, foundUser.authorities) + authToken.details = WebAuthenticationDetailsSource().buildDetails(request) + SecurityContextHolder.getContext().authentication = authToken + } +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/JwtProperties.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/JwtProperties.kt new file mode 100644 index 0000000..0602587 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/JwtProperties.kt @@ -0,0 +1,10 @@ +package com.vndevteam.kotlinwebspringboot3.infrastructure.config + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties("jwt") +data class JwtProperties( + val key: String, + val jwtTokenExpiration: Long, + val refreshTokenExpiration: Long, +) diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/LocaleConfig.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/LocaleConfig.kt index 7e19df4..aff28fc 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/LocaleConfig.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/LocaleConfig.kt @@ -19,7 +19,7 @@ class LocaleConfig { fun acceptHeaderLocaleResolver(): LocaleResolver { val localeResolver = AcceptHeaderLocaleResolver() if (defaultLocale.isNotBlank()) { - localeResolver.setDefaultLocale(Locale(defaultLocale)) + localeResolver.setDefaultLocale(Locale.forLanguageTag(defaultLocale)) } return localeResolver } diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/MessageConfig.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/MessageConfig.kt index 96af00a..6eaa95e 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/MessageConfig.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/MessageConfig.kt @@ -1,6 +1,6 @@ package com.vndevteam.kotlinwebspringboot3.infrastructure.config -import com.vndevteam.kotlinwebspringboot3.infrastructure.util.MsgUtils +import com.vndevteam.kotlinwebspringboot3.util.MsgUtils import jakarta.annotation.PostConstruct import org.springframework.context.MessageSource import org.springframework.context.annotation.Bean diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/SecurityConfig.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/SecurityConfig.kt index 4934cb2..88c5829 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/SecurityConfig.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/SecurityConfig.kt @@ -2,19 +2,38 @@ package com.vndevteam.kotlinwebspringboot3.infrastructure.config import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.http.HttpMethod +import org.springframework.security.authentication.AuthenticationProvider import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity -import org.springframework.security.web.SecurityFilterChain +import org.springframework.security.config.http.SessionCreationPolicy +import org.springframework.security.web.DefaultSecurityFilterChain +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter @Configuration @EnableWebSecurity -class SecurityConfig { +class SecurityConfig(private val authenticationProvider: AuthenticationProvider) { @Bean - fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { + fun securityFilterChain( + http: HttpSecurity, + jwtAuthFilter: JwtAuthFilter + ): DefaultSecurityFilterChain { http - .authorizeHttpRequests { request -> request.anyRequest().permitAll() } - .csrf { csrf -> csrf.disable() } + .csrf { it.disable() } + .authorizeHttpRequests { + it.requestMatchers("/auth/login", "/auth/refresh") + .permitAll() + .requestMatchers(HttpMethod.POST, "/users") + .permitAll() + .requestMatchers("/users**") + .hasRole("ADMIN") + .anyRequest() + .fullyAuthenticated() + } + .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) } + .authenticationProvider(authenticationProvider) + .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter::class.java) return http.build() } diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/WebConfig.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/WebConfig.kt index 9169f48..96ec51f 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/WebConfig.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/config/WebConfig.kt @@ -1,5 +1,35 @@ package com.vndevteam.kotlinwebspringboot3.infrastructure.config +import com.vndevteam.kotlinwebspringboot3.domain.auth.CustomUserDetailsService +import com.vndevteam.kotlinwebspringboot3.domain.user.UserRepository +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.authentication.AuthenticationProvider +import org.springframework.security.authentication.dao.DaoAuthenticationProvider +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder -@Configuration class WebConfig {} +@Configuration +@EnableConfigurationProperties(JwtProperties::class) +class WebConfig { + @Bean fun encoder(): PasswordEncoder = BCryptPasswordEncoder() + + @Bean + fun userDetailsService(userRepository: UserRepository): UserDetailsService = + CustomUserDetailsService(userRepository) + + @Bean + fun authenticationProvider(userRepository: UserRepository): AuthenticationProvider = + DaoAuthenticationProvider().also { + it.setUserDetailsService(userDetailsService(userRepository)) + it.setPasswordEncoder(encoder()) + } + + @Bean + fun authenticationManager(config: AuthenticationConfiguration): AuthenticationManager = + config.authenticationManager +} diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/ExceptionsHandler.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/ExceptionsHandler.kt index eb90f89..5f7caac 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/ExceptionsHandler.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/exception/ExceptionsHandler.kt @@ -1,7 +1,8 @@ package com.vndevteam.kotlinwebspringboot3.infrastructure.exception -import com.vndevteam.kotlinwebspringboot3.infrastructure.exception.error.ErrorDetail -import com.vndevteam.kotlinwebspringboot3.infrastructure.exception.error.ErrorResponseDto +import com.vndevteam.kotlinwebspringboot3.common.constants.ErrorConstants +import com.vndevteam.kotlinwebspringboot3.common.error.ErrorDetail +import com.vndevteam.kotlinwebspringboot3.common.error.ErrorResponseDto import com.vndevteam.kotlinwebspringboot3.util.DateTimeUtils import jakarta.validation.ConstraintViolationException import java.io.PrintWriter @@ -194,7 +195,6 @@ class ExceptionsHandler : ResponseEntityExceptionHandler() { status: HttpStatusCode, request: WebRequest ): ResponseEntity { - val zxc = 123 return ResponseEntity( ErrorResponseDto( timestamp = DateTimeUtils.getNow(), diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/BaseStringUtils.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/BaseStringUtils.kt new file mode 100644 index 0000000..7ba60a6 --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/BaseStringUtils.kt @@ -0,0 +1,7 @@ +package com.vndevteam.kotlinwebspringboot3.util + +import com.vndevteam.kotlinwebspringboot3.common.constants.SecurityConstants + +fun String?.doesNotContainBearerToken() = this == null || !this.startsWith(SecurityConstants.BEARER) + +fun String.extractTokenValue() = this.substringAfter(SecurityConstants.BEARER) diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/DateTimeUtils.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/DateTimeUtils.kt index ae196e1..5846f0a 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/DateTimeUtils.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/DateTimeUtils.kt @@ -28,5 +28,22 @@ class DateTimeUtils { fun dateTimeFormatter(format: String): DateTimeFormatter { return DateTimeFormatter.ofPattern(format, Locale.JAPAN) } + + fun getDateNow(): Date { + return getDateNow(getNow()) + } + + fun getDateNow(localDateTime: LocalDateTime?): Date { + var time = localDateTime + if (time == null) { + time = getNow() + } + + return convertLocalDateTimeToDate(time) + } + + fun convertLocalDateTimeToDate(localDateTime: LocalDateTime): Date { + return Date.from(localDateTime.atZone(zoneId).toInstant()) + } } } diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/DtoUtils.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/DtoUtils.kt new file mode 100644 index 0000000..ca0bfbf --- /dev/null +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/DtoUtils.kt @@ -0,0 +1,35 @@ +package com.vndevteam.kotlinwebspringboot3.util + +import com.vndevteam.kotlinwebspringboot3.application.post.PostEntity +import com.vndevteam.kotlinwebspringboot3.application.post.PostResDto +import com.vndevteam.kotlinwebspringboot3.application.user.UserEntity +import com.vndevteam.kotlinwebspringboot3.application.user.UserReqDto +import com.vndevteam.kotlinwebspringboot3.application.user.UserResDto +import com.vndevteam.kotlinwebspringboot3.common.enums.UserRole +import java.util.UUID +import org.springframework.security.core.userdetails.User +import org.springframework.security.core.userdetails.UserDetails + +fun PostEntity.toPostResDto(): PostResDto = + PostResDto( + id = this.id, + title = this.title, + content = this.content, + ) + +fun UserEntity.toUserResDto(): UserResDto = + UserResDto( + uuid = this.id, + email = this.email, + ) + +fun UserReqDto.toUserEntity(): UserEntity = + UserEntity( + id = UUID.randomUUID(), + email = this.email, + password = this.password, + role = UserRole.USER, + ) + +fun UserEntity.toUserDetails(): UserDetails = + User.builder().username(this.email).password(this.password).roles(this.role.name).build() diff --git a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/util/MsgUtils.kt b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/MsgUtils.kt similarity index 61% rename from kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/util/MsgUtils.kt rename to kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/MsgUtils.kt index 18963da..3b5be11 100644 --- a/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/util/MsgUtils.kt +++ b/kotlin-web-spring-boot-3/src/main/kotlin/com/vndevteam/kotlinwebspringboot3/util/MsgUtils.kt @@ -1,6 +1,6 @@ -package com.vndevteam.kotlinwebspringboot3.infrastructure.util +package com.vndevteam.kotlinwebspringboot3.util -import com.vndevteam.kotlinwebspringboot3.domain.enums.MESSAGE +import com.vndevteam.kotlinwebspringboot3.common.enums.Message import org.springframework.context.MessageSource import org.springframework.context.i18n.LocaleContextHolder @@ -8,14 +8,14 @@ object MsgUtils { private lateinit var messageSource: MessageSource fun setMessageSource(messageSource: MessageSource) { - this.messageSource = messageSource + MsgUtils.messageSource = messageSource } - fun getMessage(code: MESSAGE): String { + fun getMessage(code: Message): String { return messageSource.getMessage(code.code, null, LocaleContextHolder.getLocale()) } - fun getMessage(code: MESSAGE, vararg args: Any): String { + fun getMessage(code: Message, vararg args: Any): String { return messageSource.getMessage(code.code, args, LocaleContextHolder.getLocale()) } } diff --git a/kotlin-web-spring-boot-3/src/main/resources/application-develop.yml b/kotlin-web-spring-boot-3/src/main/resources/application-develop.yml index 4b1638d..63269ea 100644 --- a/kotlin-web-spring-boot-3/src/main/resources/application-develop.yml +++ b/kotlin-web-spring-boot-3/src/main/resources/application-develop.yml @@ -6,8 +6,8 @@ server: spring: datasource: url: jdbc:postgresql://localhost:5432/postgres - username: root - password: 12345678 + username: postgres + password: postgres driver-class-name: org.postgresql.Driver management: @@ -17,6 +17,11 @@ management: exposure: include: "*" +jwt: + key: vndevteam-kotlin-web-spring-boot-base-jvm + jwt-token-expiration: 5 + refresh-token-expiration: 30 + app: locale: default: ja # ja, vi, en,... diff --git a/kotlin-web-spring-boot-3/src/main/resources/application-production.yml b/kotlin-web-spring-boot-3/src/main/resources/application-production.yml index 0bc0e72..5283be5 100644 --- a/kotlin-web-spring-boot-3/src/main/resources/application-production.yml +++ b/kotlin-web-spring-boot-3/src/main/resources/application-production.yml @@ -10,6 +10,11 @@ spring: password: 12345678 driver-class-name: org.postgresql.Driver +jwt: + key: vndevteam-kotlin-web-spring-boot-base-jvm + jwt-token-expiration: 5 + refresh-token-expiration: 30 + management: endpoints: web: diff --git a/kotlin-web-spring-boot-3/src/main/resources/application-staging.yml b/kotlin-web-spring-boot-3/src/main/resources/application-staging.yml index b055dcd..0e33eb2 100644 --- a/kotlin-web-spring-boot-3/src/main/resources/application-staging.yml +++ b/kotlin-web-spring-boot-3/src/main/resources/application-staging.yml @@ -10,6 +10,11 @@ spring: password: 12345678 driver-class-name: org.postgresql.Driver +jwt: + key: vndevteam-kotlin-web-spring-boot-base-jvm + jwt-token-expiration: 5 + refresh-token-expiration: 30 + management: endpoints: web: diff --git a/kotlin-web-spring-boot-3/src/test/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/logging/RequestLoggingFilterTest.kt b/kotlin-web-spring-boot-3/src/test/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/logging/RequestLoggingFilterTest.kt index 0b7f7eb..00416e1 100644 --- a/kotlin-web-spring-boot-3/src/test/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/logging/RequestLoggingFilterTest.kt +++ b/kotlin-web-spring-boot-3/src/test/kotlin/com/vndevteam/kotlinwebspringboot3/infrastructure/logging/RequestLoggingFilterTest.kt @@ -5,9 +5,9 @@ import org.apache.commons.logging.Log import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito.mock +import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.kotlin.mock -import org.mockito.kotlin.never import org.springframework.mock.web.MockHttpServletRequest import org.springframework.test.util.ReflectionTestUtils