Add login IP record

This commit is contained in:
2023-10-16 17:35:34 +08:00
parent 1fcd468581
commit 8e3ceb16c1
7 changed files with 110 additions and 22 deletions

View File

@@ -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)

View File

@@ -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 = "登出")

View File

@@ -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
}

View File

@@ -2,10 +2,11 @@ package top.fatweb.api.entity.permission
import com.baomidou.mybatisplus.annotation.*
import java.io.Serializable
import java.time.LocalDateTime
/**
* <p>
* 用户
* 用户
* </p>
*
* @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)"
}
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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"))
}
*/
}