From 8e3ceb16c1524addc7d2ad3003b4ce583a5f2085 Mon Sep 17 00:00:00 2001 From: FatttSnake Date: Mon, 16 Oct 2023 17:35:34 +0800 Subject: [PATCH] Add login IP record --- db/schema.sql | 23 ++++++--- .../permission/AuthenticationController.kt | 9 ++-- .../fatweb/api/entity/permission/LoginUser.kt | 16 +++--- .../top/fatweb/api/entity/permission/User.kt | 50 +++++++++++++++++-- .../permission/IAuthenticationService.kt | 3 +- .../impl/AuthenticationServiceImpl.kt | 20 +++++++- .../fatweb/api/FatWebApiApplicationTests.kt | 11 ++++ 7 files changed, 110 insertions(+), 22 deletions(-) diff --git a/db/schema.sql b/db/schema.sql index a1140fd..f75257c 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -2,11 +2,20 @@ drop table if exists t_user; 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 '密码', - enable int not null comment '启用', - deleted bigint not null default 0, - version int not null default 0, + id bigint not null primary key, + username varchar(20) not null comment '用户名', + password char(70) not null comment '密码', + locking int not null comment '锁定', + expiration datetime comment '过期时间', + credentials_expiration datetime comment '认证过期时间', + enable int not null comment '启用', + last_login_time datetime comment '上次登录时间', + last_login_ip varchar(128) comment '上次登录 IP', + 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, constraint t_user_unique unique (username, deleted) -) comment '用户'; +) comment '用户表'; + +insert into t_user (id, username, password, locking, enable) value (0, 'admin', '$2a$10$3wDGdzTZlC..7eY6u2XM5u78xUQo0z5Sj5yOpneD4QJ0q/TA5TY0S', 0, 1) \ No newline at end of file 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 2dd6960..2f04f9e 100644 --- a/src/main/kotlin/top/fatweb/api/controller/permission/AuthenticationController.kt +++ b/src/main/kotlin/top/fatweb/api/controller/permission/AuthenticationController.kt @@ -4,7 +4,10 @@ import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag import jakarta.servlet.http.HttpServletRequest import jakarta.validation.Valid -import org.springframework.web.bind.annotation.* +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.annotation.ApiVersion import top.fatweb.api.converter.UserConverter import top.fatweb.api.entity.common.ResponseCode @@ -19,11 +22,11 @@ import top.fatweb.api.util.WebUtil class AuthenticationController(val authenticationService: IAuthenticationService, val userConverter: UserConverter) { @Operation(summary = "登录") @PostMapping("/login") - fun login(@Valid @RequestBody loginParam: LoginParam) = + fun login(request: HttpServletRequest, @Valid @RequestBody loginParam: LoginParam) = ResponseResult.success( ResponseCode.SYSTEM_LOGIN_SUCCESS, "Login success", - authenticationService.login(userConverter.loginParamToUser(loginParam)) + authenticationService.login(request, userConverter.loginParamToUser(loginParam)) ) @Operation(summary = "登出") diff --git a/src/main/kotlin/top/fatweb/api/entity/permission/LoginUser.kt b/src/main/kotlin/top/fatweb/api/entity/permission/LoginUser.kt index 951d1e2..84837d7 100644 --- a/src/main/kotlin/top/fatweb/api/entity/permission/LoginUser.kt +++ b/src/main/kotlin/top/fatweb/api/entity/permission/LoginUser.kt @@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonTypeInfo import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.userdetails.UserDetails +import java.time.LocalDateTime +import java.time.ZoneOffset @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) class LoginUser() : UserDetails { @@ -25,20 +27,22 @@ class LoginUser() : UserDetails { } @JsonIgnore - override fun getPassword(): String? = user.password + override fun getPassword() = user.password @JsonIgnore - override fun getUsername(): String? = user.username + override fun getUsername() = user.username @JsonIgnore - override fun isAccountNonExpired(): Boolean = true + override fun isAccountNonExpired() = + user.expiration == null || user.expiration!!.isAfter(LocalDateTime.now(ZoneOffset.UTC)) @JsonIgnore - override fun isAccountNonLocked(): Boolean = true + override fun isAccountNonLocked() = user.locking == 0 @JsonIgnore - override fun isCredentialsNonExpired(): Boolean = true + override fun isCredentialsNonExpired() = + user.credentialsExpiration == null || user.credentialsExpiration!!.isAfter(LocalDateTime.now(ZoneOffset.UTC)) @JsonIgnore - override fun isEnabled(): Boolean = user.enable == 1 + override fun isEnabled() = user.enable == 1 } \ 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 bff63ab..8277f73 100644 --- a/src/main/kotlin/top/fatweb/api/entity/permission/User.kt +++ b/src/main/kotlin/top/fatweb/api/entity/permission/User.kt @@ -2,10 +2,11 @@ package top.fatweb.api.entity.permission import com.baomidou.mybatisplus.annotation.* import java.io.Serializable +import java.time.LocalDateTime /** *

- * 用户 + * 用户表 *

* * @author FatttSnake @@ -13,7 +14,8 @@ import java.io.Serializable */ @TableName("t_user") class User() : Serializable { - constructor(username: String, password: String, enable: Boolean = true) : this() { + constructor(id: Long?, username: String, password: String, enable: Boolean = true) : this() { + this.id = id this.username = username this.password = password this.enable = if (enable) 1 else 0 @@ -34,12 +36,54 @@ class User() : Serializable { @TableField("password") var password: String? = null + /** + * 锁定 + */ + @TableField("locking") + var locking: Int? = null + + /** + * 过期时间 + */ + @TableField("expiration") + var expiration: LocalDateTime? = null + + /** + * 认证过期时间 + */ + @TableField("credentials_expiration") + var credentialsExpiration: LocalDateTime? = null + /** * 启用 */ @TableField("enable") var enable: Int? = null + /** + * 上次登录时间 + */ + @TableField("last_login_time") + var lastLoginTime: LocalDateTime? = null + + /** + * 上次登录 IP + */ + @TableField("last_login_ip") + var lastLoginIp: String? = null + + /** + * 创建时间 + */ + @TableField("create_time") + var createTime: LocalDateTime? = null + + /** + * 修改时间 + */ + @TableField("update_time") + var updateTime: LocalDateTime? = null + @TableField("deleted") @TableLogic var deleted: Long? = null @@ -49,6 +93,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, locking=$locking, expiration=$expiration, credentialsExpiration=$credentialsExpiration, enable=$enable, lastLoginTime=$lastLoginTime, lastLoginIp=$lastLoginIp, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version)" } } diff --git a/src/main/kotlin/top/fatweb/api/service/permission/IAuthenticationService.kt b/src/main/kotlin/top/fatweb/api/service/permission/IAuthenticationService.kt index 28becfc..5704e7d 100644 --- a/src/main/kotlin/top/fatweb/api/service/permission/IAuthenticationService.kt +++ b/src/main/kotlin/top/fatweb/api/service/permission/IAuthenticationService.kt @@ -1,11 +1,12 @@ package top.fatweb.api.service.permission +import jakarta.servlet.http.HttpServletRequest import top.fatweb.api.entity.permission.User import top.fatweb.api.vo.LoginVo import top.fatweb.api.vo.TokenVo interface IAuthenticationService { - fun login(user: User): LoginVo + fun login(request: HttpServletRequest, user: User): LoginVo fun logout(token: String): Boolean diff --git a/src/main/kotlin/top/fatweb/api/service/permission/impl/AuthenticationServiceImpl.kt b/src/main/kotlin/top/fatweb/api/service/permission/impl/AuthenticationServiceImpl.kt index d08bc10..6a173e3 100644 --- a/src/main/kotlin/top/fatweb/api/service/permission/impl/AuthenticationServiceImpl.kt +++ b/src/main/kotlin/top/fatweb/api/service/permission/impl/AuthenticationServiceImpl.kt @@ -1,30 +1,46 @@ package top.fatweb.api.service.permission.impl +import com.baomidou.mybatisplus.extension.kotlin.KtUpdateWrapper +import jakarta.servlet.http.HttpServletRequest +import org.slf4j.Logger +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.service.IUserService import top.fatweb.api.service.permission.IAuthenticationService import top.fatweb.api.util.JwtUtil import top.fatweb.api.util.RedisUtil import top.fatweb.api.util.WebUtil import top.fatweb.api.vo.LoginVo import top.fatweb.api.vo.TokenVo +import java.time.LocalDateTime +import java.time.ZoneOffset @Service class AuthenticationServiceImpl( private val authenticationManager: AuthenticationManager, - private val redisUtil: RedisUtil + private val redisUtil: RedisUtil, + private val userService: IUserService ) : IAuthenticationService { - override fun login(user: User): LoginVo { + private val logger: Logger = LoggerFactory.getLogger(this::class.java) + + override fun login(request: HttpServletRequest, user: User): LoginVo { val usernamePasswordAuthenticationToken = UsernamePasswordAuthenticationToken(user.username, user.password) val authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken) authentication ?: let { throw RuntimeException("Login failed") } + logger.info("用户登录 [用户名: '{}', IP: '{}']", user.username, request.remoteAddr) + userService.update(User().apply { + lastLoginIp = request.remoteAddr + lastLoginTime = LocalDateTime.now(ZoneOffset.UTC) + }, KtUpdateWrapper(User()).eq(User::username, user.username)) + val loginUser = authentication.principal as LoginUser loginUser.user.password = "" val userId = loginUser.user.id.toString() diff --git a/src/test/kotlin/top/fatweb/api/FatWebApiApplicationTests.kt b/src/test/kotlin/top/fatweb/api/FatWebApiApplicationTests.kt index 5ed443f..c73504e 100644 --- a/src/test/kotlin/top/fatweb/api/FatWebApiApplicationTests.kt +++ b/src/test/kotlin/top/fatweb/api/FatWebApiApplicationTests.kt @@ -3,12 +3,15 @@ package top.fatweb.api import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test 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.util.JwtUtil @ExtendWith(SpringExtension::class) class FatWebApiApplicationTests { + private val logger: Logger = LoggerFactory.getLogger(this::class.java) @Test fun removePrefixTest() { @@ -20,4 +23,12 @@ class FatWebApiApplicationTests { val jwt = JwtUtil.createJwt("User") assertEquals("User", jwt?.let { JwtUtil.parseJwt(it).subject }) } + + /* + @Test + fun generatePassword() { + val passwordEncoder = BCryptPasswordEncoder() + logger.info(passwordEncoder.encode("admin@dev")) + } + */ }