From bc07b17f20a13d171c2f31fa901503b11113f402 Mon Sep 17 00:00:00 2001 From: SOR <3272194354@qq.com> Date: Thu, 25 Jun 2026 20:48:54 +0800 Subject: [PATCH] refactor: simplify --- .idea/misc.xml | 2 +- .idea/modules.xml | 1 + .idea/vcs.xml | 1 - .../controller/v1/AuthorizeController.kt | 17 +-- .../entity/dto/EmailVerificationMsg.kt | 3 +- .../sor2171/backend/entity/enums/EmailType.kt | 13 ++ .../sor2171/backend/filter/FlowLimitFilter.kt | 39 +++--- .../backend/listener/MailQueueListener.kt | 19 +-- .../sor2171/backend/service/AccountService.kt | 2 +- .../service/impl/AccountServiceImpl.kt | 113 ++++++++++-------- .../service/v1/AuthorizationService.kt | 91 +++++++------- .../main/resources/application-example.yaml | 11 +- frontend/frontend.iml | 9 -- 13 files changed, 176 insertions(+), 145 deletions(-) create mode 100644 backend/src/main/kotlin/com/github/sor2171/backend/entity/enums/EmailType.kt delete mode 100644 frontend/frontend.iml diff --git a/.idea/misc.xml b/.idea/misc.xml index 99e7ccd..603e70a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 9f72987..34dd4d7 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,6 +3,7 @@ + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index b35f037..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/controller/v1/AuthorizeController.kt b/backend/src/main/kotlin/com/github/sor2171/backend/controller/v1/AuthorizeController.kt index 1afd7c3..3f16a6c 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/controller/v1/AuthorizeController.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/controller/v1/AuthorizeController.kt @@ -1,21 +1,16 @@ package com.github.sor2171.backend.controller.v1 import com.github.sor2171.backend.entity.ApiResponse +import com.github.sor2171.backend.entity.enums.EmailType import com.github.sor2171.backend.entity.vo.request.EmailRegisterVO import com.github.sor2171.backend.entity.vo.request.PasswordResetVO import com.github.sor2171.backend.service.v1.AuthorizationService import jakarta.validation.Valid import jakarta.validation.constraints.Email import jakarta.validation.constraints.NotBlank -import jakarta.validation.constraints.Pattern import org.slf4j.LoggerFactory import org.springframework.validation.annotation.Validated -import org.springframework.web.bind.annotation.GetMapping -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.RequestParam -import org.springframework.web.bind.annotation.RestController +import org.springframework.web.bind.annotation.* import org.springframework.web.server.ServerWebExchange import reactor.core.publisher.Mono @@ -30,27 +25,27 @@ class AuthorizeController( @GetMapping("/ask-code") fun askVerifyCode( @RequestParam @NotBlank @Email email: String, - @RequestParam @Pattern(regexp = "(register|reset)") type: String, + @RequestParam type: EmailType, exchange: ServerWebExchange ): Mono> { logger.info("Trying to ask code") val ip = exchange.request.remoteAddress?.address?.hostAddress ?: "unknown" return service.askEmailVerifyCode(type, email, ip) - .map { messageHandler(it) } + .map(::messageHandler) } @PostMapping("/register") fun emailRegister(@RequestBody @Valid vo: EmailRegisterVO): Mono> { logger.info("Trying to register") return service.registerEmailAccount(vo) - .map { messageHandler(it) } + .map(::messageHandler) } @PostMapping("/reset") fun emailResetPassword(@RequestBody @Valid vo: PasswordResetVO): Mono> { logger.info("Trying to reset password") return service.resetEmailAccountPassword(vo) - .map { messageHandler(it) } + .map(::messageHandler) } @GetMapping("/logout") diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/entity/dto/EmailVerificationMsg.kt b/backend/src/main/kotlin/com/github/sor2171/backend/entity/dto/EmailVerificationMsg.kt index 38cb443..9de63da 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/entity/dto/EmailVerificationMsg.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/entity/dto/EmailVerificationMsg.kt @@ -1,10 +1,11 @@ package com.github.sor2171.backend.entity.dto +import com.github.sor2171.backend.entity.enums.EmailType import kotlinx.serialization.Serializable @Serializable data class EmailVerificationMsg( val email: String, - val type: String, + val type: EmailType, val code: String ) \ No newline at end of file diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/entity/enums/EmailType.kt b/backend/src/main/kotlin/com/github/sor2171/backend/entity/enums/EmailType.kt new file mode 100644 index 0000000..20560d7 --- /dev/null +++ b/backend/src/main/kotlin/com/github/sor2171/backend/entity/enums/EmailType.kt @@ -0,0 +1,13 @@ +package com.github.sor2171.backend.entity.enums + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +enum class EmailType { + @SerialName("register") + REGISTER, + + @SerialName("reset") + RESET, +} \ No newline at end of file diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/filter/FlowLimitFilter.kt b/backend/src/main/kotlin/com/github/sor2171/backend/filter/FlowLimitFilter.kt index 7cdcdf7..eb340de 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/filter/FlowLimitFilter.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/filter/FlowLimitFilter.kt @@ -3,6 +3,7 @@ package com.github.sor2171.backend.filter import com.github.sor2171.backend.entity.ApiResponse import com.github.sor2171.backend.entity.toJsonString import com.github.sor2171.backend.utils.Const +import org.springframework.beans.factory.annotation.Value import org.springframework.core.annotation.Order import org.springframework.data.redis.core.ReactiveStringRedisTemplate import org.springframework.http.HttpStatus @@ -17,7 +18,14 @@ import java.time.Duration @Component @Order(Const.FLOW_LIMIT_ORDER) class FlowLimitFilter( - private val template: ReactiveStringRedisTemplate + private val template: ReactiveStringRedisTemplate, + + @param:Value($$"${app.flow-limit.count}") + private val limitCount: Int, + @param:Value($$"${app.flow-limit.limit-time}") + private val limitTime: Int, + @param:Value($$"${app.flow-limit.ban-time}") + private val expireTime: Int, ) : WebFilter { override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono { @@ -49,21 +57,22 @@ class FlowLimitFilter( if (isBlocked) { Mono.just(false) } else { - template.hasKey(counterKey).flatMap { hasCounter -> - if (hasCounter) { - template.opsForValue().increment(counterKey).flatMap { count -> - if (count > 10) { - template.opsForValue() - .set(blockKey, "1", Duration.ofMinutes(1)) - .thenReturn(false) - } else { - Mono.just(true) - } - } - } else { + template.opsForValue().increment(counterKey).flatMap { count -> + if (count == 1L) { + template.expire( + counterKey, + Duration.ofSeconds(limitTime.toLong()) + ).thenReturn(true) + } else if (count > limitCount) { template.opsForValue() - .set(counterKey, "1", Duration.ofSeconds(3)) - .thenReturn(true) + .set( + blockKey, + "1", + Duration.ofSeconds(expireTime.toLong()) + ) + .thenReturn(false) + } else { + Mono.just(true) } } } diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/listener/MailQueueListener.kt b/backend/src/main/kotlin/com/github/sor2171/backend/listener/MailQueueListener.kt index 36320f7..f442c98 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/listener/MailQueueListener.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/listener/MailQueueListener.kt @@ -1,13 +1,14 @@ package com.github.sor2171.backend.listener import com.github.sor2171.backend.entity.dto.EmailVerificationMsg +import com.github.sor2171.backend.entity.enums.EmailType import kotlinx.serialization.json.Json import org.slf4j.LoggerFactory import org.springframework.amqp.rabbit.annotation.RabbitHandler import org.springframework.amqp.rabbit.annotation.RabbitListener import org.springframework.beans.factory.annotation.Value -import org.springframework.mail.MailSender import org.springframework.mail.SimpleMailMessage +import org.springframework.mail.javamail.JavaMailSender import org.springframework.stereotype.Component @Component @@ -15,9 +16,8 @@ import org.springframework.stereotype.Component class MailQueueListener( @param:Value($$"${spring.mail.username}") private val username: String, - private val json: Json, - private val sender: MailSender + private val sender: JavaMailSender ) { private val logger = LoggerFactory.getLogger(this::class.java) @@ -28,27 +28,30 @@ class MailQueueListener( val (email, type, code) = data val message = when (type) { - "register" -> createMailMessage( + EmailType.REGISTER -> createMailMessage( "Welcome to Our Service", "Thank you for registering! Your verification code is: $code" + "\nThis code is valid for 3 minutes.", email ) - "reset" -> createMailMessage( + EmailType.RESET -> createMailMessage( "Password Reset Request", "You requested a password reset. Your verification code is: $code" + "\nThis code is valid for 3 minutes.", email ) - - else -> return } + sender.send(message) logger.info("Email sent to $email.") } - fun createMailMessage(title: String, content: String, to: String): SimpleMailMessage { + private fun createMailMessage( + title: String, + content: String, + to: String + ): SimpleMailMessage { val message = SimpleMailMessage() message.subject = title message.text = content diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/service/AccountService.kt b/backend/src/main/kotlin/com/github/sor2171/backend/service/AccountService.kt index a498ab3..693570d 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/service/AccountService.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/service/AccountService.kt @@ -11,5 +11,5 @@ interface AccountService : IService, ReactiveUserDetailsService { fun findAccountByEmail(email: String): Mono fun existAccountByName(username: String): Mono fun existAccountByEmail(email: String): Mono - fun resetPasswordByEmail(email: String, encodedPassword: String): Mono + fun resetPasswordByEmail(email: String, encodedPassword: String): Mono } \ No newline at end of file diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/service/impl/AccountServiceImpl.kt b/backend/src/main/kotlin/com/github/sor2171/backend/service/impl/AccountServiceImpl.kt index d6cbd79..2dbf893 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/service/impl/AccountServiceImpl.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/service/impl/AccountServiceImpl.kt @@ -27,70 +27,87 @@ class AccountServiceImpl : ServiceImpl(), AccountService } } - override fun findAccountByName(username: String): Mono { - return Mono.fromCallable { - logger.info("Find account with name $username") - this.ktQuery() + override fun findAccountByName(username: String): Mono = + findAccount("name", username) { + ktQuery() .eq(Account::username, username) .one() - }.flatMap { account -> - Mono.just(account) - }.switchIfEmpty(Mono.error(UsernameNotFoundException("Account with name $username not found"))) - } + } - override fun findAccountByEmail(email: String): Mono { - return Mono.fromCallable { - logger.info("Find account with email $email") - this.ktQuery() + override fun findAccountByEmail(email: String): Mono = + findAccount("email", email) { + ktQuery() .eq(Account::email, email) .one() - }.flatMap { account -> - Mono.just(account) - }.switchIfEmpty(Mono.error(UsernameNotFoundException("Account with email $email not found"))) - } + } - override fun findAccountByNameOrEmail(text: String): Mono { - return findAccountByEmail(text) - .onErrorResume { findAccountByName(text) } - } + private fun findAccount( + fieldName: String, + value: String, + query: () -> Account? + ): Mono = + Mono.fromCallable { + logger.info("Find account with {} {}", fieldName, value) + query() + }.switchIfEmpty( + Mono.error( + UsernameNotFoundException("Account with $fieldName $value not found") + ) + ) - override fun existAccountByName(username: String): Mono { - return Mono.fromCallable { - logger.info("Determine if name $username exists") - this.ktQuery() + override fun findAccountByNameOrEmail(text: String): Mono = + findAccountByEmail(text).onErrorResume( + UsernameNotFoundException::class.java + ) { + findAccountByName(text) + } + + override fun existAccountByName(username: String): Mono = + Mono.fromCallable { + logger.info("Determine if name {} exists", username) + ktQuery() .eq(Account::username, username) .exists() } - } - override fun existAccountByEmail(email: String): Mono { - return Mono.fromCallable { - logger.info("Determine if email $email exists") - this.ktQuery() + override fun existAccountByEmail(email: String): Mono = + Mono.fromCallable { + logger.info("Determine if email {} exists", email) + ktQuery() .eq(Account::email, email) .exists() } - } - override fun resetPasswordByEmail(email: String, encodedPassword: String): Mono { - return existAccountByEmail(email).flatMap { exists -> - if (!exists) { - return@flatMap Mono.just("account with the email does not exist.") - } + override fun resetPasswordByEmail( + email: String, + encodedPassword: String + ): Mono = + existAccountByEmail(email) + .flatMap { exists -> + if (!exists) { + return@flatMap Mono.error( + UsernameNotFoundException( + "Account with email $email not found" + ) + ) + } - Mono.fromCallable { - logger.info("Reset password by email $email") - this.ktUpdate() - .eq(Account::email, email) - .set(Account::password, encodedPassword) - .update() - }.flatMap { success -> - if (success) { - Mono.just("") - } else { - Mono.just("something went wrong. Please contact the administrator.") + Mono.fromCallable { + ktUpdate() + .eq(Account::email, email) + .set(Account::password, encodedPassword) + .update() + } + } + .flatMap { success -> + if (success) { + Mono.empty() + } else { + Mono.error( + IllegalStateException( + "Failed to reset password" + ) + ) } } - } - } } \ No newline at end of file diff --git a/backend/src/main/kotlin/com/github/sor2171/backend/service/v1/AuthorizationService.kt b/backend/src/main/kotlin/com/github/sor2171/backend/service/v1/AuthorizationService.kt index 3559611..005f3e5 100644 --- a/backend/src/main/kotlin/com/github/sor2171/backend/service/v1/AuthorizationService.kt +++ b/backend/src/main/kotlin/com/github/sor2171/backend/service/v1/AuthorizationService.kt @@ -2,6 +2,7 @@ package com.github.sor2171.backend.service.v1 import com.github.sor2171.backend.entity.dto.Account import com.github.sor2171.backend.entity.dto.EmailVerificationMsg +import com.github.sor2171.backend.entity.enums.EmailType import com.github.sor2171.backend.entity.vo.request.EmailRegisterVO import com.github.sor2171.backend.entity.vo.request.PasswordResetVO import com.github.sor2171.backend.entity.vo.response.ReloginVO @@ -27,7 +28,7 @@ class AuthorizationService( private val stringRedisTemplate: ReactiveStringRedisTemplate, private val encoder: PasswordEncoder ) { - fun askEmailVerifyCode(type: String, email: String, ip: String): Mono { + fun askEmailVerifyCode(type: EmailType, email: String, ip: String): Mono { return verifyLimit(ip).flatMap { allowed -> if (allowed) { val code = (100000..999999).random().toString() @@ -52,32 +53,27 @@ class AuthorizationService( fun registerEmailAccount(vo: EmailRegisterVO): Mono { val (email, code, username, password) = vo - return verifyCode(email, code).flatMap { error -> - if (!error.isBlank()) { - return@flatMap Mono.just(error) + return verifyCode(email, code) + .filter { it.isBlank() } + .switchIfEmpty(Mono.just("wrong code")) + .flatMap { + accountService.existAccountByEmail(email) } - - accountService - .existAccountByEmail(email) - .flatMap { emailExists -> - if (emailExists) { - return@flatMap Mono.just("account with the same email already exists.") - } - accountService - .existAccountByName(username) - .flatMap { usernameExists -> - if (usernameExists) { - return@flatMap Mono.just("username already exists.") - } - - val encodedPassword = encoder.encode(password) + .flatMap { emailExists -> + if (emailExists) { + Mono.just("account with the same email already exists.") + } else { + accountService.existAccountByName(username).flatMap { usernameExists -> + if (usernameExists) { + Mono.just("username already exists.") + } else { val account = Account( - null, - username, - encodedPassword!!, - email, - "user", - getCurrentDateTime() + id = null, + username = username, + password = encoder.encode(password)!!, + email = email, + role = "user", + registerTime = getCurrentDateTime() ) Mono.fromCallable { accountService.save(account) } @@ -90,31 +86,31 @@ class AuthorizationService( } } } + } } - } + } } fun resetEmailAccountPassword(vo: PasswordResetVO): Mono { val (email, code, password) = vo - return verifyCode(email, code).flatMap { error -> - if (!error.isBlank()) { - return@flatMap Mono.just(error) - } else { - val encodedPassword = encoder.encode(password) - ?: return@flatMap Const.INTERNAL_ERROR_MONO + val encodedPassword = + encoder.encode(password) + ?: return Const.INTERNAL_ERROR_MONO - return@flatMap accountService - .resetPasswordByEmail(email, encodedPassword) - .flatMap { text -> - if (text.isBlank()) { - stringRedisTemplate - .delete(Const.VERIFY_EMAIL_DATA + email) - .then(Mono.just(text)) - } else Mono.just(text) - } - } - } + return verifyCode(email, code) + .then( + accountService.resetPasswordByEmail( + email, + encodedPassword + ) + ) + .then( + stringRedisTemplate.delete( + Const.VERIFY_EMAIL_DATA + email + ) + ) + .thenReturn("Password reset successfully") } fun jwtTokenRelogin(headerToken: String?): Mono? { @@ -144,12 +140,9 @@ class AuthorizationService( return stringRedisTemplate.opsForValue() .get(Const.VERIFY_EMAIL_DATA + email) .flatMap { code -> - when { - receivedCode == null || code != receivedCode -> - Mono.just("verify code is wrong.") - - else -> Mono.just("") - } + if (receivedCode == null || code != receivedCode) + Mono.just("verify code is wrong.") + else Mono.just("") } .switchIfEmpty(Mono.just("verify code has not been sent")) } diff --git a/backend/src/main/resources/application-example.yaml b/backend/src/main/resources/application-example.yaml index fa18fbf..fe1d774 100644 --- a/backend/src/main/resources/application-example.yaml +++ b/backend/src/main/resources/application-example.yaml @@ -32,4 +32,13 @@ spring: url: jdbc:mysql://localhost:3306/kotlin_springboot #username: root #password: 123456 - driver-class-name: com.mysql.cj.jdbc.Driver \ No newline at end of file + driver-class-name: com.mysql.cj.jdbc.Driver + +mybatis-plus: + type-handlers-package: com.github.sor2171.backend.config + +app: + flow-limit: + count: 1000 + limit-time: 3 + ban-time: 60 \ No newline at end of file diff --git a/frontend/frontend.iml b/frontend/frontend.iml deleted file mode 100644 index 36f463a..0000000 --- a/frontend/frontend.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file