Add register and verify to AuthenticationController

This commit is contained in:
2023-12-21 18:35:42 +08:00
parent e7c8311b83
commit c40bb0bbaa
25 changed files with 275 additions and 42 deletions

View File

@@ -36,19 +36,7 @@ class AvatarController(
@Operation(summary = "获取随机头像")
@GetMapping(produces = [MediaType.IMAGE_PNG_VALUE])
fun getRandom(@Valid avatarBaseParam: AvatarBaseParam?): ByteArray =
when ((1..4).random()) {
1 -> avatarService.triangle(avatarBaseParam)
2 -> avatarService.square(avatarBaseParam)
3 -> avatarService.identicon(avatarBaseParam)
else -> avatarService.github(AvatarGitHubParam().apply {
seed = avatarBaseParam?.seed
size = avatarBaseParam?.size
margin = avatarBaseParam?.margin
padding = avatarBaseParam?.padding
colors = avatarBaseParam?.colors
background = avatarBaseParam?.background
})
}
avatarService.random(avatarBaseParam)
/**
* Get random avatar as base64
@@ -67,19 +55,7 @@ class AvatarController(
@Valid avatarBaseParam: AvatarBaseParam?
): ResponseResult<AvatarBase64Vo> =
ResponseResult.success(
ResponseCode.API_AVATAR_SUCCESS, data = when ((1..4).random()) {
1 -> avatarService.triangleBase64(avatarBaseParam)
2 -> avatarService.squareBase64(avatarBaseParam)
3 -> avatarService.identiconBase64(avatarBaseParam)
else -> avatarService.githubBase64(AvatarGitHubParam().apply {
seed = avatarBaseParam?.seed
size = avatarBaseParam?.size
margin = avatarBaseParam?.margin
padding = avatarBaseParam?.padding
colors = avatarBaseParam?.colors
background = avatarBaseParam?.background
})
}
ResponseCode.API_AVATAR_SUCCESS, data = avatarService.randomBase64(avatarBaseParam)
)
/**

View File

@@ -10,6 +10,8 @@ import top.fatweb.api.annotation.BaseController
import top.fatweb.api.entity.common.ResponseCode
import top.fatweb.api.entity.common.ResponseResult
import top.fatweb.api.param.permission.LoginParam
import top.fatweb.api.param.permission.RegisterParam
import top.fatweb.api.param.permission.VerifyParam
import top.fatweb.api.service.permission.IAuthenticationService
import top.fatweb.api.util.WebUtil
import top.fatweb.api.vo.permission.LoginVo
@@ -26,6 +28,34 @@ import top.fatweb.api.vo.permission.TokenVo
class AuthenticationController(
private val authenticationService: IAuthenticationService
) {
/**
* Register
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Operation(summary = "注册")
@PostMapping("/register")
fun register(@Valid @RequestBody registerParam: RegisterParam): ResponseResult<Nothing> {
authenticationService.register(registerParam)
return ResponseResult.success(ResponseCode.PERMISSION_REGISTER_SUCCESS)
}
/**
* Verify
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Operation(summary = "验证")
@PostMapping("/verify")
fun verify(@Valid @RequestBody verifyParam: VerifyParam): ResponseResult<Nothing> {
authenticationService.verify(verifyParam)
return ResponseResult.success(ResponseCode.PERMISSION_VERIFY_SUCCESS)
}
/**
* Login
*

View File

@@ -74,6 +74,7 @@ object UserConverter {
fun userToUserWithRoleInfoVo(user: User) = UserWithRoleInfoVo(
id = user.id,
username = user.username,
verify = user.verify,
locking = user.locking?.let { it == 1 },
expiration = user.expiration,
credentialsExpiration = user.credentialsExpiration,
@@ -137,6 +138,7 @@ object UserConverter {
id = user.id,
username = user.username,
password = user.password,
verify = user.verify,
locking = user.locking?.let { it == 1 },
expiration = user.expiration,
credentialsExpiration = user.credentialsExpiration,

View File

@@ -18,6 +18,8 @@ enum class ResponseCode(val code: Int) {
PERMISSION_PASSWORD_CHANGE_SUCCESS(BusinessCode.PERMISSION, 1),
PERMISSION_LOGOUT_SUCCESS(BusinessCode.PERMISSION, 2),
PERMISSION_TOKEN_RENEW_SUCCESS(BusinessCode.PERMISSION, 3),
PERMISSION_REGISTER_SUCCESS(BusinessCode.PERMISSION, 4),
PERMISSION_VERIFY_SUCCESS(BusinessCode.PERMISSION, 5),
PERMISSION_UNAUTHORIZED(BusinessCode.PERMISSION, 50),
PERMISSION_USERNAME_NOT_FOUND(BusinessCode.PERMISSION, 51),

View File

@@ -46,6 +46,15 @@ class User() : Serializable {
@TableField("password")
var password: String? = null
/**
* Verify
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@TableField("verify")
var verify: String? = null
/**
* Locking
*

View File

@@ -19,7 +19,7 @@ data class LoginParam(
*/
@Schema(description = "账户", example = "test", required = true)
@field:NotBlank(message = "Account can not be blank")
val account: String? = null,
val account: String?,
/**
* Password
@@ -29,5 +29,5 @@ data class LoginParam(
*/
@Schema(description = "密码", example = "test123456", required = true)
@field:NotBlank(message = "Password can not be blank")
val password: String? = null
val password: String?
)

View File

@@ -0,0 +1,34 @@
package top.fatweb.api.param.permission
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.Email
import jakarta.validation.constraints.NotBlank
/**
* Register parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "注册请求参数")
data class RegisterParam(
/**
* Email
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "邮箱", example = "guest@fatweb.top", required = true)
@field:Email(message = "Illegal email address")
val email: String?,
/**
* Password
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "密码", example = "test123456", required = true)
@field:NotBlank(message = "Password can not be blank")
val password: String?
)

View File

@@ -0,0 +1,50 @@
package top.fatweb.api.param.permission
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.NotBlank
/**
* Verify parameters
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "验证请求参数")
data class VerifyParam(
/**
* Code
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "验证码", example = "9c4b8199-1dbe-4f6f-96a5-fe1d75cc6a65", required = true)
@field:NotBlank(message = "Code can not be blank")
val code: String?,
/**
* Username
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "用户名", example = "adb")
val username: String?,
/**
* Nickname
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "昵称", example = "QwQ")
val nickname: String?,
/**
* Avatar
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "头像")
val avatar: String?
)

View File

@@ -37,5 +37,5 @@ data class GroupAddParam(
* @since 1.0.0
*/
@Schema(description = "角色 ID 列表")
val roleIds: List<Long>? = null
val roleIds: List<Long>?
)

View File

@@ -19,7 +19,7 @@ data class GroupGetParam(
* @since 1.0.0
*/
@Schema(description = "查询用户组名称")
val searchName: String? = null,
val searchName: String?,
/**
* Use regex

View File

@@ -48,5 +48,5 @@ data class GroupUpdateParam(
* @since 1.0.0
*/
@Schema(description = "角色 ID 列表")
val roleIds: List<Long>? = null
val roleIds: List<Long>?
)

View File

@@ -37,5 +37,5 @@ data class RoleAddParam(
* @since 1.0.0
*/
@Schema(description = "权限 ID 列表")
val powerIds: List<Long>? = null
val powerIds: List<Long>?
)

View File

@@ -19,7 +19,7 @@ data class RoleGetParam(
* @since 1.0.0
*/
@Schema(description = "查询角色名称")
val searchName: String? = null,
val searchName: String?,
/**
* Use regex

View File

@@ -48,5 +48,5 @@ data class RoleUpdateParam(
* @since 1.0.0
*/
@Schema(description = "权限 ID 列表")
val powerIds: List<Long>? = null
val powerIds: List<Long>?
)

View File

@@ -32,6 +32,15 @@ data class UserAddParam(
@Schema(description = "密码(为空自动生成随机密码)")
val password: String?,
/**
* Verified
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "是否已验证")
val verified: Boolean = false,
/**
* Locking
*

View File

@@ -28,7 +28,7 @@ data class UserGetParam(
* @since 1.0.0
*/
@Schema(description = "查询内容")
val searchValue: String? = null,
val searchValue: String?,
/**
* Use regex

View File

@@ -32,6 +32,15 @@ data class UserUpdateParam(
@Schema(description = "用户名")
val username: String?,
/**
* Verified
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "是否已验证")
val verified: Boolean = false,
/**
* Locking
*

View File

@@ -21,7 +21,7 @@ data class SysLogGetParam(
* @since 1.0.0
*/
@Schema(description = "类型过滤(多个使用逗号分隔)", example = "INFO", allowableValues = ["INFO", "ERROR"])
val logType: String? = null,
val logType: String?,
/**
* Request method to filter
@@ -34,7 +34,7 @@ data class SysLogGetParam(
example = "GET,POST",
allowableValues = ["GET", "POST", "PUT", "PATCH", "DELETE", "DELETE", "OPTIONS"]
)
val requestMethod: String? = null,
val requestMethod: String?,
/**
* Request URL to search for
@@ -43,7 +43,7 @@ data class SysLogGetParam(
* @since 1.0.0
*/
@Schema(description = "查询请求 Url")
val searchRequestUrl: String? = null,
val searchRequestUrl: String?,
/**
* Use regex
@@ -65,7 +65,7 @@ data class SysLogGetParam(
*/
@Schema(description = "查询开始时间")
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
val searchStartTime: LocalDateTime? = null,
val searchStartTime: LocalDateTime?,
/**
* End time to search for
@@ -76,5 +76,5 @@ data class SysLogGetParam(
*/
@Schema(description = "查询结束时间")
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
val searchEndTime: LocalDateTime? = null
val searchEndTime: LocalDateTime?
) : PageSortParam()

View File

@@ -11,6 +11,22 @@ import top.fatweb.api.vo.api.v1.avatar.AvatarBase64Vo
* @since 1.0.0
*/
interface IAvatarService {
/**
* Generate random avatar
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
fun random(avatarBaseParam: AvatarBaseParam?): ByteArray
/**
* Generate random avatar as base64
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
fun randomBase64(avatarBaseParam: AvatarBaseParam?): AvatarBase64Vo
/**
* Generate triangle style avatar
*

View File

@@ -25,6 +25,34 @@ import kotlin.io.encoding.ExperimentalEncodingApi
@OptIn(ExperimentalEncodingApi::class)
@Service
class AvatarServiceImpl : IAvatarService {
override fun random(avatarBaseParam: AvatarBaseParam?): ByteArray =
when ((1..4).random()) {
1 -> triangle(avatarBaseParam)
2 -> square(avatarBaseParam)
3 -> identicon(avatarBaseParam)
else -> github(AvatarGitHubParam().apply {
seed = avatarBaseParam?.seed
size = avatarBaseParam?.size
margin = avatarBaseParam?.margin
padding = avatarBaseParam?.padding
colors = avatarBaseParam?.colors
background = avatarBaseParam?.background
})
}
override fun randomBase64(avatarBaseParam: AvatarBaseParam?): AvatarBase64Vo = when ((1..4).random()) {
1 -> triangleBase64(avatarBaseParam)
2 -> squareBase64(avatarBaseParam)
3 -> identiconBase64(avatarBaseParam)
else -> githubBase64(AvatarGitHubParam().apply {
seed = avatarBaseParam?.seed
size = avatarBaseParam?.size
margin = avatarBaseParam?.margin
padding = avatarBaseParam?.padding
colors = avatarBaseParam?.colors
background = avatarBaseParam?.background
})
}
override fun triangle(avatarBaseParam: AvatarBaseParam?): ByteArray {
val avatar = (

View File

@@ -3,6 +3,8 @@ package top.fatweb.api.service.permission
import jakarta.servlet.http.HttpServletRequest
import top.fatweb.api.entity.permission.User
import top.fatweb.api.param.permission.LoginParam
import top.fatweb.api.param.permission.RegisterParam
import top.fatweb.api.param.permission.VerifyParam
import top.fatweb.api.vo.permission.LoginVo
import top.fatweb.api.vo.permission.TokenVo
@@ -13,6 +15,22 @@ import top.fatweb.api.vo.permission.TokenVo
* @since 1.0.0
*/
interface IAuthenticationService {
/**
* Register
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
fun register(registerParam: RegisterParam)
/**
* Verify
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
fun verify(verifyParam: VerifyParam)
/**
* Login
*

View File

@@ -6,15 +6,22 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import top.fatweb.api.annotation.EventLogRecord
import top.fatweb.api.entity.permission.LoginUser
import top.fatweb.api.entity.permission.User
import top.fatweb.api.entity.permission.UserInfo
import top.fatweb.api.entity.system.EventLog
import top.fatweb.api.exception.TokenHasExpiredException
import top.fatweb.api.param.permission.LoginParam
import top.fatweb.api.param.permission.RegisterParam
import top.fatweb.api.param.permission.VerifyParam
import top.fatweb.api.properties.SecurityProperties
import top.fatweb.api.service.api.v1.IAvatarService
import top.fatweb.api.service.permission.IAuthenticationService
import top.fatweb.api.service.permission.IUserInfoService
import top.fatweb.api.service.permission.IUserService
import top.fatweb.api.util.JwtUtil
import top.fatweb.api.util.RedisUtil
@@ -23,6 +30,7 @@ import top.fatweb.api.vo.permission.LoginVo
import top.fatweb.api.vo.permission.TokenVo
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.util.*
/**
* Authentication service implement
@@ -37,11 +45,33 @@ import java.time.ZoneOffset
@Service
class AuthenticationServiceImpl(
private val authenticationManager: AuthenticationManager,
private val passwordEncoder: PasswordEncoder,
private val redisUtil: RedisUtil,
private val userService: IUserService
private val userService: IUserService,
private val userInfoService: IUserInfoService,
private val avatarService: IAvatarService
) : IAuthenticationService {
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
@Transactional
override fun register(registerParam: RegisterParam) {
val user = User().apply {
username = "\$UNNAMED_${UUID.randomUUID()}"
password = passwordEncoder.encode(registerParam.password)
locking = 0
enable = 1
}
userService.save(user)
userInfoService.save(UserInfo().apply {
userId = user.id
avatar = avatarService.randomBase64(null).base64
email = registerParam.email
})
}
override fun verify(verifyParam: VerifyParam) {
}
@EventLogRecord(EventLog.Event.LOGIN)
override fun login(request: HttpServletRequest, loginParam: LoginParam): LoginVo {
val usernamePasswordAuthenticationToken =

View File

@@ -43,6 +43,15 @@ data class UserWithPasswordRoleInfoVo(
@Schema(description = "密码")
val password: String?,
/**
* Verify
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "验证信息")
val verify: String?,
/**
* Locking
*

View File

@@ -34,6 +34,15 @@ data class UserWithRoleInfoVo(
@Schema(description = "用户名", example = "User")
val username: String?,
/**
* Verify
*
* @author FatttSnake, fatttsnake@gmail.com
* @since 1.0.0
*/
@Schema(description = "验证信息")
val verify: String?,
/**
* Locking
*

View File

@@ -5,6 +5,7 @@ create table if not exists t_user
id bigint not null primary key,
username varchar(20) not null comment '用户名',
password char(70) not null comment '密码',
verify varchar(36) null comment '验证信息',
locking int not null comment '锁定',
expiration datetime comment '过期时间',
credentials_expiration datetime comment '认证过期时间',
@@ -17,5 +18,6 @@ create table if not exists t_user
update_time datetime not null default (utc_timestamp()) comment '修改时间',
deleted bigint not null default 0,
version int not null default 0,
constraint t_user_unique unique (username, deleted)
constraint t_user_unique_username unique (username, deleted),
constraint t_user_unique_verify unique (verify, deleted)
) comment '用户表';