From 4d4514091cc42407c1aec13cece49fdfa6d3b86f Mon Sep 17 00:00:00 2001 From: Lyle Liu Date: Sat, 24 Feb 2024 17:58:43 +0800 Subject: [PATCH] register user auto-inject create user and update user --- .../security/account/CreateAccountDto.kt | 4 +- .../security/account/CreateAccountQuery.kt | 2 + .../boson/security/account/domain/Account.kt | 4 +- .../account/service/AccountService.kt | 42 +++++++++++++++---- .../security/auth/CurrentLoginUidUtil.kt | 10 ++--- ...ionToken.kt => OnceAuthenticationToken.kt} | 8 ++-- .../security/auth/service/UserService.kt | 3 +- .../boson/security/web/AuthController.kt | 32 +++++++++++++- 8 files changed, 80 insertions(+), 25 deletions(-) rename src/main/kotlin/top/cgglyle/boson/security/auth/{SystemAuthenticationToken.kt => OnceAuthenticationToken.kt} (75%) diff --git a/src/main/kotlin/top/cgglyle/boson/security/account/CreateAccountDto.kt b/src/main/kotlin/top/cgglyle/boson/security/account/CreateAccountDto.kt index 709ac8f..890d385 100644 --- a/src/main/kotlin/top/cgglyle/boson/security/account/CreateAccountDto.kt +++ b/src/main/kotlin/top/cgglyle/boson/security/account/CreateAccountDto.kt @@ -21,6 +21,7 @@ import jakarta.validation.constraints.NotBlank import org.hibernate.validator.constraints.Length import top.cgglyle.boson.security.authorization.RID import top.cgglyle.boson.security.common.Expiration +import top.cgglyle.boson.security.common.UID import java.io.Serializable /** @@ -34,5 +35,6 @@ data class CreateAccountDto( val roles: Set = mutableSetOf(), val expiration: Expiration = Expiration.NEVER, val locked: Boolean = false, - val enable: Boolean = true + val enable: Boolean = true, + val uid: UID? = null, ) : Serializable \ No newline at end of file diff --git a/src/main/kotlin/top/cgglyle/boson/security/account/CreateAccountQuery.kt b/src/main/kotlin/top/cgglyle/boson/security/account/CreateAccountQuery.kt index dc82a12..b06f7a7 100644 --- a/src/main/kotlin/top/cgglyle/boson/security/account/CreateAccountQuery.kt +++ b/src/main/kotlin/top/cgglyle/boson/security/account/CreateAccountQuery.kt @@ -21,6 +21,7 @@ import jakarta.validation.constraints.Max import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.Size import top.cgglyle.boson.security.common.Expiration +import top.cgglyle.boson.security.common.UID data class CreateAccountQuery( @Max(64) @@ -36,4 +37,5 @@ data class CreateAccountQuery( val locked: Boolean = false, val accountExpiration: Expiration = Expiration.NEVER, val credentialExpiration: Expiration = Expiration.NEVER, + val uid: UID? = null, ) diff --git a/src/main/kotlin/top/cgglyle/boson/security/account/domain/Account.kt b/src/main/kotlin/top/cgglyle/boson/security/account/domain/Account.kt index d1684ef..9e8d4f2 100644 --- a/src/main/kotlin/top/cgglyle/boson/security/account/domain/Account.kt +++ b/src/main/kotlin/top/cgglyle/boson/security/account/domain/Account.kt @@ -31,13 +31,15 @@ import java.util.* @Entity @Table(name = "sys_account") -class Account(command: CreateAccountCommand) : AbstractModifiedAuditingEntity() { +class Account( + command: CreateAccountCommand, @Embedded @AttributeOverride( name = "value", column = Column(name = "uid", updatable = false, nullable = false, unique = true) ) val uid: UID = UID.randomUID() +) : AbstractModifiedAuditingEntity() { @NotBlank @Length(max = 64) diff --git a/src/main/kotlin/top/cgglyle/boson/security/account/service/AccountService.kt b/src/main/kotlin/top/cgglyle/boson/security/account/service/AccountService.kt index e90c214..ee8ae99 100644 --- a/src/main/kotlin/top/cgglyle/boson/security/account/service/AccountService.kt +++ b/src/main/kotlin/top/cgglyle/boson/security/account/service/AccountService.kt @@ -26,6 +26,7 @@ import top.cgglyle.boson.security.account.domain.AccountRepository import top.cgglyle.boson.security.common.UID import top.cgglyle.boson.security.common.UsernameFindable import top.cgglyle.boson.security.exception.DataNotFoundException +import top.cgglyle.boson.security.exception.IllegalArgumentException /** * @author: Lyle Liu @@ -65,7 +66,23 @@ class AccountService( if (existUid(uid)) Unit else throw DataNotFoundException("Uid: $uid not found!") override fun save(accountDto: CreateAccountDto): UID { - val newAccount = Account( + val newAccount = if (accountDto.uid == null) { + createAccount(accountDto) + } else createAccountUseUid(accountDto) + val account = accountRepository.save(newAccount) + return account.uid + } + + override fun delete(uid: UID) { + accountRepository.deleteByUid(uid) + } + + override fun findNameByUid(uid: UID): String? { + return findByUid(uid)?.username + } + + private fun createAccount(accountDto: CreateAccountDto): Account { + return Account( CreateAccountCommand( accountDto.username, accountDto.email, @@ -75,15 +92,22 @@ class AccountService( accountDto.enable ) ) - val account = accountRepository.save(newAccount) - return account.uid } - override fun delete(uid: UID) { - accountRepository.deleteByUid(uid) - } - - override fun findNameByUid(uid: UID): String? { - return findByUid(uid)?.username + private fun createAccountUseUid(accountDto: CreateAccountDto): Account { + if (accountDto.uid == null) { + throw IllegalArgumentException("uid must not be null!") + } + return Account( + CreateAccountCommand( + accountDto.username, + accountDto.email, + accountDto.roles, + accountDto.expiration, + accountDto.locked, + accountDto.enable + ), + accountDto.uid, + ) } } \ No newline at end of file diff --git a/src/main/kotlin/top/cgglyle/boson/security/auth/CurrentLoginUidUtil.kt b/src/main/kotlin/top/cgglyle/boson/security/auth/CurrentLoginUidUtil.kt index 0dbaa1c..a696dc8 100644 --- a/src/main/kotlin/top/cgglyle/boson/security/auth/CurrentLoginUidUtil.kt +++ b/src/main/kotlin/top/cgglyle/boson/security/auth/CurrentLoginUidUtil.kt @@ -51,13 +51,13 @@ object CurrentLoginUidUtil { private fun getAnonymousUid(authentication: Authentication): UID { return when (authentication) { - is SystemAuthenticationToken -> { + is OnceAuthenticationToken -> { val (uid) = authentication.principal as UidDetailUser return uid } is AnonymousAuthenticationToken -> { - UID(authentication.toString()) + UID(authentication.name) } else -> { @@ -70,11 +70,9 @@ object CurrentLoginUidUtil { private fun getUid(uidDetailUser: UidDetailUser) = uidDetailUser.uid - fun newSystemAuthenticationToken(): SystemAuthenticationToken { + fun newSystemAuthenticationToken(): OnceAuthenticationToken { val systemUserDetail = createSystemUserDetail() - return SystemAuthenticationToken( - systemUserDetail.hashCode().toString(), systemUserDetail, systemUserDetail.authorities - ) + return OnceAuthenticationToken(systemUserDetail) } private fun createSystemUserDetail(): UidDetailUser { diff --git a/src/main/kotlin/top/cgglyle/boson/security/auth/SystemAuthenticationToken.kt b/src/main/kotlin/top/cgglyle/boson/security/auth/OnceAuthenticationToken.kt similarity index 75% rename from src/main/kotlin/top/cgglyle/boson/security/auth/SystemAuthenticationToken.kt rename to src/main/kotlin/top/cgglyle/boson/security/auth/OnceAuthenticationToken.kt index 22bb539..cf1544c 100644 --- a/src/main/kotlin/top/cgglyle/boson/security/auth/SystemAuthenticationToken.kt +++ b/src/main/kotlin/top/cgglyle/boson/security/auth/OnceAuthenticationToken.kt @@ -17,9 +17,7 @@ package top.cgglyle.boson.security.auth import org.springframework.security.authentication.AnonymousAuthenticationToken -import org.springframework.security.core.GrantedAuthority -class SystemAuthenticationToken( - key: String, principal: Any, - authorities: MutableCollection? -) : AnonymousAuthenticationToken(key, principal, authorities) +class OnceAuthenticationToken( + userDetails: UidDetailUser, +) : AnonymousAuthenticationToken(userDetails.hashCode().toString(), userDetails, userDetails.authorities) diff --git a/src/main/kotlin/top/cgglyle/boson/security/auth/service/UserService.kt b/src/main/kotlin/top/cgglyle/boson/security/auth/service/UserService.kt index af3c9ea..cf66643 100644 --- a/src/main/kotlin/top/cgglyle/boson/security/auth/service/UserService.kt +++ b/src/main/kotlin/top/cgglyle/boson/security/auth/service/UserService.kt @@ -73,7 +73,8 @@ class UserService( rids, query.accountExpiration, query.locked, - query.enable + query.enable, + query.uid, ) ) diff --git a/src/main/kotlin/top/cgglyle/boson/security/web/AuthController.kt b/src/main/kotlin/top/cgglyle/boson/security/web/AuthController.kt index 626f2e6..a43bf87 100644 --- a/src/main/kotlin/top/cgglyle/boson/security/web/AuthController.kt +++ b/src/main/kotlin/top/cgglyle/boson/security/web/AuthController.kt @@ -1,18 +1,25 @@ package top.cgglyle.boson.security.web import jakarta.validation.Valid +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.http.MediaType import org.springframework.security.core.CredentialsContainer +import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.core.userdetails.User import org.springframework.security.core.userdetails.UserDetails import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.* import top.cgglyle.boson.security.account.CreateAccountQuery import top.cgglyle.boson.security.auth.AuthUserManager +import top.cgglyle.boson.security.auth.OnceAuthenticationToken +import top.cgglyle.boson.security.auth.UidDetailUser import top.cgglyle.boson.security.common.UID import top.cgglyle.boson.security.exception.IllegalArgumentException import top.cgglyle.boson.security.web.query.LoginQuery import top.cgglyle.boson.security.web.query.RegisterQuery +import java.security.Principal @RestController @RequestMapping("/api") @@ -22,11 +29,17 @@ class AuthController( ) { @PostMapping("register") - fun createUser(@Valid @RequestBody query: RegisterQuery): UID { + fun createUser(@Valid @RequestBody query: RegisterQuery, principal: Principal?): UID { if (query.email == null && query.username == null) throw IllegalArgumentException("Both username and password must not be null!") + val uid: UID? = if (principal == null) { + logger.info("[Register User] Current login user is null!") + val randomUID = UID.randomUID() + createOnceAuthenticationToken(query.username!!, randomUID) + randomUID + } else null return authUserManager.createUser( CreateAccountQuery( - query.username, query.email, query.password, setOf() + query.username, query.email, query.password, setOf(), uid = uid, ) ) } @@ -46,4 +59,19 @@ class AuthController( credentialsContainer.eraseCredentials() return userDetails } + + private fun createOnceAuthenticationToken(username: String, uid: UID) { + val userDetails = User.withUsername(username) + .password("") + .authorities(SimpleGrantedAuthority("USER")) + .build() + val uidDetails = UidDetailUser(uid, userDetails) + val onceAuthenticationToken = OnceAuthenticationToken(uidDetails) + val contextHolderStrategy = SecurityContextHolder.getContextHolderStrategy() + contextHolderStrategy.context.authentication = onceAuthenticationToken + } + + companion object { + private val logger: Logger = LoggerFactory.getLogger(AuthController::class.java) + } } \ No newline at end of file