添加登录组件

This commit is contained in:
筱锋xiao_lfeng 2023-11-29 23:26:15 +08:00
parent 86006196c8
commit b6db17290b
No known key found for this signature in database
GPG Key ID: 3D44FF873E0CD0E3
11 changed files with 200 additions and 16 deletions

View File

@ -26,6 +26,11 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId> <artifactId>spring-boot-starter-aop</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.4</version>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.module</groupId> <groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId> <artifactId>jackson-module-kotlin</artifactId>

View File

@ -9,7 +9,7 @@ import jakarta.servlet.http.HttpServletResponse
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.CookieValue import org.springframework.web.bind.annotation.CookieValue
import org.springframework.web.bind.annotation.CrossOrigin import org.springframework.web.bind.annotation.CrossOrigin
import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
@ -23,7 +23,7 @@ class TokenController(
/** /**
* 创建 Token 组件 * 创建 Token 组件
*/ */
@PostMapping("/create") @GetMapping("/create")
fun createToken( fun createToken(
@CookieValue("session") token: String?, httpServletResponse: HttpServletResponse, @CookieValue("session") token: String?, httpServletResponse: HttpServletResponse,
@RequestParam("return") returnLink: String?, @RequestParam("return") returnLink: String?,

View File

@ -4,15 +4,19 @@ import com.frontleaves.general.utils.BaseResponse
import com.frontleaves.general.utils.ErrorCode import com.frontleaves.general.utils.ErrorCode
import com.frontleaves.general.utils.ResultUtil import com.frontleaves.general.utils.ResultUtil
import com.xlf.dromstarkotlin.entity.voData.SignInVO import com.xlf.dromstarkotlin.entity.voData.SignInVO
import com.xlf.dromstarkotlin.entity.voData.SignUpVO
import com.xlf.dromstarkotlin.exception.BusinessException import com.xlf.dromstarkotlin.exception.BusinessException
import com.xlf.dromstarkotlin.services.TokenService import com.xlf.dromstarkotlin.services.TokenService
import com.xlf.dromstarkotlin.services.UserService
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse import jakarta.servlet.http.HttpServletResponse
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.CookieValue import org.springframework.web.bind.annotation.CookieValue
import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestBody
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 java.util.Date
/** /**
* 用户控制器 * 用户控制器
@ -25,31 +29,72 @@ import org.springframework.web.bind.annotation.RestController
@RestController @RestController
@RequestMapping("/api/user") @RequestMapping("/api/user")
class UserController( class UserController(
private val tokenService: TokenService private val tokenService: TokenService,
private val userService: UserService
) { ) {
/** /**
* 用户登录组件 * 用户登录组件
*/ */
@PostMapping("/sign/in") @GetMapping("/sign/in")
fun signIn( fun signIn(
signInVO: SignInVO?, @CookieValue("sessionId") token: String?, httpServletResponse: HttpServletResponse, @RequestBody signInVO: SignInVO?, @CookieValue("session") token: String?, httpServletResponse: HttpServletResponse,
httpServletRequest: HttpServletRequest httpServletRequest: HttpServletRequest
): ResponseEntity<BaseResponse>? { ): ResponseEntity<BaseResponse> {
// 判断请求体是否为空
if (signInVO == null) { if (signInVO == null) {
return BusinessException().backInfo(ErrorCode.MISSING_REQUEST_BODY, httpServletRequest) return BusinessException().backInfo(ErrorCode.MISSING_REQUEST_BODY, httpServletRequest)
} else { } else {
// 判断时间戳
if (signInVO.timestamp + 5000 < Date().time || signInVO.timestamp - 5000 > Date().time) {
return ResultUtil.error(ErrorCode.TIMESTAMP_EXPIRED, httpServletRequest)
}
// 对 Token 进行校验 // 对 Token 进行校验
if (token != null) { return if (token != null) {
// 对 token 进行校验 // 对 token 进行校验
if (!tokenService.tokenVerify(token, httpServletResponse)) { if (!tokenService.tokenVerify(token, httpServletResponse)) {
// 校验失败 // 校验失败
return BusinessException().backInfo(ErrorCode.TOKEN_VERIFY_FAILED, httpServletRequest) BusinessException().backInfo(ErrorCode.TOKEN_VERIFY_FAILED, httpServletRequest)
} else {
// 用户登录操作
userService.signIn(signInVO, token, httpServletRequest)
} }
} else { } else {
// 跳转至创建 Token 页面 // 跳转至创建 Token 页面
return ResultUtil.redirect("/api/token/create", httpServletRequest) ResultUtil.redirect("/api/token/create", httpServletRequest)
}
}
}
/**
* 用户注册组件
*/
@GetMapping("/sign/up")
fun signUp(
@RequestBody signUpVO: SignUpVO?, @CookieValue("session") token: String?, httpServletResponse: HttpServletResponse,
httpServletRequest: HttpServletRequest
): ResponseEntity<BaseResponse> {
// 判断请求体是否为空
if (signUpVO == null) {
return ResultUtil.error(ErrorCode.MISSING_REQUEST_BODY, httpServletRequest)
} else {
// 判断时间戳
if (signUpVO.timestamp + 5000 < Date().time || signUpVO.timestamp - 5000 > Date().time) {
return ResultUtil.error(ErrorCode.TIMESTAMP_EXPIRED, httpServletRequest)
}
// 对 Token 进行校验
return if (token != null) {
// 对 token 进行校验
if (!tokenService.tokenVerify(token, httpServletResponse)) {
// 校验失败
BusinessException().backInfo(ErrorCode.TOKEN_VERIFY_FAILED, httpServletRequest)
} else {
// 用户登录操作
userService.signUp(signUpVO, token, httpServletRequest)
}
} else {
// 跳转至创建 Token 页面
ResultUtil.redirect("/api/token/create", httpServletRequest)
} }
} }
return null
} }
} }

View File

@ -6,6 +6,8 @@ data class UserDO(
var id: Long?, var id: Long?,
var user: String?, var user: String?,
var password: String?, var password: String?,
var email: String?,
var tel: String?,
var permission: Short?, var permission: Short?,
var createdAt: Timestamp?, var createdAt: Timestamp?,
var updatedAt: Timestamp? var updatedAt: Timestamp?

View File

@ -7,8 +7,8 @@ package com.xlf.dromstarkotlin.entity.voData
* @since v1.0.0 * @since v1.0.0
*/ */
data class SignInVO ( data class SignInVO (
var action: String? = null, val action: String,
var username: String? = null, val username: String,
var password: String? = null, val password: String,
var timestamp: Long? = null, val timestamp: Long,
) )

View File

@ -0,0 +1,16 @@
package com.xlf.dromstarkotlin.entity.voData
/**
* 注册用户信息自定义实体类
*
* @author 筱锋xiao_lfeng
* @since v1.0.0
*/
data class SignUpVO (
val action: String,
val username: String,
val password: String,
val email: String,
val telephone: String?,
val timestamp: Long,
)

View File

@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Delete
import org.apache.ibatis.annotations.Insert import org.apache.ibatis.annotations.Insert
import org.apache.ibatis.annotations.Mapper import org.apache.ibatis.annotations.Mapper
import org.apache.ibatis.annotations.Select import org.apache.ibatis.annotations.Select
import org.apache.ibatis.annotations.Update
@Mapper @Mapper
interface TokenMapper { interface TokenMapper {
@ -19,4 +20,7 @@ interface TokenMapper {
@Insert("INSERT INTO dormstar.ds_token (user_id, token, user_agent, ip, created_at) VALUES (#{userId}, #{token}, #{userAgent}, #{ip}, #{createdAt})") @Insert("INSERT INTO dormstar.ds_token (user_id, token, user_agent, ip, created_at) VALUES (#{userId}, #{token}, #{userAgent}, #{ip}, #{createdAt})")
fun insertToken(token: TokenDO): Boolean fun insertToken(token: TokenDO): Boolean
@Update("UPDATE dormstar.ds_token SET user_id = #{userId}, updated_at = #{updatedAt} WHERE token = #{token}")
fun tokenAuthorization(token: TokenDO): Boolean
} }

View File

@ -1,4 +1,19 @@
package com.xlf.dromstarkotlin.mapper package com.xlf.dromstarkotlin.mapper
import com.xlf.dromstarkotlin.entity.doData.UserDO
import org.apache.ibatis.annotations.Insert
import org.apache.ibatis.annotations.Mapper
import org.apache.ibatis.annotations.Select
@Mapper
interface UserMapper { interface UserMapper {
@Select("SELECT * FROM dormstar.ds_user WHERE user = #{username}")
fun getUserByUsername(username: String?): UserDO?
@Select("SELECT * FROM dormstar.ds_user WHERE user = #{username} OR email = #{email} OR tel = #{phone} LIMIT 1")
fun getUser(username: String, email: String, phone: String?): UserDO?
@Insert("INSERT INTO dormstar.ds_user (user, password, email, tel, created_at) VALUES (#{user}, #{password}, #{email}, #{tel}, #{createdAt})")
fun insertUser(user: UserDO): Boolean
} }

View File

@ -0,0 +1,84 @@
package com.xlf.dromstarkotlin.services
import com.frontleaves.general.utils.BaseResponse
import com.frontleaves.general.utils.ErrorCode
import com.frontleaves.general.utils.ResultUtil
import com.xlf.dromstarkotlin.entity.doData.UserDO
import com.xlf.dromstarkotlin.entity.voData.SignInVO
import com.xlf.dromstarkotlin.entity.voData.SignUpVO
import com.xlf.dromstarkotlin.mapper.TokenMapper
import com.xlf.dromstarkotlin.mapper.UserMapper
import jakarta.servlet.http.HttpServletRequest
import org.mindrot.jbcrypt.BCrypt
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.sql.Timestamp
import java.util.*
@Service
class UserService(
val userMapper: UserMapper,
val tokenMapper: TokenMapper
) {
@Transactional
fun signIn(
signInVO: SignInVO, token:String, httpServletRequest: HttpServletRequest
): ResponseEntity<BaseResponse> {
// 检查用户是否存在
val user = userMapper.getUserByUsername(signInVO.username)
return if (user != null) {
// 检查用户密码是否匹配
if (BCrypt.checkpw(signInVO.password, user.password)) {
val tokenDO = tokenMapper.getToken(token)
.also { it!!.userId = user.id }
if (tokenDO!!.userId == null) {
// 授权 token
if (tokenMapper.tokenAuthorization(tokenDO)) {
ResultUtil.success("登陆成功", httpServletRequest)
} else {
ResultUtil.error(ErrorCode.DATABASE_UPDATE_OPERATION_FAILED, httpServletRequest)
}
} else {
ResultUtil.error(ErrorCode.YOU_ARE_ALREADY_LOGIN, httpServletRequest)
}
} else {
ResultUtil.error(ErrorCode.WRONG_PASSWORD, httpServletRequest)
}
} else {
ResultUtil.error(ErrorCode.USER_NOT_FOUNDED, httpServletRequest)
}
}
@Transactional
fun signUp(
signUpVO: SignUpVO, token: String, httpServletRequest: HttpServletRequest
): ResponseEntity<BaseResponse> {
// 检查用户和邮箱是否存在
val user = userMapper.getUser(signUpVO.username, signUpVO.email, signUpVO.telephone)
if (user == null) {
val tokenDO = tokenMapper.getToken(token)
if (tokenDO!!.userId == null) {
// 处理用户注册程序
val hashPassword = BCrypt.hashpw(signUpVO.password, BCrypt.gensalt())
val newUser = UserDO(null, signUpVO.username, hashPassword, signUpVO.email, signUpVO.telephone, 0, Timestamp(Date().time), null)
// 数据输入数据库
return if (userMapper.insertUser(newUser)) {
// 授权 token
if (tokenMapper.tokenAuthorization(tokenDO)) {
ResultUtil.success("注册成功", httpServletRequest)
} else {
ResultUtil.error(ErrorCode.DATABASE_UPDATE_OPERATION_FAILED, httpServletRequest)
}
} else {
ResultUtil.error(ErrorCode.DATABASE_INSERT_OPERATION_FAILED, httpServletRequest)
}
} else {
return ResultUtil.error(ErrorCode.YOU_ARE_ALREADY_LOGIN, httpServletRequest)
}
} else {
return ResultUtil.error(ErrorCode.USER_EXIST, httpServletRequest)
}
}
}

View File

@ -66,6 +66,12 @@ enum class ErrorCode(val output: String, val code: Int, val message: String, val
"无法生成验证密钥VerifyKey", "无法生成验证密钥VerifyKey",
HttpStatus.BAD_REQUEST HttpStatus.BAD_REQUEST
), ),
YOU_ARE_ALREADY_LOGIN(
"YouAreAlreadyLogin",
40026,
"您已经登录",
HttpStatus.BAD_REQUEST
),
YOU_ARE_NOT_LOGIN( YOU_ARE_NOT_LOGIN(
"YouAreNotLogin", "YouAreNotLogin",
40110, 40110,
@ -120,6 +126,12 @@ enum class ErrorCode(val output: String, val code: Int, val message: String, val
"apikey不正确", "apikey不正确",
HttpStatus.FORBIDDEN HttpStatus.FORBIDDEN
), ),
TIMESTAMP_EXPIRED(
"TimestampExpired",
40318,
"时间戳过期",
HttpStatus.FORBIDDEN
),
METHOD_NOT_ALLOWED( METHOD_NOT_ALLOWED(
"MethodNotAllowed", "MethodNotAllowed",
40510, 40510,

View File

@ -15,7 +15,8 @@ class ResultUtil constructor(val output: String, val code: Int, val message: Str
* *
* @return BaseResponse * @return BaseResponse
*/ */
fun success(): ResponseEntity<BaseResponse> { fun success(httpServletRequest: HttpServletRequest): ResponseEntity<BaseResponse> {
println("${SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date())} [Log] <200>Success | Message:OK | URI:${httpServletRequest.requestURI}")
return ResponseEntity return ResponseEntity
.status(200) .status(200)
.body(BaseResponse()) .body(BaseResponse())