mirror of
https://github.com/SOR2171/Springboot4-Vue3_Demo.git
synced 2026-10-23 19:41:12 +08:00
refactor: simplify
This commit is contained in:
Generated
+1
-1
@@ -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>
|
||||
Generated
+1
@@ -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
@@ -2,6 +2,5 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/ItStudio-CareerPlanningPlatform" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
+6
-11
@@ -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")
|
||||
|
||||
+2
-1
@@ -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,23 +57,24 @@ class FlowLimitFilter(
|
||||
if (isBlocked) {
|
||||
Mono.just(false)
|
||||
} else {
|
||||
template.hasKey(counterKey).flatMap { hasCounter ->
|
||||
if (hasCounter) {
|
||||
template.opsForValue().increment(counterKey).flatMap { count ->
|
||||
if (count > 10) {
|
||||
if (count == 1L) {
|
||||
template.expire(
|
||||
counterKey,
|
||||
Duration.ofSeconds(limitTime.toLong())
|
||||
).thenReturn(true)
|
||||
} else if (count > limitCount) {
|
||||
template.opsForValue()
|
||||
.set(blockKey, "1", Duration.ofMinutes(1))
|
||||
.set(
|
||||
blockKey,
|
||||
"1",
|
||||
Duration.ofSeconds(expireTime.toLong())
|
||||
)
|
||||
.thenReturn(false)
|
||||
} else {
|
||||
Mono.just(true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
template.opsForValue()
|
||||
.set(counterKey, "1", Duration.ofSeconds(3))
|
||||
.thenReturn(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>
|
||||
}
|
||||
+54
-37
@@ -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 findAccountByNameOrEmail(text: String): Mono<Account> =
|
||||
findAccountByEmail(text).onErrorResume(
|
||||
UsernameNotFoundException::class.java
|
||||
) {
|
||||
findAccountByName(text)
|
||||
}
|
||||
|
||||
override fun existAccountByName(username: String): Mono<Boolean> {
|
||||
return Mono.fromCallable {
|
||||
logger.info("Determine if name $username exists")
|
||||
this.ktQuery()
|
||||
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 ->
|
||||
override fun resetPasswordByEmail(
|
||||
email: String,
|
||||
encodedPassword: String
|
||||
): Mono<Void> =
|
||||
existAccountByEmail(email)
|
||||
.flatMap { exists ->
|
||||
if (!exists) {
|
||||
return@flatMap Mono.just("account with the email does not exist.")
|
||||
return@flatMap Mono.error(
|
||||
UsernameNotFoundException(
|
||||
"Account with email $email not found"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Mono.fromCallable {
|
||||
logger.info("Reset password by email $email")
|
||||
this.ktUpdate()
|
||||
ktUpdate()
|
||||
.eq(Account::email, email)
|
||||
.set(Account::password, encodedPassword)
|
||||
.update()
|
||||
}.flatMap { success ->
|
||||
}
|
||||
}
|
||||
.flatMap { success ->
|
||||
if (success) {
|
||||
Mono.just("")
|
||||
Mono.empty()
|
||||
} else {
|
||||
Mono.just("something went wrong. Please contact the administrator.")
|
||||
}
|
||||
}
|
||||
Mono.error(
|
||||
IllegalStateException(
|
||||
"Failed to reset password"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
-44
@@ -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 ->
|
||||
Mono.just("account with the same email already exists.")
|
||||
} else {
|
||||
accountService.existAccountByName(username).flatMap { usernameExists ->
|
||||
if (usernameExists) {
|
||||
return@flatMap Mono.just("username already exists.")
|
||||
}
|
||||
|
||||
val encodedPassword = encoder.encode(password)
|
||||
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) }
|
||||
@@ -93,28 +89,28 @@ 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 ->
|
||||
if (receivedCode == null || code != receivedCode)
|
||||
Mono.just("verify code is wrong.")
|
||||
|
||||
else -> Mono.just("")
|
||||
}
|
||||
else Mono.just("")
|
||||
}
|
||||
.switchIfEmpty(Mono.just("verify code has not been sent"))
|
||||
}
|
||||
|
||||
@@ -33,3 +33,12 @@ spring:
|
||||
#username: root
|
||||
#password: 123456
|
||||
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
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user