fix:角色授权修改
This commit is contained in:
@@ -58,18 +58,18 @@ public class AuthAdminController {
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/roles/{roleCode}/full-accounts/{uid}/change-password")
|
||||
@Operation(summary = "管理员修改 FULL 账户口令", description = "管理员为目标角色指定 UKey 席位账号设置新口令。")
|
||||
@PostMapping("/roles/{roleCode}/accounts/{uid}/change-password")
|
||||
@Operation(summary = "管理员修改角色席位口令", description = "管理员为目标角色指定席位账号设置新口令。")
|
||||
@ReplayProtected
|
||||
// @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL)
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.UPDATE, summary = "管理员修改 FULL 账户口令")
|
||||
public ApiResponse<Void> changeFullAccountPassword(
|
||||
@RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL)
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.UPDATE, summary = "管理员修改角色席位口令")
|
||||
public ApiResponse<Void> changeAccountPassword(
|
||||
@PathVariable("roleCode") String roleCode,
|
||||
@PathVariable("uid") Integer uid,
|
||||
@Valid @RequestBody AdminChangePasswordRequest request,
|
||||
HttpServletRequest httpRequest
|
||||
) {
|
||||
authAdminService.changeFullAccountPassword(
|
||||
authAdminService.changeAccountPassword(
|
||||
(String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_ROLE_CODE),
|
||||
(String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_AUTH_LEVEL),
|
||||
roleCode,
|
||||
@@ -80,28 +80,6 @@ public class AuthAdminController {
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/roles/{roleCode}/limited-accounts/{username}/change-password")
|
||||
@Operation(summary = "管理员修改 LIMITED 账户口令", description = "管理员为目标角色指定独立用户账号设置新口令。")
|
||||
@ReplayProtected
|
||||
// @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL)
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.UPDATE, summary = "管理员修改 LIMITED 账户口令")
|
||||
public ApiResponse<Void> changeLimitedAccountPassword(
|
||||
@PathVariable("roleCode") String roleCode,
|
||||
@PathVariable("username") String username,
|
||||
@Valid @RequestBody AdminChangePasswordRequest request,
|
||||
HttpServletRequest httpRequest
|
||||
) {
|
||||
authAdminService.changeLimitedAccountPassword(
|
||||
(String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_ROLE_CODE),
|
||||
(String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_AUTH_LEVEL),
|
||||
roleCode,
|
||||
username,
|
||||
request.getOldPassword(),
|
||||
request.getNewPassword()
|
||||
);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/super-admin/ukeys/issue-sign")
|
||||
@Operation(summary = "签发并绑定超级管理员 UKey", description = "系统初始化后登记超级管理员 UKey,不要求已有登录态。")
|
||||
public ApiResponse<UKeySignResult> issueSuperAdminUkeyBindingSign(
|
||||
|
||||
@@ -65,16 +65,16 @@ public class AuthController {
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/full-accounts/{uid}/change-password")
|
||||
@Operation(summary = "修改当前 FULL 账户口令", description = "基于当前 FULL 会话校验并更新指定席位账户口令。")
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.UPDATE, summary = "修改当前 FULL 账户口令")
|
||||
@PostMapping("/accounts/{uid}/change-password")
|
||||
@Operation(summary = "修改当前登录席位口令", description = "基于当前会话校验并更新指定角色席位口令。")
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.UPDATE, summary = "修改当前登录席位口令")
|
||||
@ReplayProtected
|
||||
public ApiResponse<Void> changeFullAccountPassword(
|
||||
public ApiResponse<Void> changeAccountPassword(
|
||||
@PathVariable("uid") Integer uid,
|
||||
@Valid @RequestBody ChangePasswordRequest request,
|
||||
HttpServletRequest httpRequest
|
||||
) {
|
||||
authService.changeFullAccountPassword(
|
||||
authService.changeAccountPassword(
|
||||
(String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_SESSION_TOKEN),
|
||||
uid,
|
||||
request.getCurrentPassword(),
|
||||
@@ -82,22 +82,4 @@ public class AuthController {
|
||||
);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/limited-accounts/{username}/change-password")
|
||||
@Operation(summary = "修改当前 LIMITED 账户口令", description = "基于当前 LIMITED 会话校验并更新指定受限账户口令。")
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.UPDATE, summary = "修改当前 LIMITED 账户口令")
|
||||
@ReplayProtected
|
||||
public ApiResponse<Void> changeLimitedAccountPassword(
|
||||
@PathVariable("username") String username,
|
||||
@Valid @RequestBody ChangePasswordRequest request,
|
||||
HttpServletRequest httpRequest
|
||||
) {
|
||||
authService.changeLimitedAccountPassword(
|
||||
(String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_SESSION_TOKEN),
|
||||
username,
|
||||
request.getCurrentPassword(),
|
||||
request.getNewPassword()
|
||||
);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,16 @@ public class AuthSessionPrincipal {
|
||||
return principal;
|
||||
}
|
||||
|
||||
public static AuthSessionPrincipal limited(String username) {
|
||||
public static AuthSessionPrincipal full(Integer uid, String username) {
|
||||
AuthSessionPrincipal principal = full(uid);
|
||||
principal.setUsername(username);
|
||||
return principal;
|
||||
}
|
||||
|
||||
public static AuthSessionPrincipal limited(Integer uid, String username) {
|
||||
AuthSessionPrincipal principal = new AuthSessionPrincipal();
|
||||
principal.setType("LIMITED");
|
||||
principal.setUid(uid);
|
||||
principal.setUsername(username);
|
||||
return principal;
|
||||
}
|
||||
|
||||
@@ -2,24 +2,25 @@ package com.cisd.tms.modules.auth.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "角色口令校验账号")
|
||||
public class PasswordLoginAccountRequest {
|
||||
|
||||
@NotBlank(message = "username is required")
|
||||
@Schema(description = "账号名", example = "super-admin-01")
|
||||
private String username;
|
||||
@NotNull(message = "uid is required")
|
||||
@Schema(description = "角色固定席位编号", example = "1")
|
||||
private Integer uid;
|
||||
|
||||
@NotBlank(message = "password is required")
|
||||
@Schema(description = "账号口令", example = "12345678")
|
||||
private String password;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
public Integer getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
public void setUid(Integer uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
package com.cisd.tms.modules.auth.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.cisd.tms.infrastructure.persistence.entity.BaseEntity;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@TableName("tms_auth_user_account")
|
||||
public class AuthUserAccountEntity extends BaseEntity {
|
||||
|
||||
private String username;
|
||||
private String displayName;
|
||||
private String roleCode;
|
||||
private String passwordHash;
|
||||
private String passwordSalt;
|
||||
private String status;
|
||||
private Boolean needChangePassword;
|
||||
private LocalDateTime passwordChangedAt;
|
||||
private Integer failedCount;
|
||||
private LocalDateTime lockedUntil;
|
||||
private LocalDateTime lastLoginAt;
|
||||
private LocalDateTime lastActiveAt;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getRoleCode() {
|
||||
return roleCode;
|
||||
}
|
||||
|
||||
public void setRoleCode(String roleCode) {
|
||||
this.roleCode = roleCode;
|
||||
}
|
||||
|
||||
public String getPasswordHash() {
|
||||
return passwordHash;
|
||||
}
|
||||
|
||||
public void setPasswordHash(String passwordHash) {
|
||||
this.passwordHash = passwordHash;
|
||||
}
|
||||
|
||||
public String getPasswordSalt() {
|
||||
return passwordSalt;
|
||||
}
|
||||
|
||||
public void setPasswordSalt(String passwordSalt) {
|
||||
this.passwordSalt = passwordSalt;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Boolean getNeedChangePassword() {
|
||||
return needChangePassword;
|
||||
}
|
||||
|
||||
public void setNeedChangePassword(Boolean needChangePassword) {
|
||||
this.needChangePassword = needChangePassword;
|
||||
}
|
||||
|
||||
public LocalDateTime getPasswordChangedAt() {
|
||||
return passwordChangedAt;
|
||||
}
|
||||
|
||||
public void setPasswordChangedAt(LocalDateTime passwordChangedAt) {
|
||||
this.passwordChangedAt = passwordChangedAt;
|
||||
}
|
||||
|
||||
public Integer getFailedCount() {
|
||||
return failedCount;
|
||||
}
|
||||
|
||||
public void setFailedCount(Integer failedCount) {
|
||||
this.failedCount = failedCount;
|
||||
}
|
||||
|
||||
public LocalDateTime getLockedUntil() {
|
||||
return lockedUntil;
|
||||
}
|
||||
|
||||
public void setLockedUntil(LocalDateTime lockedUntil) {
|
||||
this.lockedUntil = lockedUntil;
|
||||
}
|
||||
|
||||
public LocalDateTime getLastLoginAt() {
|
||||
return lastLoginAt;
|
||||
}
|
||||
|
||||
public void setLastLoginAt(LocalDateTime lastLoginAt) {
|
||||
this.lastLoginAt = lastLoginAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getLastActiveAt() {
|
||||
return lastActiveAt;
|
||||
}
|
||||
|
||||
public void setLastActiveAt(LocalDateTime lastActiveAt) {
|
||||
this.lastActiveAt = lastActiveAt;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.cisd.tms.modules.auth.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.cisd.tms.infrastructure.persistence.entity.BaseEntity;
|
||||
|
||||
@TableName("tms_auth_user")
|
||||
public class AuthUserEntity extends BaseEntity {
|
||||
|
||||
private String username;
|
||||
private String displayName;
|
||||
private String role;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.cisd.tms.modules.auth.enums;
|
||||
|
||||
public enum RoleCode {
|
||||
SUPER_ADMIN("SUPER_ADMIN", "超级管理员", 2),
|
||||
SUPER_ADMIN("SUPER_ADMIN", "系统管理员", 2),
|
||||
KEY_ADMIN("KEY_ADMIN", "密钥管理员", 1),
|
||||
AUDIT_ADMIN("AUDIT_ADMIN", "审计管理员", 1),
|
||||
OPS_ADMIN("OPS_ADMIN", "运维管理员", 1);
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.cisd.tms.modules.auth.mapper;
|
||||
|
||||
import com.cisd.tms.infrastructure.persistence.mapper.BaseMapperX;
|
||||
import com.cisd.tms.modules.auth.entity.AuthUserAccountEntity;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@Mapper
|
||||
public interface AuthUserAccountMapper extends BaseMapperX<AuthUserAccountEntity> {
|
||||
|
||||
AuthUserAccountEntity selectByUsername(@Param("username") String username);
|
||||
|
||||
List<AuthUserAccountEntity> selectByRoleCode(@Param("roleCode") String roleCode);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.cisd.tms.modules.auth.mapper;
|
||||
|
||||
import com.cisd.tms.infrastructure.persistence.mapper.BaseMapperX;
|
||||
import com.cisd.tms.modules.auth.entity.AuthUserEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@Mapper
|
||||
public interface AuthUserMapper extends BaseMapperX<AuthUserEntity> {
|
||||
|
||||
AuthUserEntity selectByUsername(@Param("username") String username);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.cisd.tms.modules.auth.repository;
|
||||
|
||||
import com.cisd.tms.modules.auth.entity.AuthUserAccountEntity;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface AuthUserAccountRepository {
|
||||
|
||||
Optional<AuthUserAccountEntity> findByUsername(String username);
|
||||
|
||||
List<AuthUserAccountEntity> findByRoleCode(String roleCode);
|
||||
|
||||
void save(AuthUserAccountEntity entity);
|
||||
|
||||
void update(AuthUserAccountEntity entity);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.cisd.tms.modules.auth.repository;
|
||||
|
||||
import com.cisd.tms.modules.auth.entity.AuthUserEntity;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface AuthUserRepository {
|
||||
|
||||
Optional<AuthUserEntity> findByUsername(String username);
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package com.cisd.tms.modules.auth.repository.impl;
|
||||
|
||||
import com.cisd.tms.modules.auth.entity.AuthUserAccountEntity;
|
||||
import com.cisd.tms.modules.auth.mapper.AuthUserAccountMapper;
|
||||
import com.cisd.tms.modules.auth.repository.AuthUserAccountRepository;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public class AuthUserAccountRepositoryImpl implements AuthUserAccountRepository {
|
||||
|
||||
private final AuthUserAccountMapper authUserAccountMapper;
|
||||
|
||||
public AuthUserAccountRepositoryImpl(AuthUserAccountMapper authUserAccountMapper) {
|
||||
this.authUserAccountMapper = authUserAccountMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<AuthUserAccountEntity> findByUsername(String username) {
|
||||
return Optional.ofNullable(authUserAccountMapper.selectByUsername(username));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuthUserAccountEntity> findByRoleCode(String roleCode) {
|
||||
return authUserAccountMapper.selectByRoleCode(roleCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(AuthUserAccountEntity entity) {
|
||||
authUserAccountMapper.insert(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AuthUserAccountEntity entity) {
|
||||
authUserAccountMapper.updateById(entity);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.cisd.tms.modules.auth.repository.impl;
|
||||
|
||||
import com.cisd.tms.modules.auth.entity.AuthUserEntity;
|
||||
import com.cisd.tms.modules.auth.mapper.AuthUserMapper;
|
||||
import com.cisd.tms.modules.auth.repository.AuthUserRepository;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public class AuthUserRepositoryImpl implements AuthUserRepository {
|
||||
|
||||
private final AuthUserMapper authUserMapper;
|
||||
|
||||
public AuthUserRepositoryImpl(AuthUserMapper authUserMapper) {
|
||||
this.authUserMapper = authUserMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<AuthUserEntity> findByUsername(String username) {
|
||||
return Optional.ofNullable(authUserMapper.selectByUsername(username));
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ public interface AuthAdminService {
|
||||
|
||||
void resetPassword(String operatorRoleCode, String operatorAuthLevel, String targetRoleCode);
|
||||
|
||||
void changeFullAccountPassword(
|
||||
void changeAccountPassword(
|
||||
String operatorRoleCode,
|
||||
String operatorAuthLevel,
|
||||
String targetRoleCode,
|
||||
@@ -18,15 +18,6 @@ public interface AuthAdminService {
|
||||
String newPassword
|
||||
);
|
||||
|
||||
void changeLimitedAccountPassword(
|
||||
String operatorRoleCode,
|
||||
String operatorAuthLevel,
|
||||
String targetRoleCode,
|
||||
String username,
|
||||
String oldPassword,
|
||||
String newPassword
|
||||
);
|
||||
|
||||
UKeySignResult issueUkeyBindingSign(
|
||||
String operatorRoleCode,
|
||||
String operatorAuthLevel,
|
||||
|
||||
@@ -27,7 +27,5 @@ public interface AuthService {
|
||||
|
||||
void logout(String sessionToken);
|
||||
|
||||
void changeFullAccountPassword(String sessionToken, Integer uid, String currentPassword, String newPassword);
|
||||
|
||||
void changeLimitedAccountPassword(String sessionToken, String username, String currentPassword, String newPassword);
|
||||
void changeAccountPassword(String sessionToken, Integer uid, String currentPassword, String newPassword);
|
||||
}
|
||||
|
||||
@@ -4,13 +4,11 @@ import com.cisd.tms.common.enums.ErrorCode;
|
||||
import com.cisd.tms.common.exception.BizException;
|
||||
import com.cisd.tms.common.util.TraceIdUtil;
|
||||
import com.cisd.tms.modules.auth.entity.AuthFullAccountEntity;
|
||||
import com.cisd.tms.modules.auth.entity.AuthUserAccountEntity;
|
||||
import com.cisd.tms.modules.auth.entity.RoleAccountEntity;
|
||||
import com.cisd.tms.modules.auth.entity.RoleUkeyBindingEntity;
|
||||
import com.cisd.tms.modules.auth.enums.RoleAccountStatus;
|
||||
import com.cisd.tms.modules.auth.enums.RoleCode;
|
||||
import com.cisd.tms.modules.auth.repository.AuthFullAccountRepository;
|
||||
import com.cisd.tms.modules.auth.repository.AuthUserAccountRepository;
|
||||
import com.cisd.tms.modules.auth.repository.RoleAccountRepository;
|
||||
import com.cisd.tms.modules.auth.repository.RoleUkeyBindingRepository;
|
||||
import com.cisd.tms.modules.auth.service.AuthAdminService;
|
||||
@@ -40,7 +38,6 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
|
||||
private final RoleAccountRepository roleAccountRepository;
|
||||
private final AuthFullAccountRepository authFullAccountRepository;
|
||||
private final AuthUserAccountRepository authUserAccountRepository;
|
||||
private final RoleUkeyBindingRepository roleUkeyBindingRepository;
|
||||
private final PasswordHasher passwordHasher;
|
||||
private final LmkService lmkService;
|
||||
@@ -68,19 +65,6 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
authFullAccountRepository.update(account);
|
||||
}
|
||||
|
||||
List<AuthUserAccountEntity> accounts = authUserAccountRepository.findByRoleCode(targetRoleCode);
|
||||
if (accounts.isEmpty()) {
|
||||
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "target role user accounts not found");
|
||||
}
|
||||
for (AuthUserAccountEntity account : accounts) {
|
||||
account.setStatus(RoleAccountStatus.ACTIVE.name());
|
||||
account.setNeedChangePassword(Boolean.TRUE);
|
||||
account.setFailedCount(0);
|
||||
account.setLockedUntil(null);
|
||||
account.setLastActiveAt(null);
|
||||
account.setLastLoginAt(null);
|
||||
authUserAccountRepository.update(account);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,27 +92,10 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
authFullAccountRepository.update(account);
|
||||
}
|
||||
|
||||
List<AuthUserAccountEntity> accounts = authUserAccountRepository.findByRoleCode(targetRoleCode);
|
||||
if (accounts.isEmpty()) {
|
||||
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "target role user accounts not found");
|
||||
}
|
||||
for (AuthUserAccountEntity account : accounts) {
|
||||
String newSalt = passwordSaltGenerator.nextSalt();
|
||||
account.setPasswordSalt(newSalt);
|
||||
account.setPasswordHash(passwordHasher.hash(DEFAULT_PASSWORD, newSalt));
|
||||
account.setStatus(RoleAccountStatus.ACTIVE.name());
|
||||
account.setNeedChangePassword(Boolean.TRUE);
|
||||
account.setPasswordChangedAt(current);
|
||||
account.setFailedCount(0);
|
||||
account.setLockedUntil(null);
|
||||
account.setLastActiveAt(null);
|
||||
account.setLastLoginAt(null);
|
||||
authUserAccountRepository.update(account);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeFullAccountPassword(
|
||||
public void changeAccountPassword(
|
||||
String operatorRoleCode,
|
||||
String operatorAuthLevel,
|
||||
String targetRoleCode,
|
||||
@@ -151,29 +118,6 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
authFullAccountRepository.update(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeLimitedAccountPassword(
|
||||
String operatorRoleCode,
|
||||
String operatorAuthLevel,
|
||||
String targetRoleCode,
|
||||
String username,
|
||||
String oldPassword,
|
||||
String newPassword
|
||||
) {
|
||||
PasswordComplexityValidator.validate(newPassword);
|
||||
loadRole(targetRoleCode);
|
||||
AuthUserAccountEntity account = authUserAccountRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "target role user account not found"));
|
||||
if (!targetRoleCode.equals(account.getRoleCode())) {
|
||||
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "target role user account not found");
|
||||
}
|
||||
if (!passwordHasher.matches(oldPassword, account.getPasswordSalt(), account.getPasswordHash())) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "old password is incorrect");
|
||||
}
|
||||
applyAdminPasswordChange(account, newPassword);
|
||||
authUserAccountRepository.update(account);
|
||||
}
|
||||
|
||||
private void bindUkey(
|
||||
String targetRoleCode,
|
||||
Integer uid,
|
||||
@@ -277,19 +221,6 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
}
|
||||
}
|
||||
|
||||
private void applyAdminPasswordChange(AuthUserAccountEntity account, String newPassword) {
|
||||
String newSalt = passwordSaltGenerator.nextSalt();
|
||||
account.setPasswordSalt(newSalt);
|
||||
account.setPasswordHash(passwordHasher.hash(newPassword, newSalt));
|
||||
account.setNeedChangePassword(Boolean.TRUE);
|
||||
account.setPasswordChangedAt(now());
|
||||
account.setFailedCount(0);
|
||||
account.setLockedUntil(null);
|
||||
if (RoleAccountStatus.LOCKED.name().equals(account.getStatus())) {
|
||||
account.setStatus(RoleAccountStatus.ACTIVE.name());
|
||||
}
|
||||
}
|
||||
|
||||
private RoleAccountEntity loadRole(String roleCode) {
|
||||
resolveRoleCode(roleCode);
|
||||
return roleAccountRepository.findByRoleCode(roleCode)
|
||||
|
||||
@@ -16,8 +16,6 @@ import com.cisd.tms.modules.auth.dto.UkeyLoginRandomResponse;
|
||||
import com.cisd.tms.modules.auth.dto.UkeyLoginRequest;
|
||||
import com.cisd.tms.modules.auth.entity.AuthFullAccountEntity;
|
||||
import com.cisd.tms.modules.auth.entity.AuthSessionEntity;
|
||||
import com.cisd.tms.modules.auth.entity.AuthUserAccountEntity;
|
||||
import com.cisd.tms.modules.auth.entity.AuthUserEntity;
|
||||
import com.cisd.tms.modules.auth.entity.RoleAccountEntity;
|
||||
import com.cisd.tms.modules.auth.entity.RoleUkeyBindingEntity;
|
||||
import com.cisd.tms.modules.auth.enums.AuthLevel;
|
||||
@@ -26,8 +24,6 @@ import com.cisd.tms.modules.auth.enums.RoleAccountStatus;
|
||||
import com.cisd.tms.modules.auth.enums.RoleCode;
|
||||
import com.cisd.tms.modules.auth.repository.AuthFullAccountRepository;
|
||||
import com.cisd.tms.modules.auth.repository.AuthSessionRepository;
|
||||
import com.cisd.tms.modules.auth.repository.AuthUserAccountRepository;
|
||||
import com.cisd.tms.modules.auth.repository.AuthUserRepository;
|
||||
import com.cisd.tms.modules.auth.repository.RoleAccountRepository;
|
||||
import com.cisd.tms.modules.auth.repository.RoleUkeyBindingRepository;
|
||||
import com.cisd.tms.modules.auth.service.AuthPolicyService;
|
||||
@@ -69,8 +65,6 @@ public class AuthServiceImpl implements AuthService {
|
||||
private static final int PASSWORD_EXPIRE_DAYS = 30;
|
||||
|
||||
private final RoleAccountRepository roleAccountRepository;
|
||||
private final AuthUserRepository authUserRepository;
|
||||
private final AuthUserAccountRepository authUserAccountRepository;
|
||||
private final AuthFullAccountRepository authFullAccountRepository;
|
||||
private final AuthSessionRepository authSessionRepository;
|
||||
private final RoleUkeyBindingRepository roleUkeyBindingRepository;
|
||||
@@ -92,19 +86,17 @@ public class AuthServiceImpl implements AuthService {
|
||||
|
||||
validateRoleStatus(roleAccount);
|
||||
AuthMethod authMethod = resolveAuthMethod(request.getUkeySerials());
|
||||
List<AuthUserAccountEntity> validatedAccounts = new ArrayList<>();
|
||||
List<AuthFullAccountEntity> validatedFullAccounts = new ArrayList<>();
|
||||
List<AuthFullAccountEntity> validatedAccounts;
|
||||
if (AuthMethod.PASSWORD == authMethod) {
|
||||
validatedAccounts = validateRoleAccounts(roleAccount, request.getAccounts());
|
||||
resetValidatedAccounts(validatedAccounts);
|
||||
} else {
|
||||
validatedFullAccounts = validateFullAccounts(roleAccount, request.getFullAccounts());
|
||||
resetValidatedFullAccounts(validatedFullAccounts);
|
||||
validatedAccounts = validateFullAccounts(roleAccount, request.getFullAccounts());
|
||||
}
|
||||
resetValidatedAccounts(validatedAccounts);
|
||||
AuthLevel authLevel = resolveAuthLevel(roleAccount, request.getUkeySerials());
|
||||
List<AuthSessionPrincipal> authenticatedPrincipals = AuthMethod.PASSWORD == authMethod
|
||||
? normalizeAuthenticatedUsers(validatedAccounts)
|
||||
: normalizeAuthenticatedFullAccounts(validatedFullAccounts);
|
||||
? normalizeAuthenticatedAccounts(validatedAccounts, AuthLevel.LIMITED)
|
||||
: normalizeAuthenticatedAccounts(validatedAccounts, AuthLevel.FULL);
|
||||
expireConcurrentSessions(roleAccount.getRoleCode(), authLevel, authenticatedPrincipals);
|
||||
AuthSessionEntity session = buildSession(
|
||||
roleAccount.getRoleCode(),
|
||||
@@ -120,9 +112,7 @@ public class AuthServiceImpl implements AuthService {
|
||||
response.setToken(session.getSessionToken());
|
||||
response.setExpiresAt(OffsetDateTime.of(session.getExpiresAt(), ZoneOffset.UTC).toString());
|
||||
response.setNeedChangePassword(
|
||||
AuthMethod.PASSWORD == authMethod
|
||||
? needsPasswordChangeForLimitedAccounts(validatedAccounts)
|
||||
: needsPasswordChangeForFullAccounts(validatedFullAccounts)
|
||||
needsPasswordChangeForAccounts(validatedAccounts)
|
||||
);
|
||||
return response;
|
||||
}
|
||||
@@ -189,36 +179,19 @@ public class AuthServiceImpl implements AuthService {
|
||||
@Override
|
||||
public CurrentUserResponse me(String username, String sessionToken) {
|
||||
String normalized = username == null || username.isBlank() ? "admin" : username;
|
||||
RoleAccountEntity roleAccount = roleAccountRepository.findByRoleCode(normalized).orElse(null);
|
||||
if (roleAccount != null) {
|
||||
CurrentUserResponse response = new CurrentUserResponse();
|
||||
response.setUsername(roleAccount.getRoleCode());
|
||||
response.setDisplayName(roleAccount.getDisplayName());
|
||||
response.setRole(roleAccount.getRoleCode());
|
||||
AuthSessionEntity session = sessionToken == null ? null : authSessionRepository.findBySessionToken(sessionToken).orElse(null);
|
||||
if (session != null) {
|
||||
response.setAuthLevel(session.getAuthLevel());
|
||||
response.setNeedChangePassword(needsPasswordChange(session));
|
||||
} else {
|
||||
response.setNeedChangePassword(Boolean.FALSE);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
AuthUserEntity entity = authUserRepository.findByUsername(normalized).orElse(null);
|
||||
RoleAccountEntity roleAccount = roleAccountRepository.findByRoleCode(normalized)
|
||||
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "role account not found"));
|
||||
CurrentUserResponse response = new CurrentUserResponse();
|
||||
if (entity == null) {
|
||||
response.setUsername(normalized);
|
||||
response.setDisplayName("TMS Administrator");
|
||||
response.setRole("ADMIN");
|
||||
response.setUsername(roleAccount.getRoleCode());
|
||||
response.setDisplayName(roleAccount.getDisplayName());
|
||||
response.setRole(roleAccount.getRoleCode());
|
||||
AuthSessionEntity session = sessionToken == null ? null : authSessionRepository.findBySessionToken(sessionToken).orElse(null);
|
||||
if (session != null) {
|
||||
response.setAuthLevel(session.getAuthLevel());
|
||||
response.setNeedChangePassword(needsPasswordChange(session));
|
||||
} else {
|
||||
response.setNeedChangePassword(Boolean.FALSE);
|
||||
return response;
|
||||
}
|
||||
|
||||
response.setUsername(entity.getUsername());
|
||||
response.setDisplayName(entity.getDisplayName());
|
||||
response.setRole(entity.getRole());
|
||||
response.setNeedChangePassword(Boolean.FALSE);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -231,17 +204,14 @@ public class AuthServiceImpl implements AuthService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeFullAccountPassword(String sessionToken, Integer uid, String currentPassword, String newPassword) {
|
||||
public void changeAccountPassword(String sessionToken, Integer uid, String currentPassword, String newPassword) {
|
||||
PasswordComplexityValidator.validate(newPassword);
|
||||
AuthSessionEntity session = requireActiveSession(sessionToken);
|
||||
if (!AuthLevel.FULL.name().equals(session.getAuthLevel())) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "full session is required");
|
||||
}
|
||||
if (uid == null || !containsAuthenticatedFullUid(session, uid)) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "full account is not authenticated in current session");
|
||||
if (uid == null || !containsAuthenticatedUid(session, uid)) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "account is not authenticated in current session");
|
||||
}
|
||||
AuthFullAccountEntity fullAccount = authFullAccountRepository.findByRoleCodeAndUid(session.getRoleCode(), uid)
|
||||
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "full account not found"));
|
||||
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "account not found"));
|
||||
validateFullAccountStatus(fullAccount);
|
||||
if (!passwordHasher.matches(currentPassword, fullAccount.getPasswordSalt(), fullAccount.getPasswordHash())) {
|
||||
onFullPasswordFailed(fullAccount);
|
||||
@@ -265,49 +235,13 @@ public class AuthServiceImpl implements AuthService {
|
||||
authSessionRepository.update(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeLimitedAccountPassword(String sessionToken, String username, String currentPassword, String newPassword) {
|
||||
PasswordComplexityValidator.validate(newPassword);
|
||||
AuthSessionEntity session = requireActiveSession(sessionToken);
|
||||
if (!AuthLevel.LIMITED.name().equals(session.getAuthLevel())) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "limited session is required");
|
||||
}
|
||||
if (username == null || username.isBlank() || !containsAuthenticatedLimitedUsername(session, username)) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "limited account is not authenticated in current session");
|
||||
}
|
||||
AuthUserAccountEntity userAccount = authUserAccountRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "user account not found"));
|
||||
validateUserAccountRole(session.getRoleCode(), userAccount);
|
||||
validateUserAccountStatus(userAccount);
|
||||
if (!passwordHasher.matches(currentPassword, userAccount.getPasswordSalt(), userAccount.getPasswordHash())) {
|
||||
onPasswordFailed(userAccount);
|
||||
}
|
||||
|
||||
String salt = passwordSaltGenerator.nextSalt();
|
||||
LocalDateTime current = now();
|
||||
userAccount.setPasswordSalt(salt);
|
||||
userAccount.setPasswordHash(passwordHasher.hash(newPassword, salt));
|
||||
userAccount.setFailedCount(0);
|
||||
userAccount.setLockedUntil(null);
|
||||
userAccount.setStatus(RoleAccountStatus.ACTIVE.name());
|
||||
userAccount.setNeedChangePassword(Boolean.FALSE);
|
||||
userAccount.setPasswordChangedAt(current);
|
||||
userAccount.setLastLoginAt(current);
|
||||
userAccount.setLastActiveAt(current);
|
||||
authUserAccountRepository.update(userAccount);
|
||||
|
||||
session.setLastActiveAt(current);
|
||||
session.setExpiresAt(current.plusMinutes(IDLE_TIMEOUT_MINUTES));
|
||||
authSessionRepository.update(session);
|
||||
}
|
||||
|
||||
private void validateRoleStatus(RoleAccountEntity roleAccount) {
|
||||
if (!RoleAccountStatus.ACTIVE.name().equals(roleAccount.getStatus())) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "role is not enabled");
|
||||
}
|
||||
}
|
||||
|
||||
private List<AuthUserAccountEntity> validateRoleAccounts(
|
||||
private List<AuthFullAccountEntity> validateRoleAccounts(
|
||||
RoleAccountEntity roleAccount,
|
||||
List<PasswordLoginAccountRequest> accounts
|
||||
) {
|
||||
@@ -319,54 +253,25 @@ public class AuthServiceImpl implements AuthService {
|
||||
);
|
||||
}
|
||||
|
||||
Set<String> uniqueUsernames = new LinkedHashSet<>();
|
||||
List<AuthUserAccountEntity> validatedAccounts = new ArrayList<>();
|
||||
Set<Integer> uniqueUids = new LinkedHashSet<>();
|
||||
List<AuthFullAccountEntity> validatedAccounts = new ArrayList<>();
|
||||
for (PasswordLoginAccountRequest account : accounts) {
|
||||
if (!uniqueUsernames.add(account.getUsername())) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "duplicate account username submitted");
|
||||
Integer uid = account.getUid();
|
||||
if (uid == null || !uniqueUids.add(uid)) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "duplicate account uid submitted");
|
||||
}
|
||||
AuthUserAccountEntity userAccount = authUserAccountRepository.findByUsername(account.getUsername())
|
||||
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "user account not found"));
|
||||
validateUserAccountRole(roleAccount.getRoleCode(), userAccount);
|
||||
validateUserAccountStatus(userAccount);
|
||||
if (!passwordHasher.matches(account.getPassword(), userAccount.getPasswordSalt(), userAccount.getPasswordHash())) {
|
||||
onPasswordFailed(userAccount);
|
||||
AuthFullAccountEntity roleAccountSeat = authFullAccountRepository
|
||||
.findByRoleCodeAndUid(roleAccount.getRoleCode(), uid)
|
||||
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "account not found"));
|
||||
validateFullAccountStatus(roleAccountSeat);
|
||||
if (!passwordHasher.matches(account.getPassword(), roleAccountSeat.getPasswordSalt(), roleAccountSeat.getPasswordHash())) {
|
||||
onFullPasswordFailed(roleAccountSeat);
|
||||
}
|
||||
validatedAccounts.add(userAccount);
|
||||
validatedAccounts.add(roleAccountSeat);
|
||||
}
|
||||
return validatedAccounts;
|
||||
}
|
||||
|
||||
private void validateUserAccountRole(String expectedRoleCode, AuthUserAccountEntity userAccount) {
|
||||
if (!expectedRoleCode.equals(userAccount.getRoleCode())) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "user account does not belong to target role");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateUserAccountStatus(AuthUserAccountEntity userAccount) {
|
||||
if (RoleAccountStatus.UNENABLED.name().equals(userAccount.getStatus())) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "user account is not enabled");
|
||||
}
|
||||
if (RoleAccountStatus.LOCKED.name().equals(userAccount.getStatus())
|
||||
&& (userAccount.getLockedUntil() == null || userAccount.getLockedUntil().isAfter(now()))) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "user account is locked");
|
||||
}
|
||||
}
|
||||
|
||||
private void onPasswordFailed(AuthUserAccountEntity userAccount) {
|
||||
int failedCount = userAccount.getFailedCount() == null ? 0 : userAccount.getFailedCount();
|
||||
failedCount++;
|
||||
userAccount.setFailedCount(failedCount);
|
||||
if (failedCount >= MAX_FAILED_ATTEMPTS) {
|
||||
userAccount.setStatus(RoleAccountStatus.LOCKED.name());
|
||||
userAccount.setLockedUntil(now().plusMinutes(IDLE_TIMEOUT_MINUTES));
|
||||
authUserAccountRepository.update(userAccount);
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "user account is locked");
|
||||
}
|
||||
authUserAccountRepository.update(userAccount);
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "password is incorrect");
|
||||
}
|
||||
|
||||
private void validateFullAccountStatus(AuthFullAccountEntity fullAccount) {
|
||||
if (RoleAccountStatus.UNENABLED.name().equals(fullAccount.getStatus())) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "full account is not enabled");
|
||||
@@ -391,19 +296,7 @@ public class AuthServiceImpl implements AuthService {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "password is incorrect");
|
||||
}
|
||||
|
||||
private void resetValidatedAccounts(List<AuthUserAccountEntity> validatedAccounts) {
|
||||
LocalDateTime current = now();
|
||||
for (AuthUserAccountEntity account : validatedAccounts) {
|
||||
account.setFailedCount(0);
|
||||
account.setLockedUntil(null);
|
||||
account.setStatus(RoleAccountStatus.ACTIVE.name());
|
||||
account.setLastLoginAt(current);
|
||||
account.setLastActiveAt(current);
|
||||
authUserAccountRepository.update(account);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetValidatedFullAccounts(List<AuthFullAccountEntity> validatedAccounts) {
|
||||
private void resetValidatedAccounts(List<AuthFullAccountEntity> validatedAccounts) {
|
||||
LocalDateTime current = now();
|
||||
for (AuthFullAccountEntity account : validatedAccounts) {
|
||||
account.setFailedCount(0);
|
||||
@@ -587,25 +480,18 @@ public class AuthServiceImpl implements AuthService {
|
||||
}
|
||||
}
|
||||
|
||||
private List<AuthSessionPrincipal> normalizeAuthenticatedUsers(List<AuthUserAccountEntity> validatedAccounts) {
|
||||
private List<AuthSessionPrincipal> normalizeAuthenticatedAccounts(
|
||||
List<AuthFullAccountEntity> validatedAccounts,
|
||||
AuthLevel authLevel
|
||||
) {
|
||||
if (validatedAccounts == null || validatedAccounts.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return validatedAccounts.stream()
|
||||
.map(AuthUserAccountEntity::getUsername)
|
||||
.sorted()
|
||||
.map(AuthSessionPrincipal::limited)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<AuthSessionPrincipal> normalizeAuthenticatedFullAccounts(List<AuthFullAccountEntity> validatedAccounts) {
|
||||
if (validatedAccounts == null || validatedAccounts.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return validatedAccounts.stream()
|
||||
.map(AuthFullAccountEntity::getUid)
|
||||
.sorted()
|
||||
.map(AuthSessionPrincipal::full)
|
||||
.sorted(java.util.Comparator.comparing(AuthFullAccountEntity::getUid))
|
||||
.map(account -> AuthLevel.FULL == authLevel
|
||||
? AuthSessionPrincipal.full(account.getUid(), account.getAccountName())
|
||||
: AuthSessionPrincipal.limited(account.getUid(), account.getAccountName()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -636,7 +522,11 @@ public class AuthServiceImpl implements AuthService {
|
||||
if (value.startsWith("FULL:")) {
|
||||
principals.add(AuthSessionPrincipal.full(Integer.parseInt(value.substring("FULL:".length()))));
|
||||
} else {
|
||||
principals.add(AuthSessionPrincipal.limited(value));
|
||||
AuthFullAccountEntity account = authFullAccountRepository.findByRoleCode(session.getRoleCode()).stream()
|
||||
.filter(item -> value.equals(item.getAccountName()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
principals.add(AuthSessionPrincipal.limited(account == null ? null : account.getUid(), value));
|
||||
}
|
||||
}
|
||||
return principals;
|
||||
@@ -650,25 +540,16 @@ public class AuthServiceImpl implements AuthService {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsAuthenticatedFullUid(AuthSessionEntity session, Integer uid) {
|
||||
private boolean containsAuthenticatedUid(AuthSessionEntity session, Integer uid) {
|
||||
return authenticatedPrincipals(session).stream()
|
||||
.anyMatch(principal -> "FULL".equals(principal.getType()) && uid.equals(principal.getUid()));
|
||||
}
|
||||
|
||||
private boolean containsAuthenticatedLimitedUsername(AuthSessionEntity session, String username) {
|
||||
return authenticatedPrincipals(session).stream()
|
||||
.anyMatch(principal -> "LIMITED".equals(principal.getType()) && username.equals(principal.getUsername()));
|
||||
.anyMatch(principal -> uid.equals(principal.getUid()));
|
||||
}
|
||||
|
||||
private LocalDateTime now() {
|
||||
return LocalDateTime.ofInstant(clock.instant(), ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
private boolean needsPasswordChangeForLimitedAccounts(List<AuthUserAccountEntity> accounts) {
|
||||
return accounts != null && accounts.stream().anyMatch(this::needsPasswordChange);
|
||||
}
|
||||
|
||||
private boolean needsPasswordChangeForFullAccounts(List<AuthFullAccountEntity> accounts) {
|
||||
private boolean needsPasswordChangeForAccounts(List<AuthFullAccountEntity> accounts) {
|
||||
return accounts != null && accounts.stream().anyMatch(this::needsPasswordChange);
|
||||
}
|
||||
|
||||
@@ -680,18 +561,10 @@ public class AuthServiceImpl implements AuthService {
|
||||
if (principals.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (AuthLevel.FULL.name().equals(session.getAuthLevel())) {
|
||||
List<AuthFullAccountEntity> accounts = authFullAccountRepository.findByRoleCode(session.getRoleCode());
|
||||
return accounts.stream()
|
||||
.filter(account -> principals.stream()
|
||||
.anyMatch(principal -> "FULL".equals(principal.getType()) && account.getUid().equals(principal.getUid())))
|
||||
.anyMatch(this::needsPasswordChange);
|
||||
}
|
||||
List<AuthUserAccountEntity> accounts = authUserAccountRepository.findByRoleCode(session.getRoleCode());
|
||||
List<AuthFullAccountEntity> accounts = authFullAccountRepository.findByRoleCode(session.getRoleCode());
|
||||
return accounts.stream()
|
||||
.filter(account -> principals.stream()
|
||||
.anyMatch(principal -> "LIMITED".equals(principal.getType())
|
||||
&& account.getUsername().equals(principal.getUsername())))
|
||||
.anyMatch(principal -> account.getUid().equals(principal.getUid())))
|
||||
.anyMatch(this::needsPasswordChange);
|
||||
}
|
||||
|
||||
@@ -700,11 +573,6 @@ public class AuthServiceImpl implements AuthService {
|
||||
|| isPasswordExpired(account.getPasswordChangedAt());
|
||||
}
|
||||
|
||||
private boolean needsPasswordChange(AuthUserAccountEntity account) {
|
||||
return Boolean.TRUE.equals(account.getNeedChangePassword())
|
||||
|| isPasswordExpired(account.getPasswordChangedAt());
|
||||
}
|
||||
|
||||
private boolean isPasswordExpired(LocalDateTime passwordChangedAt) {
|
||||
return passwordChangedAt == null || !passwordChangedAt.isAfter(now().minusDays(PASSWORD_EXPIRE_DAYS));
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ public class PcieCompatUkeyVerifier implements CompatUkeyVerifier {
|
||||
private static final String PROVIDER = "BC";
|
||||
private static final String SIGNATURE_ALGORITHM = "SM3withSM2";
|
||||
private static final String SM2_CURVE = "sm2p256v1";
|
||||
private static final int GM0018_ECCREF_PUBLIC_KEY_LENGTH = 132;
|
||||
private static final int GM0018_ECCREF_COMPONENT_LENGTH = 64;
|
||||
private static final int SM2_COORDINATE_LENGTH = 32;
|
||||
|
||||
static {
|
||||
if (Security.getProvider(PROVIDER) == null) {
|
||||
@@ -65,6 +68,9 @@ public class PcieCompatUkeyVerifier implements CompatUkeyVerifier {
|
||||
public void verifyLoginSignature(String pubKey, String loginSignData, String loginSign) {
|
||||
try {
|
||||
PublicKey publicKey = parsePublicKey(pubKey);
|
||||
if (loginSignData == null || loginSignData.isBlank()) {
|
||||
throw new IllegalArgumentException("login sign data is required");
|
||||
}
|
||||
byte[] signatureBytes = decodeSignature(loginSign);
|
||||
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM, PROVIDER);
|
||||
signature.initVerify(publicKey);
|
||||
@@ -96,7 +102,7 @@ public class PcieCompatUkeyVerifier implements CompatUkeyVerifier {
|
||||
try {
|
||||
return KeyFactory.getInstance("EC", PROVIDER).generatePublic(new X509EncodedKeySpec(decoded));
|
||||
} catch (Exception ignored) {
|
||||
return parseUncompressedPointPublicKey(decoded);
|
||||
return parseUncompressedPointPublicKey(toPointBytes(decoded));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,12 +116,40 @@ public class PcieCompatUkeyVerifier implements CompatUkeyVerifier {
|
||||
}
|
||||
}
|
||||
|
||||
private PublicKey parseUncompressedPointPublicKey(byte[] keyBytes) throws Exception {
|
||||
byte[] pointBytes = keyBytes;
|
||||
private byte[] toPointBytes(byte[] keyBytes) {
|
||||
if (keyBytes.length == GM0018_ECCREF_PUBLIC_KEY_LENGTH) {
|
||||
return gm0018EccRefToPointBytes(keyBytes);
|
||||
}
|
||||
return keyBytes;
|
||||
}
|
||||
|
||||
private byte[] gm0018EccRefToPointBytes(byte[] keyBytes) {
|
||||
int bitsLittleEndian = (keyBytes[0] & 0xff)
|
||||
| ((keyBytes[1] & 0xff) << 8)
|
||||
| ((keyBytes[2] & 0xff) << 16)
|
||||
| ((keyBytes[3] & 0xff) << 24);
|
||||
int bitsBigEndian = ((keyBytes[0] & 0xff) << 24)
|
||||
| ((keyBytes[1] & 0xff) << 16)
|
||||
| ((keyBytes[2] & 0xff) << 8)
|
||||
| (keyBytes[3] & 0xff);
|
||||
if (bitsLittleEndian != 256 && bitsBigEndian != 256) {
|
||||
throw new IllegalArgumentException("login public key is invalid");
|
||||
}
|
||||
byte[] pointBytes = new byte[65];
|
||||
pointBytes[0] = 0x04;
|
||||
System.arraycopy(keyBytes, 4 + GM0018_ECCREF_COMPONENT_LENGTH - SM2_COORDINATE_LENGTH,
|
||||
pointBytes, 1, SM2_COORDINATE_LENGTH);
|
||||
System.arraycopy(keyBytes, 4 + GM0018_ECCREF_COMPONENT_LENGTH * 2 - SM2_COORDINATE_LENGTH,
|
||||
pointBytes, 1 + SM2_COORDINATE_LENGTH, SM2_COORDINATE_LENGTH);
|
||||
return pointBytes;
|
||||
}
|
||||
|
||||
private PublicKey parseUncompressedPointPublicKey(byte[] pointBytes) throws Exception {
|
||||
if (pointBytes.length == 64) {
|
||||
byte[] rawPointBytes = pointBytes;
|
||||
pointBytes = new byte[65];
|
||||
pointBytes[0] = 0x04;
|
||||
System.arraycopy(keyBytes, 0, pointBytes, 1, keyBytes.length);
|
||||
System.arraycopy(rawPointBytes, 0, pointBytes, 1, rawPointBytes.length);
|
||||
}
|
||||
if (pointBytes.length != 65 || pointBytes[0] != 0x04) {
|
||||
throw new IllegalArgumentException("login public key is invalid");
|
||||
@@ -129,16 +163,31 @@ public class PcieCompatUkeyVerifier implements CompatUkeyVerifier {
|
||||
}
|
||||
|
||||
private byte[] decodeSignature(String value) {
|
||||
if (value == null || value.isBlank()) {
|
||||
throw new IllegalArgumentException("login signature is required");
|
||||
}
|
||||
byte[] decoded = decodeHexOrBase64(normalize(value), "login signature is invalid");
|
||||
if (isDerSignature(decoded)) {
|
||||
return decoded;
|
||||
}
|
||||
if (decoded.length == GM0018_ECCREF_COMPONENT_LENGTH * 2) {
|
||||
return rawRsToDer(gm0018SignatureToRawRs(decoded));
|
||||
}
|
||||
if (decoded.length == 64) {
|
||||
return rawRsToDer(decoded);
|
||||
}
|
||||
throw new IllegalArgumentException("login signature is invalid");
|
||||
}
|
||||
|
||||
private byte[] gm0018SignatureToRawRs(byte[] signatureBytes) {
|
||||
byte[] rawSignature = new byte[64];
|
||||
System.arraycopy(signatureBytes, GM0018_ECCREF_COMPONENT_LENGTH - SM2_COORDINATE_LENGTH,
|
||||
rawSignature, 0, SM2_COORDINATE_LENGTH);
|
||||
System.arraycopy(signatureBytes, GM0018_ECCREF_COMPONENT_LENGTH * 2 - SM2_COORDINATE_LENGTH,
|
||||
rawSignature, SM2_COORDINATE_LENGTH, SM2_COORDINATE_LENGTH);
|
||||
return rawSignature;
|
||||
}
|
||||
|
||||
private boolean isDerSignature(byte[] signatureBytes) {
|
||||
try (ASN1InputStream inputStream = new ASN1InputStream(signatureBytes)) {
|
||||
ASN1Primitive primitive = inputStream.readObject();
|
||||
|
||||
@@ -142,7 +142,7 @@ public class OperationAuditAspect {
|
||||
return "";
|
||||
}
|
||||
return request.getAccounts().stream()
|
||||
.map(account -> account.getUsername() == null ? "" : account.getUsername().trim())
|
||||
.map(account -> account.getUid() == null ? "" : "uid=" + account.getUid())
|
||||
.filter(value -> !value.isBlank())
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
@@ -139,16 +139,6 @@ CREATE TABLE IF NOT EXISTS tms_resource_task_step (
|
||||
-- 认证与设备基础表
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tms_auth_user (
|
||||
id BIGINT PRIMARY KEY,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
display_name VARCHAR(128) NOT NULL,
|
||||
role VARCHAR(64) NOT NULL,
|
||||
create_time DATETIME(3) NOT NULL,
|
||||
update_time DATETIME(3) NOT NULL,
|
||||
UNIQUE KEY uk_tms_auth_user_username (username)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tms_device_node (
|
||||
id BIGINT PRIMARY KEY,
|
||||
node_id VARCHAR(64) NOT NULL,
|
||||
@@ -267,26 +257,6 @@ CREATE TABLE IF NOT EXISTS tms_role_ukey_binding (
|
||||
KEY idx_tms_role_ukey_binding_role_status (role_code, status)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tms_auth_user_account (
|
||||
id BIGINT PRIMARY KEY,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
display_name VARCHAR(128) NOT NULL,
|
||||
role_code VARCHAR(64) NOT NULL,
|
||||
password_hash VARCHAR(256) NOT NULL,
|
||||
password_salt VARCHAR(128) NOT NULL,
|
||||
status VARCHAR(32) NOT NULL,
|
||||
need_change_password TINYINT(1) NOT NULL DEFAULT 1,
|
||||
password_changed_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
failed_count INT NOT NULL DEFAULT 0,
|
||||
locked_until DATETIME(3) NULL,
|
||||
last_login_at DATETIME(3) NULL,
|
||||
last_active_at DATETIME(3) NULL,
|
||||
create_time DATETIME(3) NOT NULL,
|
||||
update_time DATETIME(3) NOT NULL,
|
||||
UNIQUE KEY uk_tms_auth_user_account_username (username),
|
||||
KEY idx_tms_auth_user_account_role_code (role_code)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tms_auth_full_account (
|
||||
id BIGINT PRIMARY KEY,
|
||||
role_code VARCHAR(64) NOT NULL,
|
||||
@@ -439,110 +409,6 @@ VALUES
|
||||
CURRENT_TIMESTAMP(3)
|
||||
);
|
||||
|
||||
INSERT IGNORE INTO tms_auth_user_account (
|
||||
id,
|
||||
username,
|
||||
display_name,
|
||||
role_code,
|
||||
password_hash,
|
||||
password_salt,
|
||||
status,
|
||||
need_change_password,
|
||||
password_changed_at,
|
||||
failed_count,
|
||||
locked_until,
|
||||
last_login_at,
|
||||
last_active_at,
|
||||
create_time,
|
||||
update_time
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
12001,
|
||||
'super-admin-01',
|
||||
'超级管理员账号一',
|
||||
'SUPER_ADMIN',
|
||||
'Viuew8WE+qziUZwGGU0x25cUpzsTF+QvQFU0uhb+yVA=',
|
||||
'init-super-admin-salt-20260325',
|
||||
'ACTIVE',
|
||||
1,
|
||||
CURRENT_TIMESTAMP(3),
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
CURRENT_TIMESTAMP(3),
|
||||
CURRENT_TIMESTAMP(3)
|
||||
),
|
||||
(
|
||||
12002,
|
||||
'super-admin-02',
|
||||
'超级管理员账号二',
|
||||
'SUPER_ADMIN',
|
||||
'Viuew8WE+qziUZwGGU0x25cUpzsTF+QvQFU0uhb+yVA=',
|
||||
'init-super-admin-salt-20260325',
|
||||
'ACTIVE',
|
||||
1,
|
||||
CURRENT_TIMESTAMP(3),
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
CURRENT_TIMESTAMP(3),
|
||||
CURRENT_TIMESTAMP(3)
|
||||
),
|
||||
(
|
||||
12003,
|
||||
'key-admin-01',
|
||||
'密钥管理员账号',
|
||||
'KEY_ADMIN',
|
||||
'/xqmDy3A/q9X5p7XWznIPSBabQ6bxTLAPgtHQ6Be33c=',
|
||||
'init-key-admin-salt-20260325',
|
||||
'ACTIVE',
|
||||
1,
|
||||
CURRENT_TIMESTAMP(3),
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
CURRENT_TIMESTAMP(3),
|
||||
CURRENT_TIMESTAMP(3)
|
||||
),
|
||||
(
|
||||
12004,
|
||||
'audit-admin-01',
|
||||
'审计管理员账号',
|
||||
'AUDIT_ADMIN',
|
||||
'j/J1LKyUOzVEJfbbHe2dwKBKNdabRk502olk/LWIwhg=',
|
||||
'init-audit-admin-salt-20260325',
|
||||
'ACTIVE',
|
||||
1,
|
||||
CURRENT_TIMESTAMP(3),
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
CURRENT_TIMESTAMP(3),
|
||||
CURRENT_TIMESTAMP(3)
|
||||
),
|
||||
(
|
||||
12005,
|
||||
'ops-admin-01',
|
||||
'运维管理员账号',
|
||||
'OPS_ADMIN',
|
||||
'2e9F4aCgsDb+AuD1LrBEqEg8yjy3ODo27UHtx06vj/A=',
|
||||
'init-ops-admin-salt-20260325',
|
||||
'ACTIVE',
|
||||
1,
|
||||
CURRENT_TIMESTAMP(3),
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
CURRENT_TIMESTAMP(3),
|
||||
CURRENT_TIMESTAMP(3)
|
||||
);
|
||||
|
||||
INSERT IGNORE INTO tms_auth_full_account (
|
||||
id,
|
||||
role_code,
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
<?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">
|
||||
<mapper namespace="com.cisd.tms.modules.auth.mapper.AuthUserAccountMapper">
|
||||
|
||||
<resultMap id="AuthUserAccountResultMap" type="com.cisd.tms.modules.auth.entity.AuthUserAccountEntity">
|
||||
<id property="id" column="id"/>
|
||||
<result property="username" column="username"/>
|
||||
<result property="displayName" column="display_name"/>
|
||||
<result property="roleCode" column="role_code"/>
|
||||
<result property="passwordHash" column="password_hash"/>
|
||||
<result property="passwordSalt" column="password_salt"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="needChangePassword" column="need_change_password"/>
|
||||
<result property="passwordChangedAt" column="password_changed_at"/>
|
||||
<result property="failedCount" column="failed_count"/>
|
||||
<result property="lockedUntil" column="locked_until"/>
|
||||
<result property="lastLoginAt" column="last_login_at"/>
|
||||
<result property="lastActiveAt" column="last_active_at"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByUsername" parameterType="string" resultMap="AuthUserAccountResultMap">
|
||||
SELECT id,
|
||||
username,
|
||||
display_name,
|
||||
role_code,
|
||||
password_hash,
|
||||
password_salt,
|
||||
status,
|
||||
need_change_password,
|
||||
password_changed_at,
|
||||
failed_count,
|
||||
locked_until,
|
||||
last_login_at,
|
||||
last_active_at,
|
||||
create_time,
|
||||
update_time
|
||||
FROM tms_auth_user_account
|
||||
WHERE username = #{username}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<select id="selectByRoleCode" parameterType="string" resultMap="AuthUserAccountResultMap">
|
||||
SELECT id,
|
||||
username,
|
||||
display_name,
|
||||
role_code,
|
||||
password_hash,
|
||||
password_salt,
|
||||
status,
|
||||
need_change_password,
|
||||
password_changed_at,
|
||||
failed_count,
|
||||
locked_until,
|
||||
last_login_at,
|
||||
last_active_at,
|
||||
create_time,
|
||||
update_time
|
||||
FROM tms_auth_user_account
|
||||
WHERE role_code = #{roleCode}
|
||||
ORDER BY username ASC
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -1,23 +0,0 @@
|
||||
<?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">
|
||||
<mapper namespace="com.cisd.tms.modules.auth.mapper.AuthUserMapper">
|
||||
|
||||
<resultMap id="AuthUserResultMap" type="com.cisd.tms.modules.auth.entity.AuthUserEntity">
|
||||
<id property="id" column="id"/>
|
||||
<result property="username" column="username"/>
|
||||
<result property="displayName" column="display_name"/>
|
||||
<result property="role" column="role"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByUsername" parameterType="string" resultMap="AuthUserResultMap">
|
||||
SELECT id, username, display_name, role, create_time, update_time
|
||||
FROM tms_auth_user
|
||||
WHERE username = #{username}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user