diff --git a/src/main/java/com/cisd/tms/modules/auth/service/PasswordComplexityValidator.java b/src/main/java/com/cisd/tms/modules/auth/service/PasswordComplexityValidator.java deleted file mode 100644 index 64bb296..0000000 --- a/src/main/java/com/cisd/tms/modules/auth/service/PasswordComplexityValidator.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.cisd.tms.modules.auth.service; - -import com.cisd.tms.common.enums.ErrorCode; -import com.cisd.tms.common.exception.BizException; - -public final class PasswordComplexityValidator { - - public static final String MESSAGE = "密码复杂度不符合要求:需由8到16位大小写字母、数字、符号组成,且三类均需包含"; - - private PasswordComplexityValidator() { - } - - public static void validate(String password) { - String value = password == null ? "" : password; - if (value.isBlank() - || value.length() < 8 - || value.length() > 16 - || !isAllowedCharacters(value) - || !containsUppercase(value) - || !containsLowercase(value) - || !containsDigit(value) - || !containsSymbol(value)) { - throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), MESSAGE); - } - } - - private static boolean isAllowedCharacters(String value) { - return value.codePoints().allMatch(codePoint -> - isAsciiLetter(codePoint) || isAsciiDigit(codePoint) || isSymbol(codePoint)); - } - - private static boolean containsUppercase(String value) { - return value.codePoints().anyMatch(codePoint -> codePoint >= 'A' && codePoint <= 'Z'); - } - - private static boolean containsLowercase(String value) { - return value.codePoints().anyMatch(codePoint -> codePoint >= 'a' && codePoint <= 'z'); - } - - private static boolean containsDigit(String value) { - return value.codePoints().anyMatch(PasswordComplexityValidator::isAsciiDigit); - } - - private static boolean containsSymbol(String value) { - return value.codePoints().anyMatch(PasswordComplexityValidator::isSymbol); - } - - private static boolean isAsciiLetter(int codePoint) { - return (codePoint >= 'A' && codePoint <= 'Z') || (codePoint >= 'a' && codePoint <= 'z'); - } - - private static boolean isAsciiDigit(int codePoint) { - return codePoint >= '0' && codePoint <= '9'; - } - - private static boolean isSymbol(int codePoint) { - return codePoint >= 33 && codePoint <= 126 - && !isAsciiDigit(codePoint) - && !isAsciiLetter(codePoint); - } -} diff --git a/src/test/java/com/cisd/tms/TmsApplicationTests.java b/src/test/java/com/cisd/tms/TmsApplicationTests.java index d960a13..9178d58 100644 --- a/src/test/java/com/cisd/tms/TmsApplicationTests.java +++ b/src/test/java/com/cisd/tms/TmsApplicationTests.java @@ -19,6 +19,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @AutoConfigureMockMvc class TmsApplicationTests { + private static final String DEFAULT_PASSWORD_DIGEST = + "fc216e5eea029a7b5c267ab13cc2e0927a3810e66a2a09b86f8fdbd8d4aa647b"; + private static final Pattern AUTH_FULL_ACCOUNT_ROW = Pattern.compile( "\\(\\s*1210[1-5],.*?'([^']+)'\\s*,\\s*'([0-9a-f]{32})'", Pattern.DOTALL @@ -115,7 +118,7 @@ class TmsApplicationTests { String hash = matcher.group(1); String salt = matcher.group(2); org.junit.jupiter.api.Assertions.assertTrue(salts.add(salt), "duplicate salt: " + salt); - org.junit.jupiter.api.Assertions.assertTrue(hasher.matches("Sunyard@123", salt, hash), "hash mismatch for salt: " + salt); + org.junit.jupiter.api.Assertions.assertTrue(hasher.matches(DEFAULT_PASSWORD_DIGEST, salt, hash), "hash mismatch for salt: " + salt); } org.junit.jupiter.api.Assertions.assertEquals(5, rows); } diff --git a/src/test/java/com/cisd/tms/modules/auth/controller/AuthControllerTest.java b/src/test/java/com/cisd/tms/modules/auth/controller/AuthControllerTest.java index e51d071..e5160d7 100644 --- a/src/test/java/com/cisd/tms/modules/auth/controller/AuthControllerTest.java +++ b/src/test/java/com/cisd/tms/modules/auth/controller/AuthControllerTest.java @@ -28,6 +28,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. class AuthControllerTest { + private static final String DIGEST_12345678 = + "0fffff81e971fa3f09107abf77931463fc0710bfb8962efeae3d5654b073bb0c"; + private static final String DIGEST_ABC1234 = + "6b66c27d356b7fef3e2c2a986043031a410bb564205f22525ba022591671a7e5"; + @Test void shouldLoginWithPasswordPayload() throws Exception { AuthService authService = Mockito.mock(AuthService.class); @@ -51,13 +56,13 @@ class AuthControllerTest { "accounts": [ { "uid": 1, - "password": "12345678" + "password": "%s" } ], "captchaCode": "ABCD", "captchaId": "captcha-001" } - """)) + """.formatted(DIGEST_12345678))) .andExpect(status().isOk()) .andExpect(content().string(containsString("\"token\":\"token-password-001\""))) .andExpect(content().string(containsString("\"authLevel\":\"LIMITED\""))); @@ -113,7 +118,7 @@ class AuthControllerTest { { "pubKey": "PUB-1", "uid": 1, - "password": "11111111", + "password": "%s", "serverRandom": "RB-1", "issueSignature": "ISSUE-1", "loginPayload": "LOGIN-DATA-1", @@ -122,7 +127,7 @@ class AuthControllerTest { { "pubKey": "PUB-2", "uid": 2, - "password": "22222222", + "password": "%s", "serverRandom": "RB-2", "issueSignature": "ISSUE-2", "loginPayload": "LOGIN-DATA-2", @@ -130,7 +135,7 @@ class AuthControllerTest { } ] } - """)) + """.formatted(DIGEST_12345678, DIGEST_ABC1234))) .andExpect(status().isOk()) .andExpect(content().string(containsString("\"token\":\"token-ukey-001\""))) .andExpect(content().string(containsString("\"authLevel\":\"FULL\""))); @@ -215,14 +220,14 @@ class AuthControllerTest { .contentType(MediaType.APPLICATION_JSON) .content(""" { - "oldPassword": "12345678", - "newPassword": "Abc1234!" + "oldPassword": "%s", + "newPassword": "%s" } - """)) + """.formatted(DIGEST_12345678, DIGEST_ABC1234))) .andExpect(status().isOk()) .andExpect(content().string(containsString("\"success\":true"))); - Mockito.verify(authService).changeAccountPassword("token-change-001", 1, "12345678", "Abc1234!"); + Mockito.verify(authService).changeAccountPassword("token-change-001", 1, DIGEST_12345678, DIGEST_ABC1234); } @Test @@ -241,14 +246,14 @@ class AuthControllerTest { .contentType(MediaType.APPLICATION_JSON) .content(""" { - "oldPassword": "12345678", - "newPassword": "Abc1234!" + "oldPassword": "%s", + "newPassword": "%s" } - """)) + """.formatted(DIGEST_12345678, DIGEST_ABC1234))) .andExpect(status().isOk()) .andExpect(content().string(containsString("\"success\":true"))); - Mockito.verify(authAdminService).changeAccountPassword("SUPER_ADMIN", "FULL", "KEY_ADMIN", 1, "12345678", "Abc1234!"); + Mockito.verify(authAdminService).changeAccountPassword("SUPER_ADMIN", "FULL", "KEY_ADMIN", 1, DIGEST_12345678, DIGEST_ABC1234); } @Test diff --git a/src/test/java/com/cisd/tms/modules/auth/service/PasswordComplexityValidatorTest.java b/src/test/java/com/cisd/tms/modules/auth/service/PasswordComplexityValidatorTest.java deleted file mode 100644 index 48c13b4..0000000 --- a/src/test/java/com/cisd/tms/modules/auth/service/PasswordComplexityValidatorTest.java +++ /dev/null @@ -1,38 +0,0 @@ -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 PasswordComplexityValidatorTest { - - @Test - void shouldAcceptPasswordWithLettersDigitsAndSymbolInAllowedLength() { - Assertions.assertDoesNotThrow(() -> PasswordComplexityValidator.validate("Abc1234!")); - Assertions.assertDoesNotThrow(() -> PasswordComplexityValidator.validate("Aa123456789012!")); - } - - @Test - void shouldRejectPasswordOutsideNewComplexityPolicy() { - assertInvalid(null); - assertInvalid(""); - assertInvalid(" "); - assertInvalid("Abc123!"); - assertInvalid("Abc1234567890123!"); - assertInvalid("abc1234!"); - assertInvalid("ABC1234!"); - assertInvalid("Abcdefg!"); - assertInvalid("Abc12345"); - assertInvalid("中Abc1234!"); - assertInvalid("Abc1234!"); - assertInvalid("Abc 123!"); - } - - private void assertInvalid(String password) { - BizException exception = Assertions.assertThrows( - BizException.class, - () -> PasswordComplexityValidator.validate(password) - ); - Assertions.assertEquals(PasswordComplexityValidator.MESSAGE, exception.getMessage()); - } -}