From 98263732ca23ffa117c6729d12d1bd03f20eefc1 Mon Sep 17 00:00:00 2001 From: xds Date: Thu, 6 Mar 2025 18:55:12 +0300 Subject: [PATCH] login fix and spaces creation --- .../budgerapp/controllers/AuthController.kt | 11 +- .../luminic/budgerapp/services/AuthService.kt | 117 +++++++++--------- .../budgerapp/services/SpaceService.kt | 7 +- .../budgerapp/services/TokenService.kt | 5 +- .../space/luminic/budgerapp/utils/JWTUtil.kt | 8 -- 5 files changed, 71 insertions(+), 77 deletions(-) diff --git a/src/main/kotlin/space/luminic/budgerapp/controllers/AuthController.kt b/src/main/kotlin/space/luminic/budgerapp/controllers/AuthController.kt index 5026624..5f1fd4b 100644 --- a/src/main/kotlin/space/luminic/budgerapp/controllers/AuthController.kt +++ b/src/main/kotlin/space/luminic/budgerapp/controllers/AuthController.kt @@ -30,18 +30,19 @@ class AuthController( @PostMapping("/login") suspend fun login(@RequestBody request: AuthRequest): Map { - return authService.login(request.username, request.password) - .map { token -> mapOf("token" to token) }.awaitFirst() + val token = authService.login(request.username, request.password) + return mapOf("token" to token) } @PostMapping("/register") - fun register(@RequestBody request: RegisterRequest): Mono { + suspend fun register(@RequestBody request: RegisterRequest): User { return authService.register(request.username, request.password, request.firstName) } @PostMapping("/tgLogin") - fun tgLogin(@RequestHeader("X-Tg-Id") tgId: String): Mono> { - return authService.tgLogin(tgId).map { token -> mapOf("token" to token) } + suspend fun tgLogin(@RequestHeader("X-Tg-Id") tgId: String): Map { + val token = authService.tgLogin(tgId) + return mapOf("token" to token) } diff --git a/src/main/kotlin/space/luminic/budgerapp/services/AuthService.kt b/src/main/kotlin/space/luminic/budgerapp/services/AuthService.kt index bcbd427..d778e2e 100644 --- a/src/main/kotlin/space/luminic/budgerapp/services/AuthService.kt +++ b/src/main/kotlin/space/luminic/budgerapp/services/AuthService.kt @@ -1,7 +1,10 @@ package space.luminic.budgerapp.services import kotlinx.coroutines.reactive.awaitFirstOrNull +import kotlinx.coroutines.reactor.awaitSingle +import kotlinx.coroutines.reactor.awaitSingleOrNull import org.springframework.cache.annotation.Cacheable +import org.springframework.security.core.userdetails.UsernameNotFoundException import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.stereotype.Service import reactor.core.publisher.Mono @@ -22,72 +25,66 @@ class AuthService( private val jwtUtil: JWTUtil, private val userService: UserService -) { +) { private val passwordEncoder = BCryptPasswordEncoder() - fun login(username: String, password: String): Mono { - return userRepository.findByUsername(username) - .flatMap { user -> - if (passwordEncoder.matches(password, user.password)) { - val token = jwtUtil.generateToken(user.username!!) - val expireAt = Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 10) - tokenService.saveToken( - token = token, - username = username, - expiresAt = LocalDateTime.ofInstant( - expireAt.toInstant(), - ZoneId.systemDefault() - ) - ).thenReturn(token) - } else { - Mono.error(AuthException("Invalid credentials")) - } - } - } - - fun tgLogin(tgId: String): Mono { - return userRepository.findByTgId(tgId) - .switchIfEmpty(Mono.error(AuthException("Invalid credentials"))) - .flatMap { user -> - println("here") - val token = jwtUtil.generateToken(user.username!!) - val expireAt = Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 10) - tokenService.saveToken( - token = token, - username = user.username, - expiresAt = LocalDateTime.ofInstant( - expireAt.toInstant(), - ZoneId.systemDefault() - ) - ).thenReturn(token) - - } - } - - fun register(username: String, password: String, firstName: String): Mono { - return userRepository.findByUsername(username) - .flatMap { Mono.error(IllegalArgumentException("User with username '$username' already exists")) } // Ошибка, если пользователь уже существует - .switchIfEmpty( - Mono.defer { - val newUser = User( - username = username, - password = passwordEncoder.encode(password), // Шифрование пароля - firstName = firstName, - roles = mutableListOf("USER") - ) - userRepository.save(newUser).map { user -> - user.password = null - user - } // Сохранение нового пользователя - - } + suspend fun login(username: String, password: String): String { + val user = userRepository.findByUsername(username).awaitFirstOrNull() + ?: throw UsernameNotFoundException("Пользователь не найден") + return if (passwordEncoder.matches(password, user.password)) { + val token = jwtUtil.generateToken(user.username!!) + val expireAt = Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 10) + tokenService.saveToken( + token = token, + username = username, + expiresAt = LocalDateTime.ofInstant( + expireAt.toInstant(), + ZoneId.systemDefault() + ) ) + token + } else { + throw IllegalArgumentException("Ошибка логина или пароля") + } + } + + suspend fun tgLogin(tgId: String): String { + val user = + userRepository.findByTgId(tgId).awaitSingleOrNull() ?: throw UsernameNotFoundException("Пользователь не найден") + + val token = jwtUtil.generateToken(user.username!!) + val expireAt = Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 10) + tokenService.saveToken( + token = token, + username = user.username, + expiresAt = LocalDateTime.ofInstant( + expireAt.toInstant(), + ZoneId.systemDefault() + ) + ) + return token + + } + + suspend fun register(username: String, password: String, firstName: String): User { + val user = userRepository.findByUsername(username).awaitSingleOrNull() + if (user == null) { + var newUser = User( + username = username, + password = passwordEncoder.encode(password), // Шифрование пароля + firstName = firstName, + roles = mutableListOf("USER") + ) + newUser = userRepository.save(newUser).awaitSingle() + newUser.password = null + return newUser + } else throw IllegalArgumentException("Пользователь уже зарегистрирован") } @Cacheable(cacheNames = ["tokens"], key = "#token") - suspend fun isTokenValid(token: String): User { - val tokenDetails = tokenService.getToken(token).awaitFirstOrNull() ?: throw AuthException("Invalid token") + suspend fun isTokenValid(token: String): User { + val tokenDetails = tokenService.getToken(token).awaitFirstOrNull() ?: throw AuthException("Токен не валиден") when { tokenDetails.status == TokenStatus.ACTIVE && tokenDetails.expiresAt.isAfter(LocalDateTime.now()) -> { return userService.getByUserNameWoPass(tokenDetails.username) @@ -95,7 +92,7 @@ class AuthService( else -> { tokenService.revokeToken(tokenDetails.token) - throw AuthException("Token expired or inactive") + throw AuthException("Токен истек или не валиден") } } } diff --git a/src/main/kotlin/space/luminic/budgerapp/services/SpaceService.kt b/src/main/kotlin/space/luminic/budgerapp/services/SpaceService.kt index 0ec30b0..efc61ae 100644 --- a/src/main/kotlin/space/luminic/budgerapp/services/SpaceService.kt +++ b/src/main/kotlin/space/luminic/budgerapp/services/SpaceService.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactive.awaitFirstOrNull +import kotlinx.coroutines.reactive.awaitLast import kotlinx.coroutines.reactive.awaitSingle import kotlinx.coroutines.reactor.awaitSingleOrNull import org.bson.Document @@ -18,6 +19,7 @@ import space.luminic.budgerapp.configs.AuthException import space.luminic.budgerapp.models.* import space.luminic.budgerapp.repos.* import java.time.LocalDateTime +import java.time.ZoneId import java.util.UUID @Service @@ -92,7 +94,8 @@ class SpaceService( username = userDoc.getString("username"), firstName = userDoc.getString("firstName") ) - }.toMutableList() + }.toMutableList(), + createdAt = doc.getDate("createdAt").toInstant().atZone(ZoneId.systemDefault()).toLocalDate() ) } }.awaitFirst() @@ -120,7 +123,7 @@ class SpaceService( .map { category -> category.copy(id = null, space = savedSpace) // Создаем новую копию } - categoryRepo.saveAll(categories).awaitSingle() + categoryRepo.saveAll(categories).awaitLast() savedSpace } diff --git a/src/main/kotlin/space/luminic/budgerapp/services/TokenService.kt b/src/main/kotlin/space/luminic/budgerapp/services/TokenService.kt index e1ec91d..c39f4b0 100644 --- a/src/main/kotlin/space/luminic/budgerapp/services/TokenService.kt +++ b/src/main/kotlin/space/luminic/budgerapp/services/TokenService.kt @@ -1,5 +1,6 @@ package space.luminic.budgerapp.services +import kotlinx.coroutines.reactor.awaitSingle import org.springframework.cache.annotation.CacheEvict import org.springframework.stereotype.Service import reactor.core.publisher.Mono @@ -12,14 +13,14 @@ import java.time.LocalDateTime class TokenService(private val tokenRepository: TokenRepo) { @CacheEvict("tokens", allEntries = true) - fun saveToken(token: String, username: String, expiresAt: LocalDateTime): Mono { + suspend fun saveToken(token: String, username: String, expiresAt: LocalDateTime): Token { val newToken = Token( token = token, username = username, issuedAt = LocalDateTime.now(), expiresAt = expiresAt ) - return tokenRepository.save(newToken) + return tokenRepository.save(newToken).awaitSingle() } fun getToken(token: String): Mono { diff --git a/src/main/kotlin/space/luminic/budgerapp/utils/JWTUtil.kt b/src/main/kotlin/space/luminic/budgerapp/utils/JWTUtil.kt index 8f1654b..79f9d1e 100644 --- a/src/main/kotlin/space/luminic/budgerapp/utils/JWTUtil.kt +++ b/src/main/kotlin/space/luminic/budgerapp/utils/JWTUtil.kt @@ -24,14 +24,6 @@ class JWTUtil(private val tokenService: TokenService) { .setExpiration(expireAt) // 10 дней .signWith(key) .compact() - tokenService.saveToken( - token, - username, - LocalDateTime.from( - expireAt.toInstant().atZone(ZoneId.systemDefault()) - .toLocalDateTime() - ) - ) return token }