refactor: simplify

This commit is contained in:
SOR
2026-06-25 20:48:54 +08:00
parent cc0d8da750
commit bc07b17f20
13 changed files with 176 additions and 145 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$/backend" />
</component>
<component name="ProjectRootManager" version="2">
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="25" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+1
View File
@@ -3,6 +3,7 @@
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Springboot4-Vue3.iml" filepath="$PROJECT_DIR$/.idea/Springboot4-Vue3.iml" />
<module fileurl="file://$PROJECT_DIR$/frontend/frontend.iml" filepath="$PROJECT_DIR$/frontend/frontend.iml" />
</modules>
</component>
</project>
Generated
-1
View File
@@ -2,6 +2,5 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/ItStudio-CareerPlanningPlatform" vcs="Git" />
</component>
</project>
@@ -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<ApiResponse<Any?>> {
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<ApiResponse<Any?>> {
logger.info("Trying to register")
return service.registerEmailAccount(vo)
.map { messageHandler(it) }
.map(::messageHandler)
}
@PostMapping("/reset")
fun emailResetPassword(@RequestBody @Valid vo: PasswordResetVO): Mono<ApiResponse<Any?>> {
logger.info("Trying to reset password")
return service.resetEmailAccountPassword(vo)
.map { messageHandler(it) }
.map(::messageHandler)
}
@GetMapping("/logout")
@@ -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
)
@@ -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,
}
@@ -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<Void> {
@@ -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)
}
}
}
@@ -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
@@ -11,5 +11,5 @@ interface AccountService : IService<Account>, ReactiveUserDetailsService {
fun findAccountByEmail(email: String): Mono<Account>
fun existAccountByName(username: String): Mono<Boolean>
fun existAccountByEmail(email: String): Mono<Boolean>
fun resetPasswordByEmail(email: String, encodedPassword: String): Mono<String>
fun resetPasswordByEmail(email: String, encodedPassword: String): Mono<Void>
}
@@ -27,70 +27,87 @@ class AccountServiceImpl : ServiceImpl<AccountMapper, Account>(), AccountService
}
}
override fun findAccountByName(username: String): Mono<Account> {
return Mono.fromCallable {
logger.info("Find account with name $username")
this.ktQuery()
override fun findAccountByName(username: String): Mono<Account> =
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<Account> {
return Mono.fromCallable {
logger.info("Find account with email $email")
this.ktQuery()
override fun findAccountByEmail(email: String): Mono<Account> =
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<Account> {
return findAccountByEmail(text)
.onErrorResume { findAccountByName(text) }
}
private fun findAccount(
fieldName: String,
value: String,
query: () -> Account?
): Mono<Account> =
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<Boolean> {
return Mono.fromCallable {
logger.info("Determine if name $username exists")
this.ktQuery()
override fun findAccountByNameOrEmail(text: String): Mono<Account> =
findAccountByEmail(text).onErrorResume(
UsernameNotFoundException::class.java
) {
findAccountByName(text)
}
override fun existAccountByName(username: String): Mono<Boolean> =
Mono.fromCallable {
logger.info("Determine if name {} exists", username)
ktQuery()
.eq(Account::username, username)
.exists()
}
}
override fun existAccountByEmail(email: String): Mono<Boolean> {
return Mono.fromCallable {
logger.info("Determine if email $email exists")
this.ktQuery()
override fun existAccountByEmail(email: String): Mono<Boolean> =
Mono.fromCallable {
logger.info("Determine if email {} exists", email)
ktQuery()
.eq(Account::email, email)
.exists()
}
}
override fun resetPasswordByEmail(email: String, encodedPassword: String): Mono<String> {
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<Void> =
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"
)
)
}
}
}
}
}
@@ -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<String> {
fun askEmailVerifyCode(type: EmailType, email: String, ip: String): Mono<String> {
return verifyLimit(ip).flatMap { allowed ->
if (allowed) {
val code = (100000..999999).random().toString()
@@ -52,32 +53,27 @@ class AuthorizationService(
fun registerEmailAccount(vo: EmailRegisterVO): Mono<String> {
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<String> {
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<ReloginVO>? {
@@ -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"))
}
@@ -32,4 +32,13 @@ spring:
url: jdbc:mysql://localhost:3306/kotlin_springboot
#username: root
#password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
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
-9
View File
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="index" level="application" />
</component>
</module>