diff --git a/build.gradle.kts b/build.gradle.kts index 97e3d6f..db5fd77 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -44,3 +44,8 @@ kotlin { tasks.withType { useJUnitPlatform() } + +// 打包时排除dev环境配置 +tasks.named("processResources") { + exclude("application-dev.yaml") +} \ No newline at end of file diff --git a/src/main/kotlin/com/msksbr/bookmgr/config/IpExtractor.kt b/src/main/kotlin/com/msksbr/bookmgr/config/IpExtractor.kt new file mode 100644 index 0000000..70219d4 --- /dev/null +++ b/src/main/kotlin/com/msksbr/bookmgr/config/IpExtractor.kt @@ -0,0 +1,25 @@ +package com.msksbr.bookmgr.config + +import jakarta.servlet.http.HttpServletRequest +import org.springframework.stereotype.Component + +//获取真实IP的bean +@Component +class IpExtractor { + fun getRealIp(request: HttpServletRequest): String { + val headers = listOf( + "X-Forwarded-For", + "Proxy-Client-IP", + "WL-Proxy-Client-IP", + "HTTP_X_FORWARDED_FOR", + "X-Real-IP" + ) + return headers + .mapNotNull { request.getHeader(it) } + .firstOrNull { it.isNotBlank() && !it.equals("unknown", ignoreCase = true) } + ?.split(",") + ?.first() + ?.trim() + ?: request.remoteAddr + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt b/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt index 43d2ba0..8f586df 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/controller/AuthController.kt @@ -1,16 +1,17 @@ package com.msksbr.bookmgr.controller +import com.msksbr.bookmgr.config.IpExtractor import com.msksbr.bookmgr.dto.UserLoginDTO import com.msksbr.bookmgr.script.log import com.msksbr.bookmgr.service.AuthService -import jakarta.servlet.http.HttpSession -import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController import com.msksbr.bookmgr.template.Result import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpSession 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 /* * 登录接口 @@ -19,7 +20,8 @@ import org.springframework.web.bind.annotation.RequestBody @RestController @RequestMapping("/api/auth") class AuthController( - val authService: AuthService + val authService: AuthService, + val ipExtractor: IpExtractor ) { @PostMapping("/login") fun login( @@ -28,27 +30,28 @@ class AuthController( loginDTO: UserLoginDTO, session: HttpSession, request: HttpServletRequest - ) : Result { - log.info("Login from ${request.remoteAddr} username: ${loginDTO.username}") + ): Result { + log.info("Login from ${ipExtractor.getRealIp(request)} username: ${loginDTO.username}") log.info("UA: ${request.getHeader("User-Agent")}") // 调用service验证 - val pass=authService.login(loginDTO) + val pass = authService.login(loginDTO) return if (pass) { // 登录成功,存入session session.setAttribute("loginUser", loginDTO.username) log.info("Login success") Result.success("login success") - }else{ + } else { log.info("Login failed") Result.error("username or password not match") } } + @PostMapping("/logout") fun logout( session: HttpSession, - request:HttpServletRequest + request: HttpServletRequest ): Result { - log.info("Logout from ${request.remoteAddr}") + log.info("Logout from ${ipExtractor.getRealIp(request)}") log.info("UA: ${request.getHeader("User-Agent")}") // 直接销毁session session.invalidate() diff --git a/src/main/kotlin/com/msksbr/bookmgr/service/impl/AuthServiceImpl.kt b/src/main/kotlin/com/msksbr/bookmgr/service/impl/AuthServiceImpl.kt index 8adbe8b..9ac5613 100644 --- a/src/main/kotlin/com/msksbr/bookmgr/service/impl/AuthServiceImpl.kt +++ b/src/main/kotlin/com/msksbr/bookmgr/service/impl/AuthServiceImpl.kt @@ -21,6 +21,8 @@ class AuthServiceImpl(private val userMapper: UserMapper, private val passwordEn // 找不到用户时直接返回false if (user == null) { log.debug("User ${loginDTO.username} does not exist") + // 跑一遍dummyHash来对齐响应时间,避免“信息泄露”攻击 + passwordEncoder.encode("dummyHash") return false } // 比对密码 diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index f6af5d2..ce5ac48 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,6 +1,4 @@ spring: - profiles: - active: dev application: name: bookMgr datasource: @@ -17,4 +15,9 @@ spring: mybatis-plus: configuration: # 开启驼峰命名法 - map-underscore-to-camel-case: true \ No newline at end of file + map-underscore-to-camel-case: true + # 开启日志输出sql语句 + # log-impl: org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl +# logging: +# level: +# com.msksbr.bookmgr: "DEBUG" \ No newline at end of file diff --git a/src/test/kotlin/com/msksbr/bookmgr/BookMgrApplicationTests.kt b/src/test/kotlin/com/msksbr/bookmgr/BookMgrApplicationTests.kt deleted file mode 100644 index 9ac6592..0000000 --- a/src/test/kotlin/com/msksbr/bookmgr/BookMgrApplicationTests.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.msksbr.bookmgr - -import org.junit.jupiter.api.Test -import org.springframework.boot.test.context.SpringBootTest - -@SpringBootTest -class BookMgrApplicationTests { - - @Test - fun contextLoads() { - } - -} diff --git a/src/test/kotlin/com/msksbr/bookmgr/mapper/UserMapperTest.kt b/src/test/kotlin/com/msksbr/bookmgr/mapper/UserMapperTest.kt deleted file mode 100644 index 9ee9e6c..0000000 --- a/src/test/kotlin/com/msksbr/bookmgr/mapper/UserMapperTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.msksbr.bookmgr.mapper - -import com.msksbr.bookmgr.entity.User -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNotNull -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.transaction.annotation.Transactional - -@SpringBootTest -@Transactional // 测试完可以自动回滚 -class UserMapperTest { - // Spring自动装配userMapper - @Autowired - private lateinit var userMapper: UserMapper - - @Test - // 新增测试 - fun testInsert() { - // 准备用户 - val user = User(null,"Test", "123456", "admin") - // 插入 - userMapper.insert(user) - // 拿到插入后的自增id - val userId = user.id ?: throw AssertionError("用户插入失败,ID为空") - // 查询插入后的用户 - val res = userMapper.selectById(userId) - // 比对 - assertNotNull(res) - assertEquals(user.username, res.username) - assertEquals(user.password, res.password) - assertEquals(user.role, res.role) - } - // 修改测试 - @Test - fun testUpdate() { - // 先新增 - val user = User(null, "OldName", "123456", "admin") - userMapper.insert(user) - val userId = user.id!! - // 修改字段 - user.username="newName" - user.password="654321" - userMapper.updateById(user) - // 比对 - val res = userMapper.selectById(userId) - assertNotNull(res) - assertEquals(user.username, res.username) - assertEquals(user.password, res.password) - assertEquals(user.role, res.role) - } - // 删除测试 - @Test - fun testDelete() { - // 新增 - val user = User(null,"DelTest", "123456", "admin") - userMapper.insert(user) - val userId = user.id!! - // 删除 - userMapper.deleteById(userId) - // 确认删除 - val res = userMapper.selectById(userId) - assertNull(res) - } -} \ No newline at end of file