fix: DB connect

This commit is contained in:
SOR
2026-08-18 18:41:39 +08:00
parent 1938fb120b
commit e71f84a943
11 changed files with 125 additions and 41 deletions
@@ -67,14 +67,14 @@ class SecurityConfig(
writeJsonResponse(
exchange,
HttpStatus.UNAUTHORIZED,
ApiResponse.unauthenticated<Any>(ex.message)
ApiResponse.unauthenticated(ex.message)
)
}
.accessDeniedHandler { exchange, ex ->
writeJsonResponse(
exchange,
HttpStatus.FORBIDDEN,
ApiResponse.forbidden<Any>(ex.message)
ApiResponse.forbidden(ex.message)
)
}
}
@@ -112,7 +112,7 @@ class SecurityConfig(
writeJsonResponse(
webFilterExchange.exchange,
HttpStatus.UNAUTHORIZED,
ApiResponse.unauthenticated<Any>(exception.message)
ApiResponse.unauthenticated(exception.message)
)
}
return filter
@@ -1,6 +1,9 @@
package com.github.sor2171.backend.config
import com.github.sor2171.backend.utils.BigDecimalSerializer
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.Configuration
@@ -9,6 +12,9 @@ class SerializationConfig {
@Bean
fun kotlinxSerializationJson(): Json = Json {
serializersModule = SerializersModule {
contextual(BigDecimalSerializer)
}
ignoreUnknownKeys = true
isLenient = true
encodeDefaults = false
@@ -17,12 +17,12 @@ class TestController(
fun test(): Mono<String> = Mono.just("Hello Kotlin WebFlux!")
@GetMapping("/wstoken")
fun testToken(): Mono<ApiResponse<String>> =
fun testToken(): Mono<ApiResponse<Any?>> =
wsTokenService.registerToken(ExampleHandler::class)
.map { token ->
ApiResponse.success(data = token)
ApiResponse.success<Any?>(data = token)
}
.onErrorResume { e ->
Mono.just(ApiResponse.innerError(e.message))
Mono.just(ApiResponse.failure(500, null, e.message))
}
}
@@ -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.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 com.github.sor2171.backend.utils.Const
import jakarta.validation.Valid
import jakarta.validation.constraints.Email
import jakarta.validation.constraints.NotBlank
@@ -31,21 +32,21 @@ class AuthorizeController(
logger.info("Trying to ask code")
val ip = exchange.request.remoteAddress?.address?.hostAddress ?: "unknown"
return service.askEmailVerifyCode(type, email, ip)
.map(::messageHandler)
.map(Const::messageHandler)
}
@PostMapping("/register")
fun emailRegister(@RequestBody @Valid vo: EmailRegisterVO): Mono<ApiResponse<Any?>> {
logger.info("Trying to register")
return service.registerEmailAccount(vo)
.map(::messageHandler)
.map(Const::messageHandler)
}
@PostMapping("/reset")
fun emailResetPassword(@RequestBody @Valid vo: PasswordResetVO): Mono<ApiResponse<Any?>> {
logger.info("Trying to reset password")
return service.resetEmailAccountPassword(vo)
.map(::messageHandler)
.map(Const::messageHandler)
}
@GetMapping("/logout")
@@ -68,12 +69,4 @@ class AuthorizeController(
?.map { ApiResponse.success(it) }
?: 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
data class ApiResponse<T>(
val code: Int,
val data: T?,
val data: T,
val message: String
) {
companion object {
@@ -15,24 +15,21 @@ data class ApiResponse<T>(
}
// 针对无 data 返回的情况,统一使用 Any?
fun <T> success(message: String = "Success"): ApiResponse<T> {
fun success(message: String = "Success"): ApiResponse<Any?> {
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")
}
fun <T> unauthenticated(message: String?) =
failure<T>(401, null, message)
fun unauthenticated(message: String?) =
failure(401, null, message)
fun <T> forbidden(message: String?) =
failure<T>(403, null, message)
fun forbidden(message: String?) =
failure(403, null, message)
fun <T> innerError(message: String?) =
failure<T>(500, null, message)
fun <T> logoutFailed(message: String = ""): ApiResponse<T> {
fun logoutFailed(message: String = ""): ApiResponse<Any?> {
val formatMessage = if (message.isBlank()) "" else ": $message"
return failure(400, null, "Logout Failed$formatMessage")
}
@@ -23,9 +23,9 @@ class FlowLimitFilter(
@param:Value($$"${app.flow-limit.count}")
private val limitCount: Int,
@param:Value($$"${app.flow-limit.limit-time}")
private val limitTime: Long,
private val limitTime: Int,
@param:Value($$"${app.flow-limit.ban-time}")
private val expireTime: Long,
private val expireTime: Int,
) : WebFilter {
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
@@ -45,7 +45,7 @@ class FlowLimitFilter(
response.statusCode = HttpStatus.FORBIDDEN
response.headers.contentType = MediaType.APPLICATION_JSON
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))
}
@@ -61,14 +61,14 @@ class FlowLimitFilter(
if (count == 1L) {
template.expire(
counterKey,
Duration.ofSeconds(limitTime)
Duration.ofSeconds(limitTime.toLong())
).thenReturn(true)
} else if (count > limitCount) {
template.opsForValue()
.set(
blockKey,
"1",
Duration.ofSeconds(expireTime)
Duration.ofSeconds(expireTime.toLong())
)
.thenReturn(false)
} else {
@@ -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
import com.github.sor2171.backend.entity.ApiResponse
import reactor.core.publisher.Mono
object Const {
const val FLOW_LIMIT_ORDER = -101
const val JWT_BLACK_LIST = "jwt:blacklist:"
const val VERIFY_EMAIL_LIMIT = "verify:email:limit:"
const val VERIFY_EMAIL_DATA = "verify:email:data:"
const val FLOW_LIMIT_COUNTER = "flow:counter:"
const val FLOW_LIMIT_BLOCK = "flow:block:"
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.UserDetails
import org.springframework.stereotype.Component
import java.time.Duration
import java.util.*
import java.util.concurrent.TimeUnit
@Component
class JwtUtils(
@@ -44,8 +44,7 @@ class JwtUtils(
template.opsForValue().set(
Const.JWT_BLACK_LIST + uuid,
"deleted",
expire,
TimeUnit.MILLISECONDS
Duration.ofMillis(expire)
)
return true
}
@@ -35,7 +35,7 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
type-handlers-package: com.github.sor2171.backend.config
type-handlers-package: club.itstudio.itmanager.mapper.typehandler
app:
flow-limit: