diff --git a/pom.xml b/pom.xml
index ddec85f..81e5aad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,6 +74,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
com.fasterxml.jackson.module
jackson-module-kotlin
@@ -112,12 +116,12 @@
com.baomidou
mybatis-plus-boot-starter
- 3.5.3.1
+ 3.5.3.2
com.baomidou
mybatis-plus-boot-starter-test
- 3.5.3.1
+ 3.5.3.2
test
@@ -125,11 +129,13 @@
druid-spring-boot-starter
1.2.19
+
org.apache.velocity
velocity-engine-core
@@ -142,7 +148,7 @@
com.auth0
java-jwt
- 4.3.0
+ 4.4.0
org.springframework.boot
@@ -152,6 +158,16 @@
com.fasterxml.jackson.datatype
jackson-datatype-jsr310
+
+ org.mapstruct
+ mapstruct
+ 1.5.5.Final
+
+
+ org.mapstruct
+ mapstruct-processor
+ 1.5.5.Final
+
@@ -180,7 +196,7 @@
compile
- compile
+ process-sources
compile
diff --git a/src/main/kotlin/top/fatweb/api/FatWebApiApplication.kt b/src/main/kotlin/top/fatweb/api/FatWebApiApplication.kt
index 9d83e83..1920cd3 100644
--- a/src/main/kotlin/top/fatweb/api/FatWebApiApplication.kt
+++ b/src/main/kotlin/top/fatweb/api/FatWebApiApplication.kt
@@ -18,14 +18,12 @@ fun main(args: Array) {
runApplication(*args)
} else {
logger.warn("File ‘application.yml’ cannot be found in the running path. The configuration file template 'application.example.yml' has been created. Please change the configuration file content and rename it to 'application.yml', and then restart the server.")
- FatWebApiApplication::class.java.getResource("/application-config-template.yml")?.readText()
- ?.let {
- File("application-config.example.yml").writeText(
- it.replace(
- "\$uuid\$",
- UUID.randomUUID().toString()
- )
+ FatWebApiApplication::class.java.getResource("/application-config-template.yml")?.readText()?.let {
+ File("application-config.example.yml").writeText(
+ it.replace(
+ "\$uuid\$", UUID.randomUUID().toString().replace("-", "")
)
- }
+ )
+ }
}
}
diff --git a/src/main/kotlin/top/fatweb/api/annotation/ApiVersion.kt b/src/main/kotlin/top/fatweb/api/annotation/ApiVersion.kt
index f087e97..a0b064c 100644
--- a/src/main/kotlin/top/fatweb/api/annotation/ApiVersion.kt
+++ b/src/main/kotlin/top/fatweb/api/annotation/ApiVersion.kt
@@ -2,13 +2,10 @@ package top.fatweb.api.annotation
import org.springframework.core.annotation.AliasFor
-
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class ApiVersion(
- @get:AliasFor("version")
- val value: Int = 1,
+ @get:AliasFor("version") val value: Int = 1,
- @get:AliasFor("value")
- val version: Int = 1
+ @get:AliasFor("value") val version: Int = 1
)
diff --git a/src/main/kotlin/top/fatweb/api/controller/permission/AuthenticationController.kt b/src/main/kotlin/top/fatweb/api/controller/permission/AuthenticationController.kt
index c36bdac..2d26a12 100644
--- a/src/main/kotlin/top/fatweb/api/controller/permission/AuthenticationController.kt
+++ b/src/main/kotlin/top/fatweb/api/controller/permission/AuthenticationController.kt
@@ -2,20 +2,30 @@ package top.fatweb.api.controller.permission
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
-import org.springframework.web.bind.annotation.*
+import jakarta.validation.Valid
+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.RestController
import top.fatweb.api.annotation.ApiVersion
import top.fatweb.api.entity.common.ResponseCode
import top.fatweb.api.entity.common.ResponseResult
-import top.fatweb.api.entity.permission.User
+import top.fatweb.api.entity.converter.UserConverter
+import top.fatweb.api.entity.param.LoginParam
import top.fatweb.api.service.permission.IAuthenticationService
@Tag(name = "身份认证", description = "身份认证相关接口")
-@RestController
+@Suppress("MVCPathVariableInspection")
@RequestMapping("/api/{apiVersion}")
@ApiVersion(2)
-class AuthenticationController(val loginService: IAuthenticationService) {
+@RestController
+class AuthenticationController(val loginService: IAuthenticationService, val userConverter: UserConverter) {
@Operation(summary = "登录")
@PostMapping("/login")
- fun login(@PathVariable apiVersion: String, @RequestBody user: User) =
- ResponseResult.success(ResponseCode.SYSTEM_LOGIN_SUCCESS, "Login success", loginService.login(user))
+ fun login(@Valid @RequestBody loginParam: LoginParam) =
+ ResponseResult.success(
+ ResponseCode.SYSTEM_LOGIN_SUCCESS,
+ "Login success",
+ loginService.login(userConverter.loginParamToUser(loginParam))
+ )
}
\ No newline at end of file
diff --git a/src/main/kotlin/top/fatweb/api/entity/common/ResponseCode.kt b/src/main/kotlin/top/fatweb/api/entity/common/ResponseCode.kt
index 8749e68..f6095bb 100644
--- a/src/main/kotlin/top/fatweb/api/entity/common/ResponseCode.kt
+++ b/src/main/kotlin/top/fatweb/api/entity/common/ResponseCode.kt
@@ -7,14 +7,16 @@ enum class ResponseCode(val code: Int) {
SYSTEM_LOGOUT_SUCCESS(BusinessCode.SYSTEM, 22),
SYSTEM_TOKEN_RENEW_SUCCESS(BusinessCode.SYSTEM, 23),
SYSTEM_UNAUTHORIZED(BusinessCode.SYSTEM, 30),
- SYSTEM_ACCESS_DENIED(BusinessCode.SYSTEM, 31),
- SYSTEM_USER_DISABLE(BusinessCode.SYSTEM, 32),
- SYSTEM_LOGIN_USERNAME_PASSWORD_ERROR(BusinessCode.SYSTEM, 33),
- SYSTEM_OLD_PASSWORD_NOT_MATCH(BusinessCode.SYSTEM, 34),
- SYSTEM_LOGOUT_FAILED(BusinessCode.SYSTEM, 35),
- SYSTEM_TOKEN_ILLEGAL(BusinessCode.SYSTEM, 36),
- SYSTEM_TOKEN_HAS_EXPIRED(BusinessCode.SYSTEM, 37),
+ SYSTEM_USERNAME_NOT_FOUND(BusinessCode.SYSTEM, 31),
+ SYSTEM_ACCESS_DENIED(BusinessCode.SYSTEM, 32),
+ SYSTEM_USER_DISABLE(BusinessCode.SYSTEM, 33),
+ SYSTEM_LOGIN_USERNAME_PASSWORD_ERROR(BusinessCode.SYSTEM, 34),
+ SYSTEM_OLD_PASSWORD_NOT_MATCH(BusinessCode.SYSTEM, 35),
+ SYSTEM_LOGOUT_FAILED(BusinessCode.SYSTEM, 36),
+ SYSTEM_TOKEN_ILLEGAL(BusinessCode.SYSTEM, 37),
+ SYSTEM_TOKEN_HAS_EXPIRED(BusinessCode.SYSTEM, 38),
SYSTEM_REQUEST_ILLEGAL(BusinessCode.SYSTEM, 40),
+ SYSTEM_ARGUMENT_NOT_VALID(BusinessCode.SYSTEM, 41),
SYSTEM_ERROR(BusinessCode.SYSTEM, 50),
SYSTEM_TIMEOUT(BusinessCode.SYSTEM, 51);
diff --git a/src/main/kotlin/top/fatweb/api/entity/converter/UserConverter.kt b/src/main/kotlin/top/fatweb/api/entity/converter/UserConverter.kt
new file mode 100644
index 0000000..8d24463
--- /dev/null
+++ b/src/main/kotlin/top/fatweb/api/entity/converter/UserConverter.kt
@@ -0,0 +1,14 @@
+package top.fatweb.api.entity.converter
+
+import org.mapstruct.Mapper
+import org.mapstruct.Mapping
+import org.mapstruct.Mappings
+import top.fatweb.api.entity.param.LoginParam
+import top.fatweb.api.entity.permission.User
+
+@Mapper(componentModel = "spring")
+interface UserConverter {
+ @Mappings(Mapping(source = "username", target = "username"), Mapping(source = "password", target = "password"))
+ fun loginParamToUser(loginParam: LoginParam): User
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/top/fatweb/api/entity/param/LoginParam.kt b/src/main/kotlin/top/fatweb/api/entity/param/LoginParam.kt
new file mode 100644
index 0000000..4ed8d0c
--- /dev/null
+++ b/src/main/kotlin/top/fatweb/api/entity/param/LoginParam.kt
@@ -0,0 +1,16 @@
+package top.fatweb.api.entity.param
+
+import io.swagger.v3.oas.annotations.media.Schema
+import jakarta.validation.constraints.NotBlank
+import java.io.Serializable
+
+class LoginParam : Serializable {
+
+ @Schema(description = "用户名", example = "test", required = true)
+ @NotBlank(message = "Username can not be blank")
+ val username: String? = null
+
+ @Schema(description = "密码", example = "test123456", required = true)
+ @NotBlank(message = "Password can not be blank")
+ val password: String? = null
+}
\ No newline at end of file
diff --git a/src/main/kotlin/top/fatweb/api/entity/permission/User.kt b/src/main/kotlin/top/fatweb/api/entity/permission/User.kt
index 3af3d08..bff63ab 100644
--- a/src/main/kotlin/top/fatweb/api/entity/permission/User.kt
+++ b/src/main/kotlin/top/fatweb/api/entity/permission/User.kt
@@ -12,7 +12,12 @@ import java.io.Serializable
* @since 2023-10-04
*/
@TableName("t_user")
-class User : Serializable {
+class User() : Serializable {
+ constructor(username: String, password: String, enable: Boolean = true) : this() {
+ this.username = username
+ this.password = password
+ this.enable = if (enable) 1 else 0
+ }
@TableId("id")
var id: Long? = null
@@ -44,13 +49,6 @@ class User : Serializable {
var version: Int? = null
override fun toString(): String {
- return "User{" +
- "id=" + id +
- ", username=" + username +
- ", password=" + password +
- ", enable=" + enable +
- ", deleted=" + deleted +
- ", version=" + version +
- "}"
+ return "User{id=$id, username=$username, password=$password, enable=$enable, deleted=$deleted, version=$version}"
}
}
diff --git a/src/main/kotlin/top/fatweb/api/handler/ExceptionHandler.kt b/src/main/kotlin/top/fatweb/api/handler/ExceptionHandler.kt
index 4d982d0..412c401 100644
--- a/src/main/kotlin/top/fatweb/api/handler/ExceptionHandler.kt
+++ b/src/main/kotlin/top/fatweb/api/handler/ExceptionHandler.kt
@@ -3,7 +3,10 @@ package top.fatweb.api.handler
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.http.converter.HttpMessageNotReadableException
+import org.springframework.security.authentication.BadCredentialsException
import org.springframework.security.authentication.InsufficientAuthenticationException
+import org.springframework.security.authentication.InternalAuthenticationServiceException
+import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice
import top.fatweb.api.entity.common.ResponseCode
@@ -26,6 +29,22 @@ class ExceptionHandler {
ResponseResult.fail(ResponseCode.SYSTEM_REQUEST_ILLEGAL, e.localizedMessage.split(":")[0], null)
}
+ is MethodArgumentNotValidException -> {
+ log.debug(e.localizedMessage, e)
+ val errorMessage = e.allErrors.map { error -> error.defaultMessage }.joinToString(". ")
+ ResponseResult.fail(ResponseCode.SYSTEM_ARGUMENT_NOT_VALID, errorMessage, null)
+ }
+
+ is InternalAuthenticationServiceException -> {
+ log.debug(e.localizedMessage, e)
+ ResponseResult.fail(ResponseCode.SYSTEM_USERNAME_NOT_FOUND, e.localizedMessage, null)
+ }
+
+ is BadCredentialsException -> {
+ log.debug(e.localizedMessage, e)
+ ResponseResult.fail(ResponseCode.SYSTEM_LOGIN_USERNAME_PASSWORD_ERROR, e.localizedMessage, null)
+ }
+
else -> {
log.error(e.localizedMessage, e)
ResponseResult.fail(ResponseCode.SYSTEM_ERROR, data = null)
diff --git a/src/main/kotlin/top/fatweb/api/service/permission/impl/UserDetailsServiceImpl.kt b/src/main/kotlin/top/fatweb/api/service/permission/impl/UserDetailsServiceImpl.kt
new file mode 100644
index 0000000..6b78e76
--- /dev/null
+++ b/src/main/kotlin/top/fatweb/api/service/permission/impl/UserDetailsServiceImpl.kt
@@ -0,0 +1,19 @@
+package top.fatweb.api.service.permission.impl
+
+import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
+import org.springframework.security.core.userdetails.UserDetails
+import org.springframework.security.core.userdetails.UserDetailsService
+import org.springframework.stereotype.Service
+import top.fatweb.api.entity.permission.LoginUser
+import top.fatweb.api.entity.permission.User
+import top.fatweb.api.service.IUserService
+
+@Service
+class UserDetailsServiceImpl(val userService: IUserService) : UserDetailsService {
+ override fun loadUserByUsername(username: String?): UserDetails {
+ val user = userService.getOne(KtQueryWrapper(User()).eq(User::username, username))
+ user ?: let { throw Exception("Username not found") }
+
+ return LoginUser(user)
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/application-config-template.yml b/src/main/resources/application-config-template.yml
index 1e91da0..5280697 100644
--- a/src/main/resources/application-config-template.yml
+++ b/src/main/resources/application-config-template.yml
@@ -4,7 +4,7 @@ app:
# token-prefix: "Bearer " # Token prefix
# jwt-ttl: 2 # The life of token
# jwt-ttl-unit: hours # Unit of life of token
- jwt-key: $uuid$ # Key to generate token
+ jwt-key: $uuid$ # Key to generate token (Only numbers and letters allow)
# jwt-issuer: FatWeb # Token issuer
server:
diff --git a/src/test/kotlin/top/fatweb/api/FatWebApiApplicationTests.kt b/src/test/kotlin/top/fatweb/api/FatWebApiApplicationTests.kt
index 9eb700d..9dc0d31 100644
--- a/src/test/kotlin/top/fatweb/api/FatWebApiApplicationTests.kt
+++ b/src/test/kotlin/top/fatweb/api/FatWebApiApplicationTests.kt
@@ -16,4 +16,15 @@ class FatWebApiApplicationTests {
fun removePrefixTest() {
assertEquals("12312", "Bearer 12312".removePrefix(SecurityConstants.tokenPrefix))
}
+
+ /*
+ @Test
+ fun addUser(@Autowired userService: IUserService, @Autowired passwordEncoder: PasswordEncoder) {
+ val username = "admin"
+ val rawPassword = "admin"
+ val encodedPassword = passwordEncoder.encode(rawPassword)
+ val user = User(username, encodedPassword)
+ userService.save(user)
+ }
+ */
}