Support login with email
This commit is contained in:
@@ -7,7 +7,6 @@ import org.springframework.web.bind.annotation.GetMapping
|
|||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RequestBody
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
import top.fatweb.api.annotation.BaseController
|
import top.fatweb.api.annotation.BaseController
|
||||||
import top.fatweb.api.converter.permission.UserConverter
|
|
||||||
import top.fatweb.api.entity.common.ResponseCode
|
import top.fatweb.api.entity.common.ResponseCode
|
||||||
import top.fatweb.api.entity.common.ResponseResult
|
import top.fatweb.api.entity.common.ResponseResult
|
||||||
import top.fatweb.api.param.permission.LoginParam
|
import top.fatweb.api.param.permission.LoginParam
|
||||||
@@ -46,7 +45,7 @@ class AuthenticationController(
|
|||||||
ResponseResult.success(
|
ResponseResult.success(
|
||||||
ResponseCode.PERMISSION_LOGIN_SUCCESS,
|
ResponseCode.PERMISSION_LOGIN_SUCCESS,
|
||||||
"Login success",
|
"Login success",
|
||||||
authenticationService.login(request, UserConverter.loginParamToUser(loginParam))
|
authenticationService.login(request, loginParam)
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import top.fatweb.api.entity.permission.Group
|
|||||||
import top.fatweb.api.entity.permission.Role
|
import top.fatweb.api.entity.permission.Role
|
||||||
import top.fatweb.api.entity.permission.User
|
import top.fatweb.api.entity.permission.User
|
||||||
import top.fatweb.api.entity.permission.UserInfo
|
import top.fatweb.api.entity.permission.UserInfo
|
||||||
import top.fatweb.api.param.permission.LoginParam
|
|
||||||
import top.fatweb.api.param.permission.user.UserAddParam
|
import top.fatweb.api.param.permission.user.UserAddParam
|
||||||
import top.fatweb.api.param.permission.user.UserUpdateParam
|
import top.fatweb.api.param.permission.user.UserUpdateParam
|
||||||
import top.fatweb.api.vo.PageVo
|
import top.fatweb.api.vo.PageVo
|
||||||
@@ -22,21 +21,6 @@ import top.fatweb.avatargenerator.GitHubAvatar
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
object UserConverter {
|
object UserConverter {
|
||||||
/**
|
|
||||||
* Convert LoginParam object into User object
|
|
||||||
*
|
|
||||||
* @param loginParam LoginParam object
|
|
||||||
* @return User object
|
|
||||||
* @author FatttSnake, fatttsnake@gmail.com
|
|
||||||
* @since 1.0.0
|
|
||||||
* @see LoginParam
|
|
||||||
* @see User
|
|
||||||
*/
|
|
||||||
fun loginParamToUser(loginParam: LoginParam) = User().apply {
|
|
||||||
username = loginParam.username
|
|
||||||
password = loginParam.password
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert User object into UserWithPowerInfoVo object
|
* Convert User object into UserWithPowerInfoVo object
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ import top.fatweb.api.entity.permission.User
|
|||||||
@Mapper
|
@Mapper
|
||||||
interface UserMapper : BaseMapper<User> {
|
interface UserMapper : BaseMapper<User> {
|
||||||
/**
|
/**
|
||||||
* Select one user with power and information by username
|
* Select one user with power and information by username or email
|
||||||
*
|
*
|
||||||
* @param username Username
|
* @param account Username or email
|
||||||
* @return User object with power and information
|
* @return User object with power and information
|
||||||
* @author FatttSnake, fatttsnake@gmail.com
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
* @see User
|
* @see User
|
||||||
*/
|
*/
|
||||||
fun selectOneWithPowerInfoByUsername(@Param("username") username: String): User?
|
fun selectOneWithPowerInfoByAccount(@Param("account") account: String): User?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select user in page
|
* Select user in page
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ import jakarta.validation.constraints.NotBlank
|
|||||||
@Schema(description = "登录请求参数")
|
@Schema(description = "登录请求参数")
|
||||||
data class LoginParam(
|
data class LoginParam(
|
||||||
/**
|
/**
|
||||||
* Username
|
* Account
|
||||||
*
|
*
|
||||||
* @author FatttSnake, fatttsnake@gmail.com
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Schema(description = "用户名", example = "test", required = true)
|
@Schema(description = "账户", example = "test", required = true)
|
||||||
@field:NotBlank(message = "Username can not be blank")
|
@field:NotBlank(message = "Account can not be blank")
|
||||||
val username: String? = null,
|
val account: String? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Password
|
* Password
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package top.fatweb.api.param.permission.user
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
import jakarta.validation.constraints.NotBlank
|
import jakarta.validation.constraints.NotBlank
|
||||||
|
import jakarta.validation.constraints.Pattern
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,6 +95,7 @@ data class UserAddParam(
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Schema(description = "邮箱")
|
@Schema(description = "邮箱")
|
||||||
|
@Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\$", message = "Illegal email address")
|
||||||
val email: String?,
|
val email: String?,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package top.fatweb.api.param.permission.user
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
import jakarta.validation.constraints.NotNull
|
import jakarta.validation.constraints.NotNull
|
||||||
|
import jakarta.validation.constraints.Pattern
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,6 +95,7 @@ data class UserUpdateParam(
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Schema(description = "邮箱")
|
@Schema(description = "邮箱")
|
||||||
|
@Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\$", message = "Illegal email address")
|
||||||
val email: String?,
|
val email: String?,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package top.fatweb.api.service.permission
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
import top.fatweb.api.entity.permission.User
|
import top.fatweb.api.entity.permission.User
|
||||||
|
import top.fatweb.api.param.permission.LoginParam
|
||||||
import top.fatweb.api.vo.permission.LoginVo
|
import top.fatweb.api.vo.permission.LoginVo
|
||||||
import top.fatweb.api.vo.permission.TokenVo
|
import top.fatweb.api.vo.permission.TokenVo
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ interface IAuthenticationService {
|
|||||||
* Login
|
* Login
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
* @param user User object
|
* @param loginParam Login parameters
|
||||||
* @return LoginVo object
|
* @return LoginVo object
|
||||||
* @author FatttSnake, fatttsnake@gmail.com
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
@@ -24,7 +25,7 @@ interface IAuthenticationService {
|
|||||||
* @see User
|
* @see User
|
||||||
* @see LoginVo
|
* @see LoginVo
|
||||||
*/
|
*/
|
||||||
fun login(request: HttpServletRequest, user: User): LoginVo
|
fun login(request: HttpServletRequest, loginParam: LoginParam): LoginVo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logout
|
* Logout
|
||||||
|
|||||||
@@ -19,15 +19,15 @@ import top.fatweb.api.vo.permission.UserWithRoleInfoVo
|
|||||||
*/
|
*/
|
||||||
interface IUserService : IService<User> {
|
interface IUserService : IService<User> {
|
||||||
/**
|
/**
|
||||||
* Get user with power by username
|
* Get user with power by username or email
|
||||||
*
|
*
|
||||||
* @param username Username
|
* @param account Username or email
|
||||||
* @return User object
|
* @return User object
|
||||||
* @author FatttSnake, fatttsnake@gmail.com
|
* @author FatttSnake, fatttsnake@gmail.com
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
* @see User
|
* @see User
|
||||||
*/
|
*/
|
||||||
fun getUserWithPowerByUsername(username: String): User?
|
fun getUserWithPowerByAccount(account: String): User?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user information
|
* Get user information
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import top.fatweb.api.entity.permission.LoginUser
|
|||||||
import top.fatweb.api.entity.permission.User
|
import top.fatweb.api.entity.permission.User
|
||||||
import top.fatweb.api.entity.system.EventLog
|
import top.fatweb.api.entity.system.EventLog
|
||||||
import top.fatweb.api.exception.TokenHasExpiredException
|
import top.fatweb.api.exception.TokenHasExpiredException
|
||||||
|
import top.fatweb.api.param.permission.LoginParam
|
||||||
import top.fatweb.api.properties.SecurityProperties
|
import top.fatweb.api.properties.SecurityProperties
|
||||||
import top.fatweb.api.service.permission.IAuthenticationService
|
import top.fatweb.api.service.permission.IAuthenticationService
|
||||||
import top.fatweb.api.service.permission.IUserService
|
import top.fatweb.api.service.permission.IUserService
|
||||||
@@ -42,8 +43,9 @@ class AuthenticationServiceImpl(
|
|||||||
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
|
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
@EventLogRecord(EventLog.Event.LOGIN)
|
@EventLogRecord(EventLog.Event.LOGIN)
|
||||||
override fun login(request: HttpServletRequest, user: User): LoginVo {
|
override fun login(request: HttpServletRequest, loginParam: LoginParam): LoginVo {
|
||||||
val usernamePasswordAuthenticationToken = UsernamePasswordAuthenticationToken(user.username, user.password)
|
val usernamePasswordAuthenticationToken =
|
||||||
|
UsernamePasswordAuthenticationToken(loginParam.account, loginParam.password)
|
||||||
val authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken)
|
val authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken)
|
||||||
authentication ?: let {
|
authentication ?: let {
|
||||||
throw RuntimeException("Login failed")
|
throw RuntimeException("Login failed")
|
||||||
@@ -52,13 +54,13 @@ class AuthenticationServiceImpl(
|
|||||||
val loginUser = authentication.principal as LoginUser
|
val loginUser = authentication.principal as LoginUser
|
||||||
loginUser.user.password = ""
|
loginUser.user.password = ""
|
||||||
|
|
||||||
logger.info("用户登录 [用户名: '{}', IP: '{}']", user.username, request.remoteAddr)
|
logger.info("用户登录 [用户名: '{}', IP: '{}']", loginUser.username, request.remoteAddr)
|
||||||
userService.update(User().apply {
|
userService.update(User().apply {
|
||||||
currentLoginIp = request.remoteAddr
|
currentLoginIp = request.remoteAddr
|
||||||
currentLoginTime = LocalDateTime.now(ZoneOffset.UTC)
|
currentLoginTime = LocalDateTime.now(ZoneOffset.UTC)
|
||||||
lastLoginIp = loginUser.user.currentLoginIp
|
lastLoginIp = loginUser.user.currentLoginIp
|
||||||
lastLoginTime = loginUser.user.currentLoginTime
|
lastLoginTime = loginUser.user.currentLoginTime
|
||||||
}, KtUpdateWrapper(User()).eq(User::username, user.username))
|
}, KtUpdateWrapper(User()).eq(User::username, loginUser.username))
|
||||||
|
|
||||||
val userId = loginUser.user.id.toString()
|
val userId = loginUser.user.id.toString()
|
||||||
val jwt = JwtUtil.createJwt(userId)
|
val jwt = JwtUtil.createJwt(userId)
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import top.fatweb.api.service.permission.IUserService
|
|||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
class UserDetailsServiceImpl(val userService: IUserService) : UserDetailsService {
|
class UserDetailsServiceImpl(val userService: IUserService) : UserDetailsService {
|
||||||
override fun loadUserByUsername(username: String): UserDetails {
|
override fun loadUserByUsername(account: String): UserDetails {
|
||||||
val user = userService.getUserWithPowerByUsername(username)
|
val user = userService.getUserWithPowerByAccount(account)
|
||||||
user ?: let { throw Exception("Username not found") }
|
user ?: let { throw Exception("Username not found") }
|
||||||
|
|
||||||
return LoginUser(user)
|
return LoginUser(user)
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ class UserServiceImpl(
|
|||||||
private val userRoleService: IUserRoleService,
|
private val userRoleService: IUserRoleService,
|
||||||
private val userGroupService: IUserGroupService
|
private val userGroupService: IUserGroupService
|
||||||
) : ServiceImpl<UserMapper, User>(), IUserService {
|
) : ServiceImpl<UserMapper, User>(), IUserService {
|
||||||
override fun getUserWithPowerByUsername(username: String): User? {
|
override fun getUserWithPowerByAccount(account: String): User? {
|
||||||
val user = baseMapper.selectOneWithPowerInfoByUsername(username)
|
val user = baseMapper.selectOneWithPowerInfoByAccount(account)
|
||||||
user ?: let { return null }
|
user ?: let { return null }
|
||||||
|
|
||||||
if (user.id == 0L) {
|
if (user.id == 0L) {
|
||||||
@@ -76,7 +76,7 @@ class UserServiceImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getInfo() = WebUtil.getLoginUsername()
|
override fun getInfo() = WebUtil.getLoginUsername()
|
||||||
?.let { username -> getUserWithPowerByUsername(username)?.let { UserConverter.userToUserWithPowerInfoVo(it) } }
|
?.let { username -> getUserWithPowerByAccount(username)?.let { UserConverter.userToUserWithPowerInfoVo(it) } }
|
||||||
|
|
||||||
override fun getPage(userGetParam: UserGetParam?): PageVo<UserWithRoleInfoVo> {
|
override fun getPage(userGetParam: UserGetParam?): PageVo<UserWithRoleInfoVo> {
|
||||||
val userIdsPage = Page<Long>(userGetParam?.currentPage ?: 1, userGetParam?.pageSize ?: 20)
|
val userIdsPage = Page<Long>(userGetParam?.currentPage ?: 1, userGetParam?.pageSize ?: 20)
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ create table if not exists t_user_info
|
|||||||
user_id bigint not null comment '用户ID',
|
user_id bigint not null comment '用户ID',
|
||||||
nickname varchar(50) null comment '昵称',
|
nickname varchar(50) null comment '昵称',
|
||||||
avatar text null comment '头像',
|
avatar text null comment '头像',
|
||||||
email varchar(100) null comment '邮箱',
|
email varchar(100) not null comment '邮箱',
|
||||||
create_time datetime not null default (utc_timestamp()) comment '创建时间',
|
create_time datetime not null default (utc_timestamp()) comment '创建时间',
|
||||||
update_time datetime not null default (utc_timestamp()) comment '修改时间',
|
update_time datetime not null default (utc_timestamp()) comment '修改时间',
|
||||||
deleted bigint not null default 0,
|
deleted bigint not null default 0,
|
||||||
version int not null default 0,
|
version int not null default 0,
|
||||||
constraint t_user_info_unique unique (user_id, deleted)
|
constraint t_user_info_unique_user_id unique (user_id, deleted),
|
||||||
|
constraint t_user_info_unique_email unique (email, deleted)
|
||||||
) comment '用户资料表';
|
) comment '用户资料表';
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="top.fatweb.api.mapper.permission.UserMapper">
|
<mapper namespace="top.fatweb.api.mapper.permission.UserMapper">
|
||||||
<select id="selectOneWithPowerInfoByUsername" resultMap="userWithPowerInfoMap">
|
<select id="selectOneWithPowerInfoByAccount" resultMap="userWithPowerInfoMap">
|
||||||
select t_user.id as user_id,
|
select t_user.id as user_id,
|
||||||
t_user.username as user_username,
|
t_user.username as user_username,
|
||||||
t_user.password as user_password,
|
t_user.password as user_password,
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
left join t_func as tf on tp.id = tf.id
|
left join t_func as tf on tp.id = tf.id
|
||||||
left join t_operation as t on tp.id = t.id
|
left join t_operation as t on tp.id = t.id
|
||||||
where t_user.deleted = 0
|
where t_user.deleted = 0
|
||||||
and t_user.username = #{username};
|
and (tui.email = #{account} or t_user.username = #{account});
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectPage" resultType="long">
|
<select id="selectPage" resultType="long">
|
||||||
|
|||||||
Reference in New Issue
Block a user