feat(auth): implement login logic with database and password verification

- Replace stub with MyBatis-Plus user query and password matching
- Integrate kotlin-logging for structured logging across services
- Add custom Log utility replacing direct SLF4J usage
- Add kotlin-logging dependency to build configuration
This commit is contained in:
2026-05-20 22:30:58 +08:00
parent 402e9e04cd
commit 2cf3806298
6 changed files with 65 additions and 27 deletions
+1
View File
@@ -29,6 +29,7 @@ dependencies {
implementation("com.baomidou:mybatis-plus-spring-boot4-starter:3.5.15") implementation("com.baomidou:mybatis-plus-spring-boot4-starter:3.5.15")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("io.github.oshai:kotlin-logging-jvm:7.0.0")
testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly("org.junit.platform:junit-platform-launcher")
@@ -1,13 +1,16 @@
package com.msksbr.bookmgr.controller package com.msksbr.bookmgr.controller
import com.msksbr.bookmgr.dto.UserLoginDTO import com.msksbr.bookmgr.dto.UserLoginDTO
import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.AuthService import com.msksbr.bookmgr.service.AuthService
import jakarta.servlet.http.HttpSession import jakarta.servlet.http.HttpSession
import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import com.msksbr.bookmgr.template.Result import com.msksbr.bookmgr.template.Result
import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid import jakarta.validation.Valid
import org.springframework.web.bind.annotation.RequestBody
/* /*
* 登录接口 * 登录接口
@@ -21,23 +24,35 @@ class AuthController(
@PostMapping("/login") @PostMapping("/login")
fun login( fun login(
@Valid @Valid
@RequestBody
loginDTO: UserLoginDTO, loginDTO: UserLoginDTO,
session: HttpSession session: HttpSession,
request: HttpServletRequest
) : Result<String> { ) : Result<String> {
log.info("Login from ${request.remoteAddr} username: ${loginDTO.username}")
log.info("UA: ${request.getHeader("User-Agent")}")
// 调用service验证 // 调用service验证
val success=authService.login(loginDTO) val pass=authService.login(loginDTO)
return if (success) { return if (pass) {
// 登录成功,存入session // 登录成功,存入session
session.setAttribute("loginUser", loginDTO.username) session.setAttribute("loginUser", loginDTO.username)
log.info("Login success")
Result.success("login success") Result.success("login success")
}else{ }else{
log.info("Login failed")
Result.error("username or password not match") Result.error("username or password not match")
} }
} }
@PostMapping("/logout") @PostMapping("/logout")
fun logout(session: HttpSession): Result<String> { fun logout(
session: HttpSession,
request:HttpServletRequest
): Result<String> {
log.info("Logout from ${request.remoteAddr}")
log.info("UA: ${request.getHeader("User-Agent")}")
// 直接销毁session // 直接销毁session
session.invalidate() session.invalidate()
log.info("Logout success")
return Result.success("logout successfully") return Result.success("logout successfully")
} }
} }
@@ -5,7 +5,7 @@ import jakarta.validation.constraints.NotBlank
data class UserLoginDTO( data class UserLoginDTO(
@NotBlank(message = "username is required") @NotBlank(message = "username is required")
var username: String, val username: String?,
@NotBlank(message = "password is required") @NotBlank(message = "password is required")
var password: String val password: String?
) )
@@ -3,7 +3,7 @@ package com.msksbr.bookmgr.runner
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.msksbr.bookmgr.entity.User import com.msksbr.bookmgr.entity.User
import com.msksbr.bookmgr.mapper.UserMapper import com.msksbr.bookmgr.mapper.UserMapper
import org.slf4j.LoggerFactory import com.msksbr.bookmgr.script.log
import org.springframework.boot.ApplicationArguments import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner import org.springframework.boot.ApplicationRunner
import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder
@@ -15,45 +15,43 @@ class InitUserRunner(
val passwordEncoder: PasswordEncoder, val passwordEncoder: PasswordEncoder,
val userMapper: UserMapper, val userMapper: UserMapper,
) : ApplicationRunner { ) : ApplicationRunner {
private val logger = LoggerFactory.getLogger(InitUserRunner::class.java)
// 添加注解,失败时可回滚 // 添加注解,失败时可回滚
@Transactional @Transactional
override fun run(args: ApplicationArguments) { override fun run(args: ApplicationArguments) {
logger.info("Starting default user initialization") log.info("Starting default user initialization")
logger.debug("Querying for admin user") log.debug("Querying for admin user")
val existsAdmin = userMapper.selectOne( val existsAdmin = userMapper.selectOne(
QueryWrapper<User>() QueryWrapper<User>()
.eq("username", "admin") .eq("username", "admin")
) )
if (existsAdmin == null) { if (existsAdmin == null) {
logger.info("Admin user not found, creating...") log.info("Admin user not found, creating...")
insertAdmin() insertAdmin()
} else { } else {
logger.debug("Admin user already exists, skipping") log.debug("Admin user already exists, skipping")
} }
logger.debug("Querying for common user01") log.debug("Querying for common user01")
val existsUser01 = userMapper.selectOne( val existsUser01 = userMapper.selectOne(
QueryWrapper<User>() QueryWrapper<User>()
.eq("username", "user01") .eq("username", "user01")
) )
if (existsUser01 == null) { if (existsUser01 == null) {
logger.info("Common user01 not found, creating...") log.info("Common user01 not found, creating...")
insertUser01() insertUser01()
} else { } else {
logger.debug("Common user01 already exists, skipping") log.debug("Common user01 already exists, skipping")
} }
val existsUser02=userMapper.selectOne( val existsUser02=userMapper.selectOne(
QueryWrapper<User>() QueryWrapper<User>()
.eq("username", "user02") .eq("username", "user02")
) )
if (existsUser02 == null) { if (existsUser02 == null) {
logger.info("Common user02 not found, creating...") log.info("Common user02 not found, creating...")
insertUser02() insertUser02()
}else{ }else{
logger.info("Common user02 already exists, skipping") log.info("Common user02 already exists, skipping")
} }
logger.info("Default user initialization completed") log.info("Default user initialization completed")
} }
fun insertAdmin() { fun insertAdmin() {
@@ -63,9 +61,9 @@ class InitUserRunner(
password = passwordEncoder.encode("admin")!!, password = passwordEncoder.encode("admin")!!,
role = "admin" role = "admin"
) )
logger.debug("Creating admin user: username={}, role={}", user.username, user.role) log.debug("Creating admin user: username=${user.username}, role=${user.role}")
userMapper.insert(user) userMapper.insert(user)
logger.info("Admin user created successfully") log.info("Admin user created successfully")
} }
fun insertUser01() { fun insertUser01() {
@@ -75,9 +73,9 @@ class InitUserRunner(
password = passwordEncoder.encode("user01")!!, password = passwordEncoder.encode("user01")!!,
role = "user" role = "user"
) )
logger.debug("Creating common user: username={}, role={}", user.username, user.role) log.debug("Creating common user: username=${user.username}, role=${user.role}")
userMapper.insert(user) userMapper.insert(user)
logger.info("Common user created successfully") log.info("Common user created successfully")
} }
fun insertUser02() { fun insertUser02() {
val user = User( val user = User(
@@ -86,8 +84,8 @@ class InitUserRunner(
password = passwordEncoder.encode("user02")!!, password = passwordEncoder.encode("user02")!!,
role = "user" role = "user"
) )
logger.debug("Creating common user: username={}, role={}", user.username, user.role) log.debug("Creating common user: username=${user.username}, role=${user.role}")
userMapper.insert(user) userMapper.insert(user)
logger.info("Common user created successfully") log.info("Common user created successfully")
} }
} }
@@ -0,0 +1,7 @@
package com.msksbr.bookmgr.script
import org.slf4j.Logger
import org.slf4j.LoggerFactory
val <T: Any> T.log: Logger
get() = LoggerFactory.getLogger(this::class.java)
@@ -1,12 +1,29 @@
package com.msksbr.bookmgr.service.impl package com.msksbr.bookmgr.service.impl
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.msksbr.bookmgr.dto.UserLoginDTO import com.msksbr.bookmgr.dto.UserLoginDTO
import com.msksbr.bookmgr.entity.User
import com.msksbr.bookmgr.mapper.UserMapper
import com.msksbr.bookmgr.script.log
import com.msksbr.bookmgr.service.AuthService import com.msksbr.bookmgr.service.AuthService
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@Service @Service
class AuthServiceImpl: AuthService { class AuthServiceImpl(private val userMapper: UserMapper, private val passwordEncoder: PasswordEncoder) : AuthService {
override fun login(loginDTO: UserLoginDTO): Boolean { override fun login(loginDTO: UserLoginDTO): Boolean {
TODO("Not yet implemented") // 数据查询
log.debug("Select user{username=${loginDTO.username}} from database")
val user = userMapper.selectOne(
QueryWrapper<User>()
.eq("username", loginDTO.username)
)
// 找不到用户时直接返回false
if (user == null) {
log.debug("User ${loginDTO.username} does not exist")
return false
}
// 比对密码
return passwordEncoder.matches(loginDTO.password, user.password)
} }
} }