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"> <component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$/backend" /> <file type="web" url="file://$PROJECT_DIR$/backend" />
</component> </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" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>
+1
View File
@@ -3,6 +3,7 @@
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Springboot4-Vue3.iml" filepath="$PROJECT_DIR$/.idea/Springboot4-Vue3.iml" /> <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> </modules>
</component> </component>
</project> </project>
Generated
-1
View File
@@ -2,6 +2,5 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" /> <mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/ItStudio-CareerPlanningPlatform" vcs="Git" />
</component> </component>
</project> </project>
@@ -1,21 +1,16 @@
package com.github.sor2171.backend.controller.v1 package com.github.sor2171.backend.controller.v1
import com.github.sor2171.backend.entity.ApiResponse 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.EmailRegisterVO
import com.github.sor2171.backend.entity.vo.request.PasswordResetVO import com.github.sor2171.backend.entity.vo.request.PasswordResetVO
import com.github.sor2171.backend.service.v1.AuthorizationService import com.github.sor2171.backend.service.v1.AuthorizationService
import jakarta.validation.Valid import jakarta.validation.Valid
import jakarta.validation.constraints.Email import jakarta.validation.constraints.Email
import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Pattern
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.validation.annotation.Validated import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.*
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.server.ServerWebExchange import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
@@ -30,27 +25,27 @@ class AuthorizeController(
@GetMapping("/ask-code") @GetMapping("/ask-code")
fun askVerifyCode( fun askVerifyCode(
@RequestParam @NotBlank @Email email: String, @RequestParam @NotBlank @Email email: String,
@RequestParam @Pattern(regexp = "(register|reset)") type: String, @RequestParam type: EmailType,
exchange: ServerWebExchange exchange: ServerWebExchange
): Mono<ApiResponse<Any?>> { ): Mono<ApiResponse<Any?>> {
logger.info("Trying to ask code") logger.info("Trying to ask code")
val ip = exchange.request.remoteAddress?.address?.hostAddress ?: "unknown" val ip = exchange.request.remoteAddress?.address?.hostAddress ?: "unknown"
return service.askEmailVerifyCode(type, email, ip) return service.askEmailVerifyCode(type, email, ip)
.map { messageHandler(it) } .map(::messageHandler)
} }
@PostMapping("/register") @PostMapping("/register")
fun emailRegister(@RequestBody @Valid vo: EmailRegisterVO): Mono<ApiResponse<Any?>> { fun emailRegister(@RequestBody @Valid vo: EmailRegisterVO): Mono<ApiResponse<Any?>> {
logger.info("Trying to register") logger.info("Trying to register")
return service.registerEmailAccount(vo) return service.registerEmailAccount(vo)
.map { messageHandler(it) } .map(::messageHandler)
} }
@PostMapping("/reset") @PostMapping("/reset")
fun emailResetPassword(@RequestBody @Valid vo: PasswordResetVO): Mono<ApiResponse<Any?>> { fun emailResetPassword(@RequestBody @Valid vo: PasswordResetVO): Mono<ApiResponse<Any?>> {
logger.info("Trying to reset password") logger.info("Trying to reset password")
return service.resetEmailAccountPassword(vo) return service.resetEmailAccountPassword(vo)
.map { messageHandler(it) } .map(::messageHandler)
} }
@GetMapping("/logout") @GetMapping("/logout")
@@ -1,10 +1,11 @@
package com.github.sor2171.backend.entity.dto package com.github.sor2171.backend.entity.dto
import com.github.sor2171.backend.entity.enums.EmailType
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
data class EmailVerificationMsg( data class EmailVerificationMsg(
val email: String, val email: String,
val type: String, val type: EmailType,
val code: String 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.ApiResponse
import com.github.sor2171.backend.entity.toJsonString import com.github.sor2171.backend.entity.toJsonString
import com.github.sor2171.backend.utils.Const import com.github.sor2171.backend.utils.Const
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.annotation.Order import org.springframework.core.annotation.Order
import org.springframework.data.redis.core.ReactiveStringRedisTemplate import org.springframework.data.redis.core.ReactiveStringRedisTemplate
import org.springframework.http.HttpStatus import org.springframework.http.HttpStatus
@@ -17,7 +18,14 @@ import java.time.Duration
@Component @Component
@Order(Const.FLOW_LIMIT_ORDER) @Order(Const.FLOW_LIMIT_ORDER)
class FlowLimitFilter( 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 { ) : WebFilter {
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> { override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
@@ -49,21 +57,22 @@ class FlowLimitFilter(
if (isBlocked) { if (isBlocked) {
Mono.just(false) Mono.just(false)
} else { } else {
template.hasKey(counterKey).flatMap { hasCounter -> template.opsForValue().increment(counterKey).flatMap { count ->
if (hasCounter) { if (count == 1L) {
template.opsForValue().increment(counterKey).flatMap { count -> template.expire(
if (count > 10) { counterKey,
template.opsForValue() Duration.ofSeconds(limitTime.toLong())
.set(blockKey, "1", Duration.ofMinutes(1)) ).thenReturn(true)
.thenReturn(false) } else if (count > limitCount) {
} else {
Mono.just(true)
}
}
} else {
template.opsForValue() template.opsForValue()
.set(counterKey, "1", Duration.ofSeconds(3)) .set(
.thenReturn(true) blockKey,
"1",
Duration.ofSeconds(expireTime.toLong())
)
.thenReturn(false)
} else {
Mono.just(true)
} }
} }
} }
@@ -1,13 +1,14 @@
package com.github.sor2171.backend.listener package com.github.sor2171.backend.listener
import com.github.sor2171.backend.entity.dto.EmailVerificationMsg import com.github.sor2171.backend.entity.dto.EmailVerificationMsg
import com.github.sor2171.backend.entity.enums.EmailType
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.amqp.rabbit.annotation.RabbitHandler import org.springframework.amqp.rabbit.annotation.RabbitHandler
import org.springframework.amqp.rabbit.annotation.RabbitListener import org.springframework.amqp.rabbit.annotation.RabbitListener
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
import org.springframework.mail.MailSender
import org.springframework.mail.SimpleMailMessage import org.springframework.mail.SimpleMailMessage
import org.springframework.mail.javamail.JavaMailSender
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Component @Component
@@ -15,9 +16,8 @@ import org.springframework.stereotype.Component
class MailQueueListener( class MailQueueListener(
@param:Value($$"${spring.mail.username}") @param:Value($$"${spring.mail.username}")
private val username: String, private val username: String,
private val json: Json, private val json: Json,
private val sender: MailSender private val sender: JavaMailSender
) { ) {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)
@@ -28,27 +28,30 @@ class MailQueueListener(
val (email, type, code) = data val (email, type, code) = data
val message = when (type) { val message = when (type) {
"register" -> createMailMessage( EmailType.REGISTER -> createMailMessage(
"Welcome to Our Service", "Welcome to Our Service",
"Thank you for registering! Your verification code is: $code" "Thank you for registering! Your verification code is: $code"
+ "\nThis code is valid for 3 minutes.", + "\nThis code is valid for 3 minutes.",
email email
) )
"reset" -> createMailMessage( EmailType.RESET -> createMailMessage(
"Password Reset Request", "Password Reset Request",
"You requested a password reset. Your verification code is: $code" "You requested a password reset. Your verification code is: $code"
+ "\nThis code is valid for 3 minutes.", + "\nThis code is valid for 3 minutes.",
email email
) )
else -> return
} }
sender.send(message) sender.send(message)
logger.info("Email sent to $email.") 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() val message = SimpleMailMessage()
message.subject = title message.subject = title
message.text = content message.text = content
@@ -11,5 +11,5 @@ interface AccountService : IService<Account>, ReactiveUserDetailsService {
fun findAccountByEmail(email: String): Mono<Account> fun findAccountByEmail(email: String): Mono<Account>
fun existAccountByName(username: String): Mono<Boolean> fun existAccountByName(username: String): Mono<Boolean>
fun existAccountByEmail(email: 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> { override fun findAccountByName(username: String): Mono<Account> =
return Mono.fromCallable { findAccount("name", username) {
logger.info("Find account with name $username") ktQuery()
this.ktQuery()
.eq(Account::username, username) .eq(Account::username, username)
.one() .one()
}.flatMap { account -> }
Mono.just(account)
}.switchIfEmpty(Mono.error(UsernameNotFoundException("Account with name $username not found")))
}
override fun findAccountByEmail(email: String): Mono<Account> { override fun findAccountByEmail(email: String): Mono<Account> =
return Mono.fromCallable { findAccount("email", email) {
logger.info("Find account with email $email") ktQuery()
this.ktQuery()
.eq(Account::email, email) .eq(Account::email, email)
.one() .one()
}.flatMap { account -> }
Mono.just(account)
}.switchIfEmpty(Mono.error(UsernameNotFoundException("Account with email $email not found")))
}
override fun findAccountByNameOrEmail(text: String): Mono<Account> { private fun findAccount(
return findAccountByEmail(text) fieldName: String,
.onErrorResume { findAccountByName(text) } 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> { override fun findAccountByNameOrEmail(text: String): Mono<Account> =
return Mono.fromCallable { findAccountByEmail(text).onErrorResume(
logger.info("Determine if name $username exists") UsernameNotFoundException::class.java
this.ktQuery() ) {
findAccountByName(text)
}
override fun existAccountByName(username: String): Mono<Boolean> =
Mono.fromCallable {
logger.info("Determine if name {} exists", username)
ktQuery()
.eq(Account::username, username) .eq(Account::username, username)
.exists() .exists()
} }
}
override fun existAccountByEmail(email: String): Mono<Boolean> { override fun existAccountByEmail(email: String): Mono<Boolean> =
return Mono.fromCallable { Mono.fromCallable {
logger.info("Determine if email $email exists") logger.info("Determine if email {} exists", email)
this.ktQuery() ktQuery()
.eq(Account::email, email) .eq(Account::email, email)
.exists() .exists()
} }
}
override fun resetPasswordByEmail(email: String, encodedPassword: String): Mono<String> { override fun resetPasswordByEmail(
return existAccountByEmail(email).flatMap { exists -> email: String,
if (!exists) { encodedPassword: String
return@flatMap Mono.just("account with the email does not exist.") ): Mono<Void> =
} existAccountByEmail(email)
.flatMap { exists ->
if (!exists) {
return@flatMap Mono.error(
UsernameNotFoundException(
"Account with email $email not found"
)
)
}
Mono.fromCallable { Mono.fromCallable {
logger.info("Reset password by email $email") ktUpdate()
this.ktUpdate() .eq(Account::email, email)
.eq(Account::email, email) .set(Account::password, encodedPassword)
.set(Account::password, encodedPassword) .update()
.update() }
}.flatMap { success -> }
if (success) { .flatMap { success ->
Mono.just("") if (success) {
} else { Mono.empty()
Mono.just("something went wrong. Please contact the administrator.") } 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.Account
import com.github.sor2171.backend.entity.dto.EmailVerificationMsg 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.EmailRegisterVO
import com.github.sor2171.backend.entity.vo.request.PasswordResetVO import com.github.sor2171.backend.entity.vo.request.PasswordResetVO
import com.github.sor2171.backend.entity.vo.response.ReloginVO import com.github.sor2171.backend.entity.vo.response.ReloginVO
@@ -27,7 +28,7 @@ class AuthorizationService(
private val stringRedisTemplate: ReactiveStringRedisTemplate, private val stringRedisTemplate: ReactiveStringRedisTemplate,
private val encoder: PasswordEncoder 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 -> return verifyLimit(ip).flatMap { allowed ->
if (allowed) { if (allowed) {
val code = (100000..999999).random().toString() val code = (100000..999999).random().toString()
@@ -52,32 +53,27 @@ class AuthorizationService(
fun registerEmailAccount(vo: EmailRegisterVO): Mono<String> { fun registerEmailAccount(vo: EmailRegisterVO): Mono<String> {
val (email, code, username, password) = vo val (email, code, username, password) = vo
return verifyCode(email, code).flatMap { error -> return verifyCode(email, code)
if (!error.isBlank()) { .filter { it.isBlank() }
return@flatMap Mono.just(error) .switchIfEmpty(Mono.just("wrong code"))
.flatMap {
accountService.existAccountByEmail(email)
} }
.flatMap { emailExists ->
accountService if (emailExists) {
.existAccountByEmail(email) Mono.just("account with the same email already exists.")
.flatMap { emailExists -> } else {
if (emailExists) { accountService.existAccountByName(username).flatMap { usernameExists ->
return@flatMap Mono.just("account with the same email already exists.") if (usernameExists) {
} Mono.just("username already exists.")
accountService } else {
.existAccountByName(username)
.flatMap { usernameExists ->
if (usernameExists) {
return@flatMap Mono.just("username already exists.")
}
val encodedPassword = encoder.encode(password)
val account = Account( val account = Account(
null, id = null,
username, username = username,
encodedPassword!!, password = encoder.encode(password)!!,
email, email = email,
"user", role = "user",
getCurrentDateTime() registerTime = getCurrentDateTime()
) )
Mono.fromCallable { accountService.save(account) } Mono.fromCallable { accountService.save(account) }
@@ -90,31 +86,31 @@ class AuthorizationService(
} }
} }
} }
}
} }
} }
} }
fun resetEmailAccountPassword(vo: PasswordResetVO): Mono<String> { fun resetEmailAccountPassword(vo: PasswordResetVO): Mono<String> {
val (email, code, password) = vo val (email, code, password) = vo
return verifyCode(email, code).flatMap { error -> val encodedPassword =
if (!error.isBlank()) { encoder.encode(password)
return@flatMap Mono.just(error) ?: return Const.INTERNAL_ERROR_MONO
} else {
val encodedPassword = encoder.encode(password)
?: return@flatMap Const.INTERNAL_ERROR_MONO
return@flatMap accountService return verifyCode(email, code)
.resetPasswordByEmail(email, encodedPassword) .then(
.flatMap { text -> accountService.resetPasswordByEmail(
if (text.isBlank()) { email,
stringRedisTemplate encodedPassword
.delete(Const.VERIFY_EMAIL_DATA + email) )
.then(Mono.just(text)) )
} else Mono.just(text) .then(
} stringRedisTemplate.delete(
} Const.VERIFY_EMAIL_DATA + email
} )
)
.thenReturn("Password reset successfully")
} }
fun jwtTokenRelogin(headerToken: String?): Mono<ReloginVO>? { fun jwtTokenRelogin(headerToken: String?): Mono<ReloginVO>? {
@@ -144,12 +140,9 @@ class AuthorizationService(
return stringRedisTemplate.opsForValue() return stringRedisTemplate.opsForValue()
.get(Const.VERIFY_EMAIL_DATA + email) .get(Const.VERIFY_EMAIL_DATA + email)
.flatMap { code -> .flatMap { code ->
when { if (receivedCode == null || code != receivedCode)
receivedCode == null || code != receivedCode -> Mono.just("verify code is wrong.")
Mono.just("verify code is wrong.") else Mono.just("")
else -> Mono.just("")
}
} }
.switchIfEmpty(Mono.just("verify code has not been sent")) .switchIfEmpty(Mono.just("verify code has not been sent"))
} }
@@ -33,3 +33,12 @@ spring:
#username: root #username: root
#password: 123456 #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>