Optimize code

This commit is contained in:
2023-10-31 15:38:48 +08:00
parent 7b2ee34917
commit a8ba913ee2
32 changed files with 156 additions and 58 deletions

View File

@@ -4,31 +4,50 @@ import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
import jakarta.annotation.PostConstruct
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.DependsOn
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Component
import top.fatweb.api.entity.permission.User
import top.fatweb.api.entity.permission.UserInfo
import top.fatweb.api.properties.AdminProperties
import top.fatweb.api.service.permission.IUserInfoService
import top.fatweb.api.service.permission.IUserService
import kotlin.random.Random
@DependsOn("adminProperties")
@Component
class InitConfig(
private val userService: IUserService, private val passwordEncoder: PasswordEncoder
private val userService: IUserService,
private val userInfoService: IUserInfoService,
private val passwordEncoder: PasswordEncoder
) {
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
@PostConstruct
fun init() {
if (!userService.exists(KtQueryWrapper(User()).eq(User::id, 0))) {
val rawPassword = getRandPassword(10)
userInfoService.remove(KtQueryWrapper(UserInfo()).eq(UserInfo::userId, 0))
val rawPassword = AdminProperties.password ?: let {
logger.warn("No default administrator password is set, a randomly generated password will be used")
getRandPassword(10)
}
val encodedPassword = passwordEncoder.encode(rawPassword)
val user = User().apply {
id = 0
username = "admin"
username = AdminProperties.username
password = encodedPassword
locking = 0
enable = 1
}
if (userService.save(user)) {
val userInfo = UserInfo().apply {
userId = 0
nickName = AdminProperties.nickName
email = AdminProperties.email
}
if (userService.save(user) && userInfoService.save(userInfo)) {
logger.warn("First startup, create administrator - username: admin, password: $rawPassword")
logger.warn("This information will only be shown once. Please change your password promptly after logging in.")
}

View File

@@ -5,7 +5,7 @@ import io.swagger.v3.oas.models.info.Contact
import io.swagger.v3.oas.models.info.Info
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import top.fatweb.api.constant.ServerConstants
import top.fatweb.api.properties.ServerProperties
@Configuration
class SwaggerConfig {
@@ -16,7 +16,7 @@ class SwaggerConfig {
return OpenAPI().info(
Info().title("FatWeb API 文档").description("FatWeb 后端 API 文档,包含各个 Controller 调用信息")
.contact(contact).version(
ServerConstants.version
ServerProperties.version
)
)
}

View File

@@ -8,7 +8,7 @@ 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.RestController
import top.fatweb.api.converter.UserConverter
import top.fatweb.api.converter.permission.UserConverter
import top.fatweb.api.entity.common.ResponseCode
import top.fatweb.api.entity.common.ResponseResult
import top.fatweb.api.param.authentication.LoginParam

View File

@@ -3,10 +3,10 @@ package top.fatweb.api.controller.permission
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import top.fatweb.api.converter.UserConverter
import top.fatweb.api.converter.permission.UserConverter
import top.fatweb.api.entity.common.ResponseResult
import top.fatweb.api.service.permission.IUserService
import top.fatweb.api.vo.authentication.UserWithInfoVo
import top.fatweb.api.vo.permission.UserWithInfoVo
/**
* <p>

View File

@@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.GetMapping
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.converter.SysLogConverter
import top.fatweb.api.converter.system.SysLogConverter
import top.fatweb.api.entity.common.ResponseCode
import top.fatweb.api.entity.common.ResponseResult
import top.fatweb.api.param.system.SysLogGetParam

View File

@@ -1,8 +1,8 @@
package top.fatweb.api.converter
package top.fatweb.api.converter.permission
import top.fatweb.api.entity.permission.User
import top.fatweb.api.param.authentication.LoginParam
import top.fatweb.api.vo.authentication.*
import top.fatweb.api.vo.permission.*
object UserConverter {
fun loginParamToUser(loginParam: LoginParam): User {
@@ -27,6 +27,15 @@ object UserConverter {
lastLoginIp = user.lastLoginIp,
createTime = user.createTime,
updateTime = user.updateTime,
userInfo = user.userInfo?.let { UserInfoVo(
id = it.id,
userId = it.userId,
nickName = it.nickName,
avatar = it.avatar,
email = it.email,
createTime = it.createTime,
updateTime = it.updateTime
) },
modules = user.modules?.map {
ModuleVo(
id = it.id,

View File

@@ -1,4 +1,4 @@
package top.fatweb.api.converter
package top.fatweb.api.converter.system
import com.baomidou.mybatisplus.core.metadata.IPage
import top.fatweb.api.entity.system.SysLog

View File

@@ -104,6 +104,9 @@ class User() : Serializable {
@Version
var version: Int? = null
@TableField(exist = false)
var userInfo: UserInfo? = null
@TableField(exist = false)
var roles: List<Role>? = null
@@ -123,6 +126,6 @@ class User() : Serializable {
var operations: List<Operation>? = null
override fun toString(): String {
return "User(id=$id, username=$username, password=$password, locking=$locking, expiration=$expiration, credentialsExpiration=$credentialsExpiration, enable=$enable, currentLoginTime=$currentLoginTime, currentLoginIp=$currentLoginIp, lastLoginTime=$lastLoginTime, lastLoginIp=$lastLoginIp, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version, roles=$roles, groups=$groups, menus=$menus, elements=$elements, operations=$operations)"
return "User(id=$id, username=$username, password=$password, locking=$locking, expiration=$expiration, credentialsExpiration=$credentialsExpiration, enable=$enable, currentLoginTime=$currentLoginTime, currentLoginIp=$currentLoginIp, lastLoginTime=$lastLoginTime, lastLoginIp=$lastLoginIp, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version, userInfo=$userInfo, roles=$roles, groups=$groups, modules=$modules, menus=$menus, elements=$elements, operations=$operations)"
}
}

View File

@@ -6,7 +6,7 @@ import java.time.LocalDateTime
/**
* <p>
* 用户信息
* 用户资料
* </p>
*
* @author FatttSnake

View File

@@ -8,9 +8,9 @@ import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Component
import org.springframework.util.StringUtils
import org.springframework.web.filter.OncePerRequestFilter
import top.fatweb.api.constant.SecurityConstants
import top.fatweb.api.entity.permission.LoginUser
import top.fatweb.api.exception.TokenHasExpiredException
import top.fatweb.api.properties.SecurityProperties
import top.fatweb.api.util.JwtUtil
import top.fatweb.api.util.RedisUtil
import top.fatweb.api.util.WebUtil
@@ -20,7 +20,7 @@ class JwtAuthenticationTokenFilter(private val redisUtil: RedisUtil) : OncePerRe
override fun doFilterInternal(
request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain
) {
val tokenWithPrefix = request.getHeader(SecurityConstants.headerString)
val tokenWithPrefix = request.getHeader(SecurityProperties.headerString)
if (!StringUtils.hasText(tokenWithPrefix) || "/error/thrown" == request.servletPath) {
filterChain.doFilter(request, response)
@@ -30,11 +30,11 @@ class JwtAuthenticationTokenFilter(private val redisUtil: RedisUtil) : OncePerRe
val token = WebUtil.getToken(tokenWithPrefix)
JwtUtil.parseJwt(token)
val redisKey = "${SecurityConstants.jwtIssuer}_login:" + token
val redisKey = "${SecurityProperties.jwtIssuer}_login:" + token
val loginUser = redisUtil.getObject<LoginUser>(redisKey)
loginUser ?: let { throw TokenHasExpiredException() }
redisUtil.setExpire(redisKey, SecurityConstants.redisTtl, SecurityConstants.redisTtlUnit)
redisUtil.setExpire(redisKey, SecurityProperties.redisTtl, SecurityProperties.redisTtlUnit)
val authenticationToken = UsernamePasswordAuthenticationToken(loginUser, null, loginUser.authorities)
SecurityContextHolder.getContext().authentication = authenticationToken

View File

@@ -6,7 +6,7 @@ import top.fatweb.api.entity.permission.UserInfo
/**
* <p>
* 用户信息表 Mapper 接口
* 用户资料表 Mapper 接口
* </p>
*
* @author FatttSnake

View File

@@ -0,0 +1,13 @@
package top.fatweb.api.properties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.stereotype.Component
@Component
@ConfigurationProperties("app.admin")
object AdminProperties {
var username = "admin"
var password: String? = null
var nickName = "Administrator"
var email = "admin@fatweb.top"
}

View File

@@ -1,4 +1,4 @@
package top.fatweb.api.constant
package top.fatweb.api.properties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.stereotype.Component
@@ -6,7 +6,7 @@ import java.util.concurrent.TimeUnit
@Component
@ConfigurationProperties("app.security")
object SecurityConstants {
object SecurityProperties {
var headerString = "Authorization"
var tokenPrefix = "Bearer "

View File

@@ -1,4 +1,4 @@
package top.fatweb.api.constant
package top.fatweb.api.properties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.stereotype.Component
@@ -8,7 +8,7 @@ import java.time.ZonedDateTime
@Component
@ConfigurationProperties("app")
object ServerConstants {
object ServerProperties {
lateinit var version: String
lateinit var buildTime: String

View File

@@ -2,8 +2,8 @@ package top.fatweb.api.service.permission
import jakarta.servlet.http.HttpServletRequest
import top.fatweb.api.entity.permission.User
import top.fatweb.api.vo.authentication.LoginVo
import top.fatweb.api.vo.authentication.TokenVo
import top.fatweb.api.vo.permission.LoginVo
import top.fatweb.api.vo.permission.TokenVo
interface IAuthenticationService {
fun login(request: HttpServletRequest, user: User): LoginVo

View File

@@ -5,7 +5,7 @@ import top.fatweb.api.entity.permission.UserInfo
/**
* <p>
* 用户信息表 服务类
* 用户资料表 服务类
* </p>
*
* @author FatttSnake

View File

@@ -7,17 +7,17 @@ import org.slf4j.LoggerFactory
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.stereotype.Service
import top.fatweb.api.constant.SecurityConstants
import top.fatweb.api.entity.permission.LoginUser
import top.fatweb.api.entity.permission.User
import top.fatweb.api.exception.TokenHasExpiredException
import top.fatweb.api.properties.SecurityProperties
import top.fatweb.api.service.permission.IAuthenticationService
import top.fatweb.api.service.permission.IUserService
import top.fatweb.api.util.JwtUtil
import top.fatweb.api.util.RedisUtil
import top.fatweb.api.util.WebUtil
import top.fatweb.api.vo.authentication.LoginVo
import top.fatweb.api.vo.authentication.TokenVo
import top.fatweb.api.vo.permission.LoginVo
import top.fatweb.api.vo.permission.TokenVo
import java.time.LocalDateTime
import java.time.ZoneOffset
@@ -54,18 +54,18 @@ class AuthenticationServiceImpl(
throw RuntimeException("Login failed")
}
val redisKey = "${SecurityConstants.jwtIssuer}_login:" + jwt
redisUtil.setObject(redisKey, loginUser, SecurityConstants.redisTtl, SecurityConstants.redisTtlUnit)
val redisKey = "${SecurityProperties.jwtIssuer}_login:" + jwt
redisUtil.setObject(redisKey, loginUser, SecurityProperties.redisTtl, SecurityProperties.redisTtlUnit)
return LoginVo(jwt, loginUser.user.currentLoginTime, loginUser.user.currentLoginIp)
}
override fun logout(token: String): Boolean = redisUtil.delObject("${SecurityConstants.jwtIssuer}_login:" + token)
override fun logout(token: String): Boolean = redisUtil.delObject("${SecurityProperties.jwtIssuer}_login:" + token)
override fun renewToken(token: String): TokenVo {
val loginUser = WebUtil.getLoginUser() ?: let { throw TokenHasExpiredException() }
val oldRedisKey = "${SecurityConstants.jwtIssuer}_login:" + token
val oldRedisKey = "${SecurityProperties.jwtIssuer}_login:" + token
redisUtil.delObject(oldRedisKey)
val jwt = JwtUtil.createJwt(WebUtil.getLoginUserId().toString())
@@ -73,9 +73,9 @@ class AuthenticationServiceImpl(
throw RuntimeException("Login failed")
}
val redisKey = "${SecurityConstants.jwtIssuer}_login:" + jwt
val redisKey = "${SecurityProperties.jwtIssuer}_login:" + jwt
redisUtil.setObject(
redisKey, loginUser, SecurityConstants.redisTtl, SecurityConstants.redisTtlUnit
redisKey, loginUser, SecurityProperties.redisTtl, SecurityProperties.redisTtlUnit
)
return TokenVo(jwt)

View File

@@ -8,7 +8,7 @@ import top.fatweb.api.service.permission.IUserInfoService
/**
* <p>
* 用户信息表 服务实现类
* 用户资料表 服务实现类
* </p>
*
* @author FatttSnake

View File

@@ -3,7 +3,7 @@ package top.fatweb.api.util
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import com.auth0.jwt.interfaces.DecodedJWT
import top.fatweb.api.constant.SecurityConstants
import top.fatweb.api.properties.SecurityProperties
import java.util.*
import java.util.concurrent.TimeUnit
import javax.crypto.spec.SecretKeySpec
@@ -17,7 +17,7 @@ object JwtUtil {
* @return 密钥
*/
private fun generalKey(): SecretKeySpec {
val encodeKey = Base64.getDecoder().decode(SecurityConstants.jwtKey)
val encodeKey = Base64.getDecoder().decode(SecurityProperties.jwtKey)
return SecretKeySpec(encodeKey, 0, encodeKey.size, "AES")
}
@@ -34,8 +34,8 @@ object JwtUtil {
*/
fun createJwt(
subject: String,
ttl: Long = SecurityConstants.jwtTtl,
timeUnit: TimeUnit = SecurityConstants.jwtTtlUnit,
ttl: Long = SecurityProperties.jwtTtl,
timeUnit: TimeUnit = SecurityProperties.jwtTtlUnit,
uuid: String = getUUID()
): String? {
val nowMillis = System.currentTimeMillis()
@@ -52,7 +52,7 @@ object JwtUtil {
val expMillis = nowMillis + unitTtl
val expDate = Date(expMillis)
return JWT.create().withJWTId(uuid).withSubject(subject).withIssuer(SecurityConstants.jwtIssuer)
return JWT.create().withJWTId(uuid).withSubject(subject).withIssuer(SecurityProperties.jwtIssuer)
.withIssuedAt(nowDate).withExpiresAt(expDate).sign(algorithm())
}

View File

@@ -2,8 +2,8 @@ package top.fatweb.api.util
import jakarta.servlet.http.HttpServletRequest
import org.springframework.security.core.context.SecurityContextHolder
import top.fatweb.api.constant.SecurityConstants
import top.fatweb.api.entity.permission.LoginUser
import top.fatweb.api.properties.SecurityProperties
object WebUtil {
fun getLoginUser() = if (SecurityContextHolder.getContext().authentication.principal is String) null
@@ -13,7 +13,7 @@ object WebUtil {
fun getLoginUsername() = getLoginUser()?.user?.username
fun getToken(tokenWithPrefix: String) = tokenWithPrefix.removePrefix(SecurityConstants.tokenPrefix)
fun getToken(tokenWithPrefix: String) = tokenWithPrefix.removePrefix(SecurityProperties.tokenPrefix)
fun getToken(request: HttpServletRequest) = getToken(request.getHeader(SecurityConstants.headerString))
fun getToken(request: HttpServletRequest) = getToken(request.getHeader(SecurityProperties.headerString))
}

View File

@@ -1,4 +1,4 @@
package top.fatweb.api.vo.authentication
package top.fatweb.api.vo.permission
import io.swagger.v3.oas.annotations.media.Schema

View File

@@ -1,4 +1,4 @@
package top.fatweb.api.vo.authentication
package top.fatweb.api.vo.permission
import io.swagger.v3.oas.annotations.media.Schema
import java.time.LocalDateTime

View File

@@ -1,4 +1,4 @@
package top.fatweb.api.vo.authentication
package top.fatweb.api.vo.permission
import io.swagger.v3.oas.annotations.media.Schema

View File

@@ -1,4 +1,4 @@
package top.fatweb.api.vo.authentication
package top.fatweb.api.vo.permission
import io.swagger.v3.oas.annotations.media.Schema

View File

@@ -1,4 +1,4 @@
package top.fatweb.api.vo.authentication
package top.fatweb.api.vo.permission
import io.swagger.v3.oas.annotations.media.Schema

View File

@@ -1,4 +1,4 @@
package top.fatweb.api.vo.authentication
package top.fatweb.api.vo.permission
import io.swagger.v3.oas.annotations.media.Schema

View File

@@ -0,0 +1,31 @@
package top.fatweb.api.vo.permission
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
import io.swagger.v3.oas.annotations.media.Schema
import java.time.LocalDateTime
@Schema(description = "用户资料返回参数")
data class UserInfoVo(
@JsonSerialize(using = ToStringSerializer::class)
val id: Long?,
@Schema(description = "用户ID")
@JsonSerialize(using = ToStringSerializer::class)
val userId: Long?,
@Schema(description = "昵称", example = "User")
val nickName: String?,
@Schema(description = "头像")
val avatar: String?,
@Schema(description = "邮箱", example = "user@fatweb.top")
val email: String?,
@Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z")
val createTime: LocalDateTime?,
@Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z")
val updateTime: LocalDateTime?
)

View File

@@ -1,4 +1,4 @@
package top.fatweb.api.vo.authentication
package top.fatweb.api.vo.permission
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
@@ -43,6 +43,9 @@ data class UserWithInfoVo(
@Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z")
val updateTime: LocalDateTime?,
@Schema(description = "用户资料")
val userInfo: UserInfoVo?,
@Schema(description = "模块列表")
val modules: List<ModuleVo>?,

View File

@@ -1,4 +1,9 @@
app:
admin:
# username: admin # Username of administrator
# password: admin # Default password of administrator
# nick-name: Administrator # Nickname of administrator
# email: admin@fatweb.top # Email of administrator
security:
# header-string: "Authorization" # The key of head to get token
# token-prefix: "Bearer " # Token prefix

View File

@@ -10,5 +10,6 @@ create table if not exists t_user_info
create_time datetime not null default (utc_timestamp()) comment '创建时间',
update_time datetime not null default (utc_timestamp()) comment '修改时间',
deleted bigint not null default 0,
version int not null default 0
) comment '用户信息表';
version int not null default 0,
constraint t_user_info_unique unique (user_id, deleted)
) comment '用户资料表';

View File

@@ -18,6 +18,7 @@
t_user.deleted as user_deleted,
t_user.version as user_version,
tui.id as user_info_id,
tui.user_id as user_info_user_id,
tui.nick_name as user_info_nick_name,
tui.avatar as user_info_avatar,
tui.email as user_info_email,
@@ -61,7 +62,7 @@
and t_user.username = #{username};
</select>
<resultMap id="userBase" type="user">
<resultMap id="userBaseMap" type="user">
<id property="id" column="user_id"/>
<result property="username" column="user_username"/>
<result property="locking" column="user_locking"/>
@@ -78,8 +79,21 @@
<result property="version" column="user_version"/>
</resultMap>
<resultMap id="userWithPowerMap" type="user" extends="userBase">
<resultMap id="userInfoMap" type="userInfo">
<id property="id" column="user_info_id"/>
<result property="userId" column="user_info_user_id"/>
<result property="nickName" column="user_info_nick_name"/>
<result property="avatar" column="user_info_avatar"/>
<result property="email" column="user_info_email"/>
<result property="createTime" column="user_info_create_time"/>
<result property="updateTime" column="user_info_update_time"/>
<result property="deleted" column="user_info_deleted"/>
<result property="version" column="user_info_version"/>
</resultMap>
<resultMap id="userWithPowerMap" type="user" extends="userBaseMap">
<result property="password" column="user_password"/>
<association property="userInfo" resultMap="userInfoMap"/>
<collection property="modules" ofType="module">
<id property="id" column="module_id"/>
<result property="name" column="module_name"/>

View File

@@ -6,7 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.test.context.junit.jupiter.SpringExtension
import top.fatweb.api.constant.SecurityConstants
import top.fatweb.api.properties.SecurityProperties
import top.fatweb.api.util.ByteUtil
import top.fatweb.api.util.JwtUtil
@@ -16,7 +16,7 @@ class FatWebApiApplicationTests {
@Test
fun removePrefixTest() {
assertEquals("12312", "Bearer 12312".removePrefix(SecurityConstants.tokenPrefix))
assertEquals("12312", "Bearer 12312".removePrefix(SecurityProperties.tokenPrefix))
}
@Test