mirror of
https://github.com/SOR2171/Springboot4-Vue3_Demo.git
synced 2026-10-25 12:31:10 +08:00
fix: DB connect
This commit is contained in:
@@ -67,14 +67,14 @@ class SecurityConfig(
|
|||||||
writeJsonResponse(
|
writeJsonResponse(
|
||||||
exchange,
|
exchange,
|
||||||
HttpStatus.UNAUTHORIZED,
|
HttpStatus.UNAUTHORIZED,
|
||||||
ApiResponse.unauthenticated<Any>(ex.message)
|
ApiResponse.unauthenticated(ex.message)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.accessDeniedHandler { exchange, ex ->
|
.accessDeniedHandler { exchange, ex ->
|
||||||
writeJsonResponse(
|
writeJsonResponse(
|
||||||
exchange,
|
exchange,
|
||||||
HttpStatus.FORBIDDEN,
|
HttpStatus.FORBIDDEN,
|
||||||
ApiResponse.forbidden<Any>(ex.message)
|
ApiResponse.forbidden(ex.message)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ class SecurityConfig(
|
|||||||
writeJsonResponse(
|
writeJsonResponse(
|
||||||
webFilterExchange.exchange,
|
webFilterExchange.exchange,
|
||||||
HttpStatus.UNAUTHORIZED,
|
HttpStatus.UNAUTHORIZED,
|
||||||
ApiResponse.unauthenticated<Any>(exception.message)
|
ApiResponse.unauthenticated(exception.message)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return filter
|
return filter
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.github.sor2171.backend.config
|
package com.github.sor2171.backend.config
|
||||||
|
|
||||||
|
import com.github.sor2171.backend.utils.BigDecimalSerializer
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
|
import kotlinx.serialization.modules.contextual
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
|
|
||||||
@@ -9,6 +12,9 @@ class SerializationConfig {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
fun kotlinxSerializationJson(): Json = Json {
|
fun kotlinxSerializationJson(): Json = Json {
|
||||||
|
serializersModule = SerializersModule {
|
||||||
|
contextual(BigDecimalSerializer)
|
||||||
|
}
|
||||||
ignoreUnknownKeys = true
|
ignoreUnknownKeys = true
|
||||||
isLenient = true
|
isLenient = true
|
||||||
encodeDefaults = false
|
encodeDefaults = false
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ class TestController(
|
|||||||
fun test(): Mono<String> = Mono.just("Hello Kotlin WebFlux!")
|
fun test(): Mono<String> = Mono.just("Hello Kotlin WebFlux!")
|
||||||
|
|
||||||
@GetMapping("/wstoken")
|
@GetMapping("/wstoken")
|
||||||
fun testToken(): Mono<ApiResponse<String>> =
|
fun testToken(): Mono<ApiResponse<Any?>> =
|
||||||
wsTokenService.registerToken(ExampleHandler::class)
|
wsTokenService.registerToken(ExampleHandler::class)
|
||||||
.map { token ->
|
.map { token ->
|
||||||
ApiResponse.success(data = token)
|
ApiResponse.success<Any?>(data = token)
|
||||||
}
|
}
|
||||||
.onErrorResume { e ->
|
.onErrorResume { e ->
|
||||||
Mono.just(ApiResponse.innerError(e.message))
|
Mono.just(ApiResponse.failure(500, null, e.message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+5
-12
@@ -1,10 +1,11 @@
|
|||||||
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.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 com.github.sor2171.backend.utils.Const
|
||||||
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
|
||||||
@@ -31,21 +32,21 @@ class AuthorizeController(
|
|||||||
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)
|
.map(Const::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)
|
.map(Const::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)
|
.map(Const::messageHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/logout")
|
@GetMapping("/logout")
|
||||||
@@ -68,12 +69,4 @@ class AuthorizeController(
|
|||||||
?.map { ApiResponse.success(it) }
|
?.map { ApiResponse.success(it) }
|
||||||
?: Mono.just(ApiResponse.failure(401, null, "Please login with password."))
|
?: Mono.just(ApiResponse.failure(401, null, "Please login with password."))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun messageHandler(wrongMessage: String): ApiResponse<Any?> {
|
|
||||||
return if (wrongMessage.isBlank()) {
|
|
||||||
ApiResponse.success()
|
|
||||||
} else {
|
|
||||||
ApiResponse.failure(400, null, wrongMessage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ import kotlinx.serialization.json.Json
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class ApiResponse<T>(
|
data class ApiResponse<T>(
|
||||||
val code: Int,
|
val code: Int,
|
||||||
val data: T?,
|
val data: T,
|
||||||
val message: String
|
val message: String
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -15,24 +15,21 @@ data class ApiResponse<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 针对无 data 返回的情况,统一使用 Any?
|
// 针对无 data 返回的情况,统一使用 Any?
|
||||||
fun <T> success(message: String = "Success"): ApiResponse<T> {
|
fun success(message: String = "Success"): ApiResponse<Any?> {
|
||||||
return ApiResponse(200, null, message)
|
return ApiResponse(200, null, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> failure(code: Int, data: T?, message: String?): ApiResponse<T> {
|
fun <T> failure(code: Int = 401, data: T, message: String?): ApiResponse<T> {
|
||||||
return ApiResponse(code, data, message ?: "Failure")
|
return ApiResponse(code, data, message ?: "Failure")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> unauthenticated(message: String?) =
|
fun unauthenticated(message: String?) =
|
||||||
failure<T>(401, null, message)
|
failure(401, null, message)
|
||||||
|
|
||||||
fun <T> forbidden(message: String?) =
|
fun forbidden(message: String?) =
|
||||||
failure<T>(403, null, message)
|
failure(403, null, message)
|
||||||
|
|
||||||
fun <T> innerError(message: String?) =
|
fun logoutFailed(message: String = ""): ApiResponse<Any?> {
|
||||||
failure<T>(500, null, message)
|
|
||||||
|
|
||||||
fun <T> logoutFailed(message: String = ""): ApiResponse<T> {
|
|
||||||
val formatMessage = if (message.isBlank()) "" else ": $message"
|
val formatMessage = if (message.isBlank()) "" else ": $message"
|
||||||
return failure(400, null, "Logout Failed$formatMessage")
|
return failure(400, null, "Logout Failed$formatMessage")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ class FlowLimitFilter(
|
|||||||
@param:Value($$"${app.flow-limit.count}")
|
@param:Value($$"${app.flow-limit.count}")
|
||||||
private val limitCount: Int,
|
private val limitCount: Int,
|
||||||
@param:Value($$"${app.flow-limit.limit-time}")
|
@param:Value($$"${app.flow-limit.limit-time}")
|
||||||
private val limitTime: Long,
|
private val limitTime: Int,
|
||||||
@param:Value($$"${app.flow-limit.ban-time}")
|
@param:Value($$"${app.flow-limit.ban-time}")
|
||||||
private val expireTime: Long,
|
private val expireTime: Int,
|
||||||
) : WebFilter {
|
) : WebFilter {
|
||||||
|
|
||||||
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
|
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
|
||||||
@@ -45,7 +45,7 @@ class FlowLimitFilter(
|
|||||||
response.statusCode = HttpStatus.FORBIDDEN
|
response.statusCode = HttpStatus.FORBIDDEN
|
||||||
response.headers.contentType = MediaType.APPLICATION_JSON
|
response.headers.contentType = MediaType.APPLICATION_JSON
|
||||||
val buffer = response.bufferFactory()
|
val buffer = response.bufferFactory()
|
||||||
.wrap(ApiResponse.forbidden<Any>("Too many requests").toJsonString().toByteArray())
|
.wrap(ApiResponse.forbidden("Too many requests").toJsonString().toByteArray())
|
||||||
return response.writeWith(Mono.just(buffer))
|
return response.writeWith(Mono.just(buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,14 +61,14 @@ class FlowLimitFilter(
|
|||||||
if (count == 1L) {
|
if (count == 1L) {
|
||||||
template.expire(
|
template.expire(
|
||||||
counterKey,
|
counterKey,
|
||||||
Duration.ofSeconds(limitTime)
|
Duration.ofSeconds(limitTime.toLong())
|
||||||
).thenReturn(true)
|
).thenReturn(true)
|
||||||
} else if (count > limitCount) {
|
} else if (count > limitCount) {
|
||||||
template.opsForValue()
|
template.opsForValue()
|
||||||
.set(
|
.set(
|
||||||
blockKey,
|
blockKey,
|
||||||
"1",
|
"1",
|
||||||
Duration.ofSeconds(expireTime)
|
Duration.ofSeconds(expireTime.toLong())
|
||||||
)
|
)
|
||||||
.thenReturn(false)
|
.thenReturn(false)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
package com.github.sor2171.backend.mapper.typehandler
|
||||||
|
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import kotlinx.datetime.toJavaLocalDateTime
|
||||||
|
import kotlinx.datetime.toKotlinLocalDateTime
|
||||||
|
import org.apache.ibatis.type.BaseTypeHandler
|
||||||
|
import org.apache.ibatis.type.JdbcType
|
||||||
|
import org.apache.ibatis.type.MappedJdbcTypes
|
||||||
|
import org.apache.ibatis.type.MappedTypes
|
||||||
|
import java.sql.CallableStatement
|
||||||
|
import java.sql.PreparedStatement
|
||||||
|
import java.sql.ResultSet
|
||||||
|
|
||||||
|
@MappedTypes(LocalDateTime::class)
|
||||||
|
@MappedJdbcTypes(JdbcType.TIMESTAMP)
|
||||||
|
class TimeTypeHandler : BaseTypeHandler<LocalDateTime>() {
|
||||||
|
override fun setNonNullParameter(
|
||||||
|
ps: PreparedStatement,
|
||||||
|
i: Int,
|
||||||
|
parameter: LocalDateTime,
|
||||||
|
jdbcType: JdbcType?
|
||||||
|
) {
|
||||||
|
ps.setObject(i, parameter.toJavaLocalDateTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getNullableResult(
|
||||||
|
rs: ResultSet,
|
||||||
|
columnName: String
|
||||||
|
): LocalDateTime? =
|
||||||
|
rs.getTimestamp(columnName)
|
||||||
|
?.toLocalDateTime()
|
||||||
|
?.toKotlinLocalDateTime()
|
||||||
|
|
||||||
|
override fun getNullableResult(
|
||||||
|
rs: ResultSet,
|
||||||
|
columnIndex: Int
|
||||||
|
): LocalDateTime? =
|
||||||
|
rs.getTimestamp(columnIndex)
|
||||||
|
?.toLocalDateTime()
|
||||||
|
?.toKotlinLocalDateTime()
|
||||||
|
|
||||||
|
override fun getNullableResult(
|
||||||
|
cs: CallableStatement,
|
||||||
|
columnIndex: Int
|
||||||
|
): LocalDateTime? =
|
||||||
|
cs.getTimestamp(columnIndex)
|
||||||
|
?.toLocalDateTime()
|
||||||
|
?.toKotlinLocalDateTime()
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.github.sor2171.backend.utils
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.SerializationException
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import kotlinx.serialization.json.JsonDecoder
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
object BigDecimalSerializer : KSerializer<BigDecimal> {
|
||||||
|
override val descriptor: SerialDescriptor =
|
||||||
|
PrimitiveSerialDescriptor("BigDecimal", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: BigDecimal) {
|
||||||
|
encoder.encodeString(value.toPlainString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): BigDecimal {
|
||||||
|
val element = (decoder as JsonDecoder).decodeJsonElement()
|
||||||
|
if (element !is JsonPrimitive) {
|
||||||
|
throw SerializationException("Expected JSON primitive, got $element")
|
||||||
|
}
|
||||||
|
return element.content.toBigDecimal()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,29 @@
|
|||||||
package com.github.sor2171.backend.utils
|
package com.github.sor2171.backend.utils
|
||||||
|
|
||||||
|
import com.github.sor2171.backend.entity.ApiResponse
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
|
|
||||||
object Const {
|
object Const {
|
||||||
const val FLOW_LIMIT_ORDER = -101
|
const val FLOW_LIMIT_ORDER = -101
|
||||||
|
|
||||||
const val JWT_BLACK_LIST = "jwt:blacklist:"
|
const val JWT_BLACK_LIST = "jwt:blacklist:"
|
||||||
const val VERIFY_EMAIL_LIMIT = "verify:email:limit:"
|
const val VERIFY_EMAIL_LIMIT = "verify:email:limit:"
|
||||||
const val VERIFY_EMAIL_DATA = "verify:email:data:"
|
const val VERIFY_EMAIL_DATA = "verify:email:data:"
|
||||||
|
|
||||||
const val FLOW_LIMIT_COUNTER = "flow:counter:"
|
const val FLOW_LIMIT_COUNTER = "flow:counter:"
|
||||||
const val FLOW_LIMIT_BLOCK = "flow:block:"
|
const val FLOW_LIMIT_BLOCK = "flow:block:"
|
||||||
|
|
||||||
const val WS_REQUEST_TOKEN = "ws:token:"
|
const val WS_REQUEST_TOKEN = "ws:token:"
|
||||||
|
|
||||||
val INTERNAL_ERROR_MONO = Mono.just("something went wrong. Please contact the administrator.")
|
const val INTERNAL_ERROR_STR = "something went wrong. Please contact the administrator."
|
||||||
|
val INTERNAL_ERROR_MONO = Mono.just(INTERNAL_ERROR_STR)
|
||||||
|
|
||||||
|
// for Controllers
|
||||||
|
fun messageHandler(wrongMessage: String): ApiResponse<Any?> {
|
||||||
|
return if (wrongMessage.isBlank()) {
|
||||||
|
ApiResponse.success()
|
||||||
|
} else {
|
||||||
|
ApiResponse.failure(400, null, wrongMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,8 +10,8 @@ import org.springframework.data.redis.core.StringRedisTemplate
|
|||||||
import org.springframework.security.core.userdetails.User
|
import org.springframework.security.core.userdetails.User
|
||||||
import org.springframework.security.core.userdetails.UserDetails
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
import java.time.Duration
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class JwtUtils(
|
class JwtUtils(
|
||||||
@@ -44,8 +44,7 @@ class JwtUtils(
|
|||||||
template.opsForValue().set(
|
template.opsForValue().set(
|
||||||
Const.JWT_BLACK_LIST + uuid,
|
Const.JWT_BLACK_LIST + uuid,
|
||||||
"deleted",
|
"deleted",
|
||||||
expire,
|
Duration.ofMillis(expire)
|
||||||
TimeUnit.MILLISECONDS
|
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ spring:
|
|||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
type-handlers-package: com.github.sor2171.backend.config
|
type-handlers-package: club.itstudio.itmanager.mapper.typehandler
|
||||||
|
|
||||||
app:
|
app:
|
||||||
flow-limit:
|
flow-limit:
|
||||||
|
|||||||
Reference in New Issue
Block a user