fix:密码摘要
This commit is contained in:
parent
597fa4fe67
commit
a5c73c82b0
@ -7,11 +7,11 @@ import jakarta.validation.constraints.NotBlank;
|
||||
public class AdminChangePasswordRequest {
|
||||
|
||||
@NotBlank(message = "oldPassword不能为空")
|
||||
@Schema(description = "旧口令", example = "12345678")
|
||||
@Schema(description = "旧口令SM3摘要", example = "fc216e5eea029a7b5c267ab13cc2e0927a3810e66a2a09b86f8fdbd8d4aa647b")
|
||||
private String oldPassword;
|
||||
|
||||
@NotBlank(message = "newPassword不能为空")
|
||||
@Schema(description = "新口令", example = "Abc1234!")
|
||||
@Schema(description = "新口令SM3摘要", example = "fc216e5eea029a7b5c267ab13cc2e0927a3810e66a2a09b86f8fdbd8d4aa647b")
|
||||
private String newPassword;
|
||||
|
||||
public String getOldPassword() {
|
||||
|
||||
@ -9,11 +9,11 @@ public class ChangePasswordRequest {
|
||||
|
||||
@NotBlank(message = "oldPassword不能为空")
|
||||
@JsonAlias("currentPassword")
|
||||
@Schema(description = "旧口令", example = "12345678")
|
||||
@Schema(description = "旧口令SM3摘要", example = "fc216e5eea029a7b5c267ab13cc2e0927a3810e66a2a09b86f8fdbd8d4aa647b")
|
||||
private String oldPassword;
|
||||
|
||||
@NotBlank(message = "newPassword不能为空")
|
||||
@Schema(description = "新口令", example = "Abc1234!")
|
||||
@Schema(description = "新口令SM3摘要", example = "fc216e5eea029a7b5c267ab13cc2e0927a3810e66a2a09b86f8fdbd8d4aa647b")
|
||||
private String newPassword;
|
||||
|
||||
public String getOldPassword() {
|
||||
|
||||
@ -12,7 +12,7 @@ public class PasswordLoginAccountRequest {
|
||||
private Integer uid;
|
||||
|
||||
@NotBlank(message = "password不能为空")
|
||||
@Schema(description = "账号口令", example = "12345678")
|
||||
@Schema(description = "账号口令SM3摘要,字段名保持password兼容前端", example = "fc216e5eea029a7b5c267ab13cc2e0927a3810e66a2a09b86f8fdbd8d4aa647b")
|
||||
private String password;
|
||||
|
||||
public Integer getUid() {
|
||||
|
||||
@ -28,7 +28,7 @@ public class UkeyLoginProof {
|
||||
@NotBlank(message = "loginSignature不能为空")
|
||||
private String loginSignature;
|
||||
|
||||
@Schema(description = "当前 UKey 固定席位对应账号的口令,所有角色都必传", example = "12345678")
|
||||
@Schema(description = "当前 UKey 固定席位对应账号口令的SM3摘要,字段名保持password兼容前端", example = "fc216e5eea029a7b5c267ab13cc2e0927a3810e66a2a09b86f8fdbd8d4aa647b")
|
||||
private String password;
|
||||
|
||||
public String getPubKey() {
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package com.cisd.tms.modules.auth.service;
|
||||
|
||||
import com.cisd.tms.common.enums.ErrorCode;
|
||||
import com.cisd.tms.common.exception.BizException;
|
||||
|
||||
public final class PasswordDigestValidator {
|
||||
|
||||
public static final String MESSAGE = "密码摘要格式无效:password必须为64位SM3十六进制摘要";
|
||||
|
||||
private PasswordDigestValidator() {
|
||||
}
|
||||
|
||||
public static void validate(String passwordDigest) {
|
||||
String value = passwordDigest == null ? "" : passwordDigest.trim();
|
||||
if (value.length() != 64 || !value.matches("(?i)^[0-9a-f]{64}$")) {
|
||||
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,7 @@ import com.cisd.tms.modules.auth.repository.AuthFullAccountRepository;
|
||||
import com.cisd.tms.modules.auth.repository.RoleAccountRepository;
|
||||
import com.cisd.tms.modules.auth.repository.RoleUkeyBindingRepository;
|
||||
import com.cisd.tms.modules.auth.service.AuthAdminService;
|
||||
import com.cisd.tms.modules.auth.service.PasswordComplexityValidator;
|
||||
import com.cisd.tms.modules.auth.service.PasswordDigestValidator;
|
||||
import com.cisd.tms.modules.auth.service.PasswordHasher;
|
||||
import com.cisd.tms.modules.auth.service.PasswordSaltGenerator;
|
||||
import com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket;
|
||||
@ -43,6 +43,7 @@ import org.springframework.stereotype.Service;
|
||||
public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
|
||||
static final String DEFAULT_PASSWORD = "Sunyard@123";
|
||||
private static final String DEFAULT_PASSWORD_DIGEST = PasswordDigestSupport.sm3Hex(DEFAULT_PASSWORD);
|
||||
|
||||
private final RoleAccountRepository roleAccountRepository;
|
||||
private final AuthFullAccountRepository authFullAccountRepository;
|
||||
@ -106,7 +107,7 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
for (AuthFullAccountEntity account : fullAccounts) {
|
||||
String newSalt = passwordSaltGenerator.nextSalt();
|
||||
account.setPasswordSalt(newSalt);
|
||||
account.setPasswordHash(passwordHasher.hash(DEFAULT_PASSWORD, newSalt));
|
||||
account.setPasswordHash(passwordHasher.hash(DEFAULT_PASSWORD_DIGEST, newSalt));
|
||||
account.setStatus(RoleAccountStatus.ACTIVE.name());
|
||||
account.setNeedChangePassword(Boolean.TRUE);
|
||||
account.setPasswordChangedAt(current);
|
||||
@ -160,7 +161,8 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
String oldPassword,
|
||||
String newPassword
|
||||
) {
|
||||
PasswordComplexityValidator.validate(newPassword);
|
||||
PasswordDigestValidator.validate(oldPassword);
|
||||
PasswordDigestValidator.validate(newPassword);
|
||||
loadRole(targetRoleCode);
|
||||
RoleCode targetRole = resolveRoleCode(targetRoleCode);
|
||||
if (uid == null || uid < 1 || uid > targetRole.getRequiredUkeyCount()) {
|
||||
|
||||
@ -120,7 +120,7 @@ public class AuthSecurityResetServiceImpl implements AuthSecurityResetService {
|
||||
1,
|
||||
"super-admin-full-01",
|
||||
"超级管理员UKey席位一",
|
||||
"u70ZBltsrr8ncTyFgBkoziqvU2PfgpBYFqeoZUz4k6Y=",
|
||||
"vVPlH2UGQcXUo7NblMJMaof32um6UinF8zsckZuxe9E=",
|
||||
"3df85c2988a5de2b9eeaa8109639641a"
|
||||
),
|
||||
new SeedFullAccount(
|
||||
@ -129,7 +129,7 @@ public class AuthSecurityResetServiceImpl implements AuthSecurityResetService {
|
||||
2,
|
||||
"super-admin-full-02",
|
||||
"超级管理员UKey席位二",
|
||||
"A5GWgv2UmFc+r6O5fkdAZkuWXcF/c7B13mSivSf7vQg=",
|
||||
"25e83ejpUmjU64cYAKcDV5hhqClEu6aol8YyR3Jey7E=",
|
||||
"50de03346bb1f6afc74938acf5e88560"
|
||||
),
|
||||
new SeedFullAccount(
|
||||
@ -138,7 +138,7 @@ public class AuthSecurityResetServiceImpl implements AuthSecurityResetService {
|
||||
1,
|
||||
"key-admin-full-01",
|
||||
"密钥管理员UKey席位",
|
||||
"Lbhpr63UUBjRkRuXZKCMZhhinXXOz0LkvjiIJYcnvJs=",
|
||||
"l65Pr7MxOvqPOe0u6ozGGZcT4raTXUgDYMQfLTINTyE=",
|
||||
"073cd5fd30701edfcf033e8f501b2b43"
|
||||
),
|
||||
new SeedFullAccount(
|
||||
@ -147,7 +147,7 @@ public class AuthSecurityResetServiceImpl implements AuthSecurityResetService {
|
||||
1,
|
||||
"audit-admin-full-01",
|
||||
"审计管理员UKey席位",
|
||||
"SsqLWKlWRlxmWaxxBaxy6nSXfvu7McBfmXfOYQBXKE0=",
|
||||
"ZRwdn3TOotyXAytO4zeSpxRm/68Rm8VcB7w7VJmZPYc=",
|
||||
"57e2616077bd8af5cb90fc9790f69573"
|
||||
),
|
||||
new SeedFullAccount(
|
||||
@ -156,7 +156,7 @@ public class AuthSecurityResetServiceImpl implements AuthSecurityResetService {
|
||||
1,
|
||||
"ops-admin-full-01",
|
||||
"运维管理员UKey席位",
|
||||
"k0a3X9siC6Nyj72U/BavF8XSSXAAIXGKte+y6JgkCTc=",
|
||||
"5Wu8Mf5hC3kq4gm/62/T7pO+aZIw8Y7uYoMIztWyT1w=",
|
||||
"a6bea7601138e90a540b39c73d53df81"
|
||||
)
|
||||
);
|
||||
|
||||
@ -30,8 +30,8 @@ import com.cisd.tms.modules.auth.service.AuthPolicyService;
|
||||
import com.cisd.tms.modules.auth.service.AuthService;
|
||||
import com.cisd.tms.modules.auth.service.CaptchaService;
|
||||
import com.cisd.tms.modules.auth.service.CompatUkeyVerifier;
|
||||
import com.cisd.tms.modules.auth.service.PasswordDigestValidator;
|
||||
import com.cisd.tms.modules.auth.service.PasswordHasher;
|
||||
import com.cisd.tms.modules.auth.service.PasswordComplexityValidator;
|
||||
import com.cisd.tms.modules.auth.service.PasswordSaltGenerator;
|
||||
import com.cisd.tms.modules.auth.service.SessionTokenGenerator;
|
||||
import com.cisd.tms.modules.auth.service.UkeyLoginRandomService;
|
||||
@ -206,7 +206,8 @@ AuthServiceImpl implements AuthService {
|
||||
|
||||
@Override
|
||||
public void changeAccountPassword(String sessionToken, Integer uid, String currentPassword, String newPassword) {
|
||||
PasswordComplexityValidator.validate(newPassword);
|
||||
PasswordDigestValidator.validate(currentPassword);
|
||||
PasswordDigestValidator.validate(newPassword);
|
||||
AuthSessionEntity session = requireActiveSession(sessionToken);
|
||||
if (uid == null || !containsAuthenticatedUid(session, uid)) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "当前会话未完成账号认证");
|
||||
@ -265,6 +266,7 @@ AuthServiceImpl implements AuthService {
|
||||
.findByRoleCodeAndUid(roleAccount.getRoleCode(), uid)
|
||||
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "账号不存在"));
|
||||
validateFullAccountStatus(roleAccountSeat);
|
||||
PasswordDigestValidator.validate(account.getPassword());
|
||||
if (!passwordHasher.matches(account.getPassword(), roleAccountSeat.getPasswordSalt(), roleAccountSeat.getPasswordHash())) {
|
||||
onFullPasswordFailed(roleAccountSeat);
|
||||
}
|
||||
@ -336,6 +338,7 @@ AuthServiceImpl implements AuthService {
|
||||
.findByRoleCodeAndUid(roleAccount.getRoleCode(), uid)
|
||||
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "完整账号不存在"));
|
||||
validateFullAccountStatus(fullAccount);
|
||||
PasswordDigestValidator.validate(account.getPassword());
|
||||
if (!passwordHasher.matches(account.getPassword(), fullAccount.getPasswordSalt(), fullAccount.getPasswordHash())) {
|
||||
onFullPasswordFailed(fullAccount);
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
package com.cisd.tms.modules.auth.service.impl;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
final class PasswordDigestSupport {
|
||||
|
||||
private PasswordDigestSupport() {
|
||||
}
|
||||
|
||||
static String sm3Hex(String password) {
|
||||
byte[] input = (password == null ? "" : password).getBytes(StandardCharsets.UTF_8);
|
||||
SM3Digest digest = new SM3Digest();
|
||||
digest.update(input, 0, input.length);
|
||||
byte[] result = new byte[digest.getDigestSize()];
|
||||
digest.doFinal(result, 0);
|
||||
return Hex.toHexString(result);
|
||||
}
|
||||
}
|
||||
@ -388,7 +388,7 @@ VALUES
|
||||
1,
|
||||
'super-admin-full-01',
|
||||
'超级管理员UKey席位一',
|
||||
'u70ZBltsrr8ncTyFgBkoziqvU2PfgpBYFqeoZUz4k6Y=',
|
||||
'vVPlH2UGQcXUo7NblMJMaof32um6UinF8zsckZuxe9E=',
|
||||
'3df85c2988a5de2b9eeaa8109639641a',
|
||||
'ACTIVE',
|
||||
1,
|
||||
@ -406,7 +406,7 @@ VALUES
|
||||
2,
|
||||
'super-admin-full-02',
|
||||
'超级管理员UKey席位二',
|
||||
'A5GWgv2UmFc+r6O5fkdAZkuWXcF/c7B13mSivSf7vQg=',
|
||||
'25e83ejpUmjU64cYAKcDV5hhqClEu6aol8YyR3Jey7E=',
|
||||
'50de03346bb1f6afc74938acf5e88560',
|
||||
'ACTIVE',
|
||||
1,
|
||||
@ -424,7 +424,7 @@ VALUES
|
||||
1,
|
||||
'key-admin-full-01',
|
||||
'密钥管理员UKey席位',
|
||||
'Lbhpr63UUBjRkRuXZKCMZhhinXXOz0LkvjiIJYcnvJs=',
|
||||
'l65Pr7MxOvqPOe0u6ozGGZcT4raTXUgDYMQfLTINTyE=',
|
||||
'073cd5fd30701edfcf033e8f501b2b43',
|
||||
'ACTIVE',
|
||||
1,
|
||||
@ -442,7 +442,7 @@ VALUES
|
||||
1,
|
||||
'audit-admin-full-01',
|
||||
'审计管理员UKey席位',
|
||||
'SsqLWKlWRlxmWaxxBaxy6nSXfvu7McBfmXfOYQBXKE0=',
|
||||
'ZRwdn3TOotyXAytO4zeSpxRm/68Rm8VcB7w7VJmZPYc=',
|
||||
'57e2616077bd8af5cb90fc9790f69573',
|
||||
'ACTIVE',
|
||||
1,
|
||||
@ -460,7 +460,7 @@ VALUES
|
||||
1,
|
||||
'ops-admin-full-01',
|
||||
'运维管理员UKey席位',
|
||||
'k0a3X9siC6Nyj72U/BavF8XSSXAAIXGKte+y6JgkCTc=',
|
||||
'5Wu8Mf5hC3kq4gm/62/T7pO+aZIw8Y7uYoMIztWyT1w=',
|
||||
'a6bea7601138e90a540b39c73d53df81',
|
||||
'ACTIVE',
|
||||
1,
|
||||
@ -473,6 +473,47 @@ VALUES
|
||||
CURRENT_TIMESTAMP(3)
|
||||
);
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 兼容旧库默认口令摘要升级
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 旧版本默认账号保存的是 PBKDF2('Sunyard@123', password_salt)。
|
||||
-- 当前版本前端提交 password=SM3('Sunyard@123'),因此默认账号统一重置为
|
||||
-- 固定 salt + PBKDF2(SM3('Sunyard@123'), salt),便于旧库直接切换到摘要口径。
|
||||
UPDATE tms_auth_full_account
|
||||
SET password_hash = 'vVPlH2UGQcXUo7NblMJMaof32um6UinF8zsckZuxe9E=',
|
||||
password_salt = '3df85c2988a5de2b9eeaa8109639641a',
|
||||
update_time = CURRENT_TIMESTAMP(3)
|
||||
WHERE role_code = 'SUPER_ADMIN'
|
||||
AND uid = 1;
|
||||
|
||||
UPDATE tms_auth_full_account
|
||||
SET password_hash = '25e83ejpUmjU64cYAKcDV5hhqClEu6aol8YyR3Jey7E=',
|
||||
password_salt = '50de03346bb1f6afc74938acf5e88560',
|
||||
update_time = CURRENT_TIMESTAMP(3)
|
||||
WHERE role_code = 'SUPER_ADMIN'
|
||||
AND uid = 2;
|
||||
|
||||
UPDATE tms_auth_full_account
|
||||
SET password_hash = 'l65Pr7MxOvqPOe0u6ozGGZcT4raTXUgDYMQfLTINTyE=',
|
||||
password_salt = '073cd5fd30701edfcf033e8f501b2b43',
|
||||
update_time = CURRENT_TIMESTAMP(3)
|
||||
WHERE role_code = 'KEY_ADMIN'
|
||||
AND uid = 1;
|
||||
|
||||
UPDATE tms_auth_full_account
|
||||
SET password_hash = 'ZRwdn3TOotyXAytO4zeSpxRm/68Rm8VcB7w7VJmZPYc=',
|
||||
password_salt = '57e2616077bd8af5cb90fc9790f69573',
|
||||
update_time = CURRENT_TIMESTAMP(3)
|
||||
WHERE role_code = 'AUDIT_ADMIN'
|
||||
AND uid = 1;
|
||||
|
||||
UPDATE tms_auth_full_account
|
||||
SET password_hash = '5Wu8Mf5hC3kq4gm/62/T7pO+aZIw8Y7uYoMIztWyT1w=',
|
||||
password_salt = 'a6bea7601138e90a540b39c73d53df81',
|
||||
update_time = CURRENT_TIMESTAMP(3)
|
||||
WHERE role_code = 'OPS_ADMIN'
|
||||
AND uid = 1;
|
||||
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- IP白名单表
|
||||
|
||||
@ -34,6 +34,10 @@ import org.mockito.Mockito;
|
||||
class AuthAdminServiceTest {
|
||||
|
||||
private static final Clock FIXED_CLOCK = Clock.fixed(Instant.parse("2026-03-23T02:00:00Z"), ZoneOffset.UTC);
|
||||
private static final String DEFAULT_PASSWORD_DIGEST = "fc216e5eea029a7b5c267ab13cc2e0927a3810e66a2a09b86f8fdbd8d4aa647b";
|
||||
private static final String DIGEST_OLD1234 = "5237f20185252395f6ce351f8e742085d6ab1207ddda94b085b409e41026fc04";
|
||||
private static final String DIGEST_NEW1234 = "6b66c27d356b7fef3e2c2a986043031a410bb564205f22525ba022591671a7e5";
|
||||
private static final String DIGEST_BAD_PASSWORD = "17cb3cec9c337826293158dd5623c75268a2eee06123ba53b9aef5b790c04e51";
|
||||
|
||||
@Test
|
||||
void shouldResetOnlyUnifiedSeatPasswords() {
|
||||
@ -49,9 +53,9 @@ class AuthAdminServiceTest {
|
||||
AuthFullAccountEntity first = accounts.findByRoleCodeAndUid(RoleCode.SUPER_ADMIN.getCode(), 1).orElseThrow();
|
||||
AuthFullAccountEntity second = accounts.findByRoleCodeAndUid(RoleCode.SUPER_ADMIN.getCode(), 2).orElseThrow();
|
||||
Assertions.assertEquals("salt-reset-1", first.getPasswordSalt());
|
||||
Assertions.assertEquals("HASH:Sunyard@123:salt-reset-1", first.getPasswordHash());
|
||||
Assertions.assertEquals("HASH:" + DEFAULT_PASSWORD_DIGEST + ":salt-reset-1", first.getPasswordHash());
|
||||
Assertions.assertEquals("salt-reset-2", second.getPasswordSalt());
|
||||
Assertions.assertEquals("HASH:Sunyard@123:salt-reset-2", second.getPasswordHash());
|
||||
Assertions.assertEquals("HASH:" + DEFAULT_PASSWORD_DIGEST + ":salt-reset-2", second.getPasswordHash());
|
||||
Assertions.assertTrue(Boolean.TRUE.equals(first.getNeedChangePassword()));
|
||||
Assertions.assertTrue(Boolean.TRUE.equals(second.getNeedChangePassword()));
|
||||
}
|
||||
@ -61,7 +65,7 @@ class AuthAdminServiceTest {
|
||||
InMemoryRoleAccountRepository roles = new InMemoryRoleAccountRepository();
|
||||
InMemoryAuthFullAccountRepository accounts = new InMemoryAuthFullAccountRepository();
|
||||
roles.save(role(RoleCode.KEY_ADMIN, RoleAccountStatus.ACTIVE));
|
||||
accounts.save(account(RoleCode.KEY_ADMIN, 1, "key-admin-01", "HASH:Old1234!:SALT-OLD", "SALT-OLD"));
|
||||
accounts.save(account(RoleCode.KEY_ADMIN, 1, "key-admin-01", "HASH:" + DIGEST_OLD1234 + ":SALT-OLD", "SALT-OLD"));
|
||||
|
||||
service(roles, accounts, new InMemoryRoleUkeyBindingRepository(), Mockito.mock(LmkService.class))
|
||||
.changeAccountPassword(
|
||||
@ -69,13 +73,13 @@ class AuthAdminServiceTest {
|
||||
AuthLevel.FULL.name(),
|
||||
RoleCode.KEY_ADMIN.getCode(),
|
||||
1,
|
||||
"Old1234!",
|
||||
"New1234!"
|
||||
DIGEST_OLD1234,
|
||||
DIGEST_NEW1234
|
||||
);
|
||||
|
||||
AuthFullAccountEntity changed = accounts.findByRoleCodeAndUid(RoleCode.KEY_ADMIN.getCode(), 1).orElseThrow();
|
||||
Assertions.assertEquals("salt-reset-1", changed.getPasswordSalt());
|
||||
Assertions.assertEquals("HASH:New1234!:salt-reset-1", changed.getPasswordHash());
|
||||
Assertions.assertEquals("HASH:" + DIGEST_NEW1234 + ":salt-reset-1", changed.getPasswordHash());
|
||||
Assertions.assertTrue(Boolean.TRUE.equals(changed.getNeedChangePassword()));
|
||||
}
|
||||
|
||||
@ -84,7 +88,7 @@ class AuthAdminServiceTest {
|
||||
InMemoryRoleAccountRepository roles = new InMemoryRoleAccountRepository();
|
||||
InMemoryAuthFullAccountRepository accounts = new InMemoryAuthFullAccountRepository();
|
||||
roles.save(role(RoleCode.KEY_ADMIN, RoleAccountStatus.ACTIVE));
|
||||
accounts.save(account(RoleCode.KEY_ADMIN, 1, "key-admin-01", "HASH:Old1234!:SALT-OLD", "SALT-OLD"));
|
||||
accounts.save(account(RoleCode.KEY_ADMIN, 1, "key-admin-01", "HASH:" + DIGEST_OLD1234 + ":SALT-OLD", "SALT-OLD"));
|
||||
|
||||
BizException exception = Assertions.assertThrows(BizException.class,
|
||||
() -> service(roles, accounts, new InMemoryRoleUkeyBindingRepository(), Mockito.mock(LmkService.class))
|
||||
@ -93,8 +97,8 @@ class AuthAdminServiceTest {
|
||||
AuthLevel.FULL.name(),
|
||||
RoleCode.KEY_ADMIN.getCode(),
|
||||
1,
|
||||
"bad-password",
|
||||
"New1234!"
|
||||
DIGEST_BAD_PASSWORD,
|
||||
DIGEST_NEW1234
|
||||
));
|
||||
|
||||
Assertions.assertEquals(ErrorCode.UNAUTHORIZED.getCode(), exception.getCode());
|
||||
|
||||
@ -22,6 +22,7 @@ import org.mockito.Mockito;
|
||||
class AuthSecurityResetServiceTest {
|
||||
|
||||
private static final Clock FIXED_CLOCK = Clock.fixed(Instant.parse("2026-05-13T08:00:00Z"), ZoneOffset.UTC);
|
||||
private static final String FIRST_SUPER_ADMIN_PASSWORD_HASH = "vVPlH2UGQcXUo7NblMJMaof32um6UinF8zsckZuxe9E=";
|
||||
|
||||
@Test
|
||||
void shouldClearBindingsSessionsAndRestoreSeededRolesAndPasswords() {
|
||||
@ -78,7 +79,7 @@ class AuthSecurityResetServiceTest {
|
||||
Assertions.assertEquals("super-admin-full-01", firstSuperAdmin.getAccountName());
|
||||
Assertions.assertEquals("超级管理员UKey席位一", firstSuperAdmin.getDisplayName());
|
||||
Assertions.assertEquals("3df85c2988a5de2b9eeaa8109639641a", firstSuperAdmin.getPasswordSalt());
|
||||
Assertions.assertEquals("u70ZBltsrr8ncTyFgBkoziqvU2PfgpBYFqeoZUz4k6Y=", firstSuperAdmin.getPasswordHash());
|
||||
Assertions.assertEquals(FIRST_SUPER_ADMIN_PASSWORD_HASH, firstSuperAdmin.getPasswordHash());
|
||||
Assertions.assertEquals(RoleAccountStatus.ACTIVE.name(), firstSuperAdmin.getStatus());
|
||||
Assertions.assertTrue(Boolean.TRUE.equals(firstSuperAdmin.getNeedChangePassword()));
|
||||
Assertions.assertEquals(0, firstSuperAdmin.getFailedCount());
|
||||
|
||||
@ -42,6 +42,13 @@ import org.junit.jupiter.api.Test;
|
||||
class AuthServiceTest {
|
||||
|
||||
private static final Clock FIXED_CLOCK = Clock.fixed(Instant.parse("2026-03-23T02:00:00Z"), ZoneOffset.UTC);
|
||||
private static final String DIGEST_12345678 = "0fffff81e971fa3f09107abf77931463fc0710bfb8962efeae3d5654b073bb0c";
|
||||
private static final String DIGEST_ABC1234 = "3add0ab5dec629efbc3a741c4063f7c82d8919b3de749a3f74b91b704aa2fd9e";
|
||||
private static final String DIGEST_OLD1234 = "5237f20185252395f6ce351f8e742085d6ab1207ddda94b085b409e41026fc04";
|
||||
private static final String DIGEST_NEW1234 = "6b66c27d356b7fef3e2c2a986043031a410bb564205f22525ba022591671a7e5";
|
||||
private static final String DIGEST_11111111 = "f5fb72062ab1d6ddc0bfecb87dc32bec6e773f585e1cbbf93de394081c2e0f37";
|
||||
private static final String DIGEST_22222222 = "377fc0693a9ed9de44bd8c8aa9d166887896f329f39f028eaf23bca7fd9f3b57";
|
||||
private static final String DIGEST_BAD_PASSWORD = "17cb3cec9c337826293158dd5623c75268a2eee06123ba53b9aef5b790c04e51";
|
||||
|
||||
@Test
|
||||
void shouldCreateLimitedSessionFromUnifiedRoleSeatPassword() {
|
||||
@ -49,12 +56,12 @@ class AuthServiceTest {
|
||||
InMemoryAuthFullAccountRepository accounts = new InMemoryAuthFullAccountRepository();
|
||||
InMemoryAuthSessionRepository sessions = new InMemoryAuthSessionRepository();
|
||||
roles.save(activeRole(RoleCode.AUDIT_ADMIN));
|
||||
accounts.save(activeAccount(RoleCode.AUDIT_ADMIN, 1, "audit-admin-01", "12345678", "SALT-A", true));
|
||||
accounts.save(activeAccount(RoleCode.AUDIT_ADMIN, 1, "audit-admin-01", DIGEST_12345678, "SALT-A", true));
|
||||
|
||||
AuthService service = newAuthService(roles, accounts, sessions, new InMemoryRoleUkeyBindingRepository(), () -> "token-limited-001");
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setRoleCode(RoleCode.AUDIT_ADMIN.getCode());
|
||||
request.setAccounts(List.of(passwordAccount(1, "12345678")));
|
||||
request.setAccounts(List.of(passwordAccount(1, DIGEST_12345678)));
|
||||
|
||||
LoginResponse response = service.login(request);
|
||||
|
||||
@ -74,7 +81,7 @@ class AuthServiceTest {
|
||||
InMemoryAuthSessionRepository sessions = new InMemoryAuthSessionRepository();
|
||||
InMemoryRoleUkeyBindingRepository bindings = new InMemoryRoleUkeyBindingRepository();
|
||||
roles.save(activeRole(RoleCode.KEY_ADMIN));
|
||||
accounts.save(activeAccount(RoleCode.KEY_ADMIN, 1, "key-admin-01", "Abc1234!", "SALT-K", false));
|
||||
accounts.save(activeAccount(RoleCode.KEY_ADMIN, 1, "key-admin-01", DIGEST_ABC1234, "SALT-K", false));
|
||||
bindings.save(activeBinding(RoleCode.KEY_ADMIN, 1, "UK-1", "PUB-1"));
|
||||
|
||||
LmkService lmkService = org.mockito.Mockito.mock(LmkService.class);
|
||||
@ -87,12 +94,12 @@ class AuthServiceTest {
|
||||
|
||||
LoginRequest passwordRequest = new LoginRequest();
|
||||
passwordRequest.setRoleCode(RoleCode.KEY_ADMIN.getCode());
|
||||
passwordRequest.setAccounts(List.of(passwordAccount(1, "Abc1234!")));
|
||||
passwordRequest.setAccounts(List.of(passwordAccount(1, DIGEST_ABC1234)));
|
||||
Assertions.assertEquals(AuthLevel.LIMITED.name(), service.login(passwordRequest).getAuthLevel());
|
||||
|
||||
UkeyLoginRequest ukeyRequest = new UkeyLoginRequest();
|
||||
ukeyRequest.setRoleCode(RoleCode.KEY_ADMIN.getCode());
|
||||
ukeyRequest.setLoginFactors(List.of(proofWithPassword("PUB-1", 1, "Abc1234!", "RB-1", "ISSUE-1", "LOGIN-DATA-1", "LOGIN-SIGN-1")));
|
||||
ukeyRequest.setLoginFactors(List.of(proofWithPassword("PUB-1", 1, DIGEST_ABC1234, "RB-1", "ISSUE-1", "LOGIN-DATA-1", "LOGIN-SIGN-1")));
|
||||
LoginResponse response = service.ukeyLogin(ukeyRequest);
|
||||
|
||||
Assertions.assertEquals(AuthLevel.FULL.name(), response.getAuthLevel());
|
||||
@ -109,7 +116,7 @@ class AuthServiceTest {
|
||||
InMemoryAuthSessionRepository sessions = new InMemoryAuthSessionRepository();
|
||||
InMemoryRoleUkeyBindingRepository bindings = new InMemoryRoleUkeyBindingRepository();
|
||||
roles.save(activeRole(RoleCode.KEY_ADMIN));
|
||||
accounts.save(activeAccount(RoleCode.KEY_ADMIN, 1, "key-admin-01", "Old1234!", "SALT-K", true));
|
||||
accounts.save(activeAccount(RoleCode.KEY_ADMIN, 1, "key-admin-01", DIGEST_OLD1234, "SALT-K", true));
|
||||
bindings.save(activeBinding(RoleCode.KEY_ADMIN, 1, "UK-1", "PUB-1"));
|
||||
AuthSessionEntity session = session("token-change-001", RoleCode.KEY_ADMIN.getCode(), AuthLevel.LIMITED.name());
|
||||
session.setAuthenticatedPrincipalsJson("[{\"type\":\"LIMITED\",\"uid\":1,\"username\":\"key-admin-01\"}]");
|
||||
@ -117,11 +124,11 @@ class AuthServiceTest {
|
||||
|
||||
AuthService service = newAuthService(roles, accounts, sessions, bindings, () -> "token-after-change");
|
||||
|
||||
service.changeAccountPassword("token-change-001", 1, "Old1234!", "New1234!");
|
||||
service.changeAccountPassword("token-change-001", 1, DIGEST_OLD1234, DIGEST_NEW1234);
|
||||
|
||||
AuthFullAccountEntity changed = accounts.findByRoleCodeAndUid(RoleCode.KEY_ADMIN.getCode(), 1).orElseThrow();
|
||||
Assertions.assertEquals("salt-test", changed.getPasswordSalt());
|
||||
Assertions.assertEquals("HASH:New1234!:salt-test", changed.getPasswordHash());
|
||||
Assertions.assertEquals("HASH:" + DIGEST_NEW1234 + ":salt-test", changed.getPasswordHash());
|
||||
Assertions.assertFalse(Boolean.TRUE.equals(changed.getNeedChangePassword()));
|
||||
}
|
||||
|
||||
@ -130,13 +137,13 @@ class AuthServiceTest {
|
||||
InMemoryRoleAccountRepository roles = new InMemoryRoleAccountRepository();
|
||||
InMemoryAuthFullAccountRepository accounts = new InMemoryAuthFullAccountRepository();
|
||||
roles.save(activeRole(RoleCode.SUPER_ADMIN));
|
||||
accounts.save(activeAccount(RoleCode.SUPER_ADMIN, 1, "super-admin-01", "11111111", "SALT-S1", 4, null));
|
||||
accounts.save(activeAccount(RoleCode.SUPER_ADMIN, 2, "super-admin-02", "22222222", "SALT-S2", 0, null));
|
||||
accounts.save(activeAccount(RoleCode.SUPER_ADMIN, 1, "super-admin-01", DIGEST_11111111, "SALT-S1", 4, null));
|
||||
accounts.save(activeAccount(RoleCode.SUPER_ADMIN, 2, "super-admin-02", DIGEST_22222222, "SALT-S2", 0, null));
|
||||
|
||||
AuthService service = newAuthService(roles, accounts, new InMemoryAuthSessionRepository(), new InMemoryRoleUkeyBindingRepository(), () -> "unused");
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setRoleCode(RoleCode.SUPER_ADMIN.getCode());
|
||||
request.setAccounts(List.of(passwordAccount(1, "bad-password"), passwordAccount(2, "22222222")));
|
||||
request.setAccounts(List.of(passwordAccount(1, DIGEST_BAD_PASSWORD), passwordAccount(2, DIGEST_22222222)));
|
||||
|
||||
BizException exception = Assertions.assertThrows(BizException.class, () -> service.login(request));
|
||||
|
||||
@ -154,7 +161,7 @@ class AuthServiceTest {
|
||||
InMemoryAuthFullAccountRepository accounts = new InMemoryAuthFullAccountRepository();
|
||||
InMemoryAuthSessionRepository sessions = new InMemoryAuthSessionRepository();
|
||||
roles.save(activeRole(RoleCode.AUDIT_ADMIN));
|
||||
AuthFullAccountEntity account = activeAccount(RoleCode.AUDIT_ADMIN, 1, "audit-admin-01", "12345678", "SALT-A", false);
|
||||
AuthFullAccountEntity account = activeAccount(RoleCode.AUDIT_ADMIN, 1, "audit-admin-01", DIGEST_12345678, "SALT-A", false);
|
||||
account.setPasswordChangedAt(LocalDateTime.of(2026, 2, 21, 2, 0));
|
||||
accounts.save(account);
|
||||
AuthSessionEntity session = session("token-me-001", RoleCode.AUDIT_ADMIN.getCode(), AuthLevel.LIMITED.name());
|
||||
@ -174,8 +181,8 @@ class AuthServiceTest {
|
||||
InMemoryAuthFullAccountRepository accounts = new InMemoryAuthFullAccountRepository();
|
||||
InMemoryRoleUkeyBindingRepository bindings = new InMemoryRoleUkeyBindingRepository();
|
||||
roles.save(activeRole(RoleCode.SUPER_ADMIN));
|
||||
accounts.save(activeAccount(RoleCode.SUPER_ADMIN, 1, "super-admin-01", "11111111", "SALT-S1", false));
|
||||
accounts.save(activeAccount(RoleCode.SUPER_ADMIN, 2, "super-admin-02", "22222222", "SALT-S2", false));
|
||||
accounts.save(activeAccount(RoleCode.SUPER_ADMIN, 1, "super-admin-01", DIGEST_11111111, "SALT-S1", false));
|
||||
accounts.save(activeAccount(RoleCode.SUPER_ADMIN, 2, "super-admin-02", DIGEST_22222222, "SALT-S2", false));
|
||||
bindings.save(activeBinding(RoleCode.SUPER_ADMIN, 1, "UK-1", "PUB-1"));
|
||||
bindings.save(activeBinding(RoleCode.SUPER_ADMIN, 1, "UK-1-BACKUP", "PUB-1-BACKUP"));
|
||||
bindings.save(activeBinding(RoleCode.SUPER_ADMIN, 2, "UK-2", "PUB-2"));
|
||||
@ -191,8 +198,8 @@ class AuthServiceTest {
|
||||
UkeyLoginRequest request = new UkeyLoginRequest();
|
||||
request.setRoleCode(RoleCode.SUPER_ADMIN.getCode());
|
||||
request.setLoginFactors(List.of(
|
||||
proofWithPassword("PUB-1-BACKUP", 1, "11111111", "RB-1", "ISSUE-1B", "LOGIN-DATA-1B", "LOGIN-SIGN-1B"),
|
||||
proofWithPassword("PUB-2-BACKUP", 2, "22222222", "RB-2", "ISSUE-2B", "LOGIN-DATA-2B", "LOGIN-SIGN-2B")
|
||||
proofWithPassword("PUB-1-BACKUP", 1, DIGEST_11111111, "RB-1", "ISSUE-1B", "LOGIN-DATA-1B", "LOGIN-SIGN-1B"),
|
||||
proofWithPassword("PUB-2-BACKUP", 2, DIGEST_22222222, "RB-2", "ISSUE-2B", "LOGIN-DATA-2B", "LOGIN-SIGN-2B")
|
||||
));
|
||||
|
||||
LoginResponse response = service.ukeyLogin(request);
|
||||
@ -208,7 +215,7 @@ class AuthServiceTest {
|
||||
InMemoryAuthFullAccountRepository accounts = new InMemoryAuthFullAccountRepository();
|
||||
InMemoryAuthSessionRepository sessions = new InMemoryAuthSessionRepository();
|
||||
roles.save(activeRole(RoleCode.KEY_ADMIN));
|
||||
accounts.save(activeAccount(RoleCode.KEY_ADMIN, 1, "key-admin-01", "12345678", "SALT-K", false));
|
||||
accounts.save(activeAccount(RoleCode.KEY_ADMIN, 1, "key-admin-01", DIGEST_12345678, "SALT-K", false));
|
||||
AuthSessionEntity expired = session("token-expired-001", RoleCode.KEY_ADMIN.getCode(), AuthLevel.FULL.name());
|
||||
expired.setAuthenticatedPrincipalsJson("[{\"type\":\"FULL\",\"uid\":1,\"username\":\"key-admin-01\"}]");
|
||||
expired.setExpiresAt(LocalDateTime.of(2026, 3, 23, 1, 59));
|
||||
@ -216,7 +223,7 @@ class AuthServiceTest {
|
||||
|
||||
BizException exception = Assertions.assertThrows(BizException.class,
|
||||
() -> newAuthService(roles, accounts, sessions, new InMemoryRoleUkeyBindingRepository(), () -> "unused")
|
||||
.changeAccountPassword("token-expired-001", 1, "12345678", "Abc1234!"));
|
||||
.changeAccountPassword("token-expired-001", 1, DIGEST_12345678, DIGEST_ABC1234));
|
||||
|
||||
Assertions.assertEquals(ErrorCode.SESSION_INVALID.getCode(), exception.getCode());
|
||||
Assertions.assertEquals("会话已过期", exception.getMessage());
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
package com.cisd.tms.modules.auth.service;
|
||||
|
||||
import com.cisd.tms.common.exception.BizException;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class PasswordDigestValidatorTest {
|
||||
|
||||
@Test
|
||||
void shouldAcceptSm3HexDigest() {
|
||||
Assertions.assertDoesNotThrow(() -> PasswordDigestValidator.validate(
|
||||
"fc216e5eea029a7b5c267ab13cc2e0927a3810e66a2a09b86f8fdbd8d4aa647b"
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectNonDigestPasswordInput() {
|
||||
assertInvalid("Sunyard@123");
|
||||
assertInvalid("12345678");
|
||||
assertInvalid("fc216e5eea029a7b5c267ab13cc2e0927a3810e66a2a09b86f8fdbd8d4aa647");
|
||||
assertInvalid("zc216e5eea029a7b5c267ab13cc2e0927a3810e66a2a09b86f8fdbd8d4aa647b");
|
||||
}
|
||||
|
||||
private void assertInvalid(String value) {
|
||||
BizException exception = Assertions.assertThrows(BizException.class, () -> PasswordDigestValidator.validate(value));
|
||||
Assertions.assertEquals(PasswordDigestValidator.MESSAGE, exception.getMessage());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user