Use Argon2 password encoder as default to remove password limit (#7407)

#### What type of PR is this?

/kind bug
/area core
/milestone 2.20.x

#### What this PR does / why we need it:

This PR makes Argon2 password encoder as default to remove password limit of 72.

Please note that there is no compatibility issue for old passwords.

#### Which issue(s) this PR fixes:

Fixes #7405 

#### Special notes for your reviewer:

1. Try to login as admin
2. Create a password having the length of 73 or more for a new user
3. See the result

#### Does this PR introduce a user-facing change?

```release-note
修复无法设置长度超过72个字符的密码的问题
```
pull/7413/head
John Niang 2025-05-06 17:19:36 +08:00 committed by GitHub
parent 0676551c77
commit 5a6f1ef641
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 26 additions and 2 deletions

View File

@ -50,6 +50,7 @@ dependencies {
api "org.springdoc:springdoc-openapi-starter-webflux-ui" api "org.springdoc:springdoc-openapi-starter-webflux-ui"
api 'org.openapi4j:openapi-schema-validator' api 'org.openapi4j:openapi-schema-validator'
api "net.bytebuddy:byte-buddy" api "net.bytebuddy:byte-buddy"
api "org.bouncycastle:bcpkix-jdk18on"
// Apache Lucene // Apache Lucene
api "org.apache.lucene:lucene-core" api "org.apache.lucene:lucene-core"

View File

@ -2,6 +2,7 @@ package run.halo.app.infra.config;
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers; import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
@ -13,7 +14,9 @@ import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.context.ServerSecurityContextRepository; import org.springframework.security.web.server.context.ServerSecurityContextRepository;
@ -136,8 +139,17 @@ public class WebServerSecurityConfig {
} }
@Bean @Bean
@SuppressWarnings("deprecation")
PasswordEncoder passwordEncoder() { PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder(); // For removing the length limit of password, we have to create an argon2 password encoder
// as default encoder.
// When https://github.com/spring-projects/spring-security/issues/16879 resolved,
// we can remove this code.
var encodingId = "argon2@SpringSecurity_v5_8";
var encoders = new HashMap<String, PasswordEncoder>();
encoders.put(encodingId, Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
encoders.put("bcrypt", new BCryptPasswordEncoder());
return new DelegatingPasswordEncoder(encodingId, encoders);
} }
@Bean @Bean

View File

@ -1,13 +1,16 @@
package run.halo.app.config; package run.halo.app.config;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY; import static org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest @SpringBootTest
@ -36,4 +39,10 @@ class SecurityConfigTest {
hsts -> assertFalse(hsts.contains("includeSubDomains"))); hsts -> assertFalse(hsts.contains("includeSubDomains")));
} }
@Test
void shouldAllowPasswordLengthMoreThan72(@Autowired PasswordEncoder passwordEncoder) {
var encoded = passwordEncoder.encode(RandomStringUtils.secure().nextAlphanumeric(73));
assertNotNull(encoded);
}
} }

View File

@ -25,6 +25,7 @@ ext {
imgscalr = '4.2' imgscalr = '4.2'
exifExtractor = '2.19.0' exifExtractor = '2.19.0'
therapiVersion = '0.13.0' therapiVersion = '0.13.0'
bouncycastleVersion = '1.80'
} }
javaPlatform { javaPlatform {
@ -37,6 +38,7 @@ dependencies {
constraints { constraints {
api "org.springdoc:springdoc-openapi-starter-webflux-ui:$springDocOpenAPI" api "org.springdoc:springdoc-openapi-starter-webflux-ui:$springDocOpenAPI"
api 'org.openapi4j:openapi-schema-validator:1.0.7' api 'org.openapi4j:openapi-schema-validator:1.0.7'
api "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
// Apache Lucene // Apache Lucene
api "org.apache.lucene:lucene-core:$lucene" api "org.apache.lucene:lucene-core:$lucene"