Jelajahi Sumber

Use include-code for password4j docs

This follows the new convention of using include-code going forward to
ensure that the documentation compiles and is tested. This also corrected
a few errors in custom params for Ballooning and PBKDF2 examples.

Issue gh-17706
Rob Winch 2 minggu lalu
induk
melakukan
d0372efadd
13 mengubah file dengan 487 tambahan dan 141 penghapusan
  1. 1 1
      crypto/spring-security-crypto.gradle
  2. 32 140
      docs/modules/ROOT/pages/features/authentication/password-storage.adoc
  3. 1 0
      docs/spring-security-docs.gradle
  4. 53 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/password4jargon2/Argon2UsageTests.java
  5. 52 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/password4jballooning/BallooningHashingUsageTests.java
  6. 52 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/password4jbcrypt/BcryptUsageTests.java
  7. 52 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/password4jpbkdf2/Pbkdf2UsageTests.java
  8. 51 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/password4jscrypt/ScryptUsageTests.java
  9. 51 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jargon2/Argon2UsageTests.kt
  10. 47 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jballooning/BallooningHashingUsageTests.kt
  11. 32 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jbcrypt/BcryptUsageTests.kt
  12. 32 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jpbkdf2/Pbkdf2UsageTests.kt
  13. 31 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jscrypt/ScryptUsageTests.kt

+ 1 - 1
crypto/spring-security-crypto.gradle

@@ -8,7 +8,7 @@ dependencies {
 	management platform(project(":spring-security-dependencies"))
 	optional 'org.springframework:spring-core'
 	optional 'org.bouncycastle:bcpkix-jdk18on'
-	optional 'com.password4j:password4j'
+	optional libs.com.password4j.password4j
 
 	testImplementation "org.assertj:assertj-core"
 	testImplementation "org.junit.jupiter:junit-jupiter-api"

+ 32 - 140
docs/modules/ROOT/pages/features/authentication/password-storage.adoc

@@ -483,37 +483,15 @@ This provides an alternative to Spring Security's built-in `Argon2PasswordEncode
 Argon2 is the winner of the https://en.wikipedia.org/wiki/Password_Hashing_Competition[Password Hashing Competition] and is recommended for new applications.
 This implementation leverages Password4j's Argon2 support which properly includes the salt in the output hash.
 
+Create an encoder with default settings:
+
 .Argon2Password4jPasswordEncoder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-// Create an encoder with default settings
-Argon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();
-String result = encoder.encode("myPassword");
-assertTrue(encoder.matches("myPassword", result));
+include-code::./Argon2UsageTests[tag=default-params,indent=0]
 
-// Create an encoder with custom Argon2 function
-Argon2Function customArgon2 = Argon2Function.getInstance(65536, 3, 4, 32, Argon2.ID);
-Argon2Password4jPasswordEncoder customEncoder = new Argon2Password4jPasswordEncoder(customArgon2);
-----
+Create an encoder with custom Argon2 parameters:
 
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-// Create an encoder with default settings
-val encoder = Argon2Password4jPasswordEncoder()
-val result: String = encoder.encode("myPassword")
-assertTrue(encoder.matches("myPassword", result))
-
-// Create an encoder with custom Argon2 function
-val customArgon2 = Argon2Function.getInstance(65536, 3, 4, 32, Argon2.ID)
-val customEncoder = Argon2Password4jPasswordEncoder(customArgon2)
-----
-======
+.Argon2Password4jPasswordEncoder Custom
+include-code::./Argon2UsageTests[tag=custom-params,indent=0]
 
 [[password4j-bcrypt]]
 === BcryptPassword4jPasswordEncoder
@@ -524,37 +502,15 @@ This provides an alternative to Spring Security's built-in `BCryptPasswordEncode
 BCrypt is a well-established password hashing algorithm that includes built-in salt generation and is resistant to rainbow table attacks.
 This implementation leverages Password4j's BCrypt support which properly includes the salt in the output hash.
 
-.BcryptPassword4jPasswordEncoder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-// Create an encoder with default settings
-BcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();
-String result = encoder.encode("myPassword");
-assertTrue(encoder.matches("myPassword", result));
+Create an encoder with default settings:
 
-// Create an encoder with custom round count
-BcryptFunction customBcrypt = BcryptFunction.getInstance(12);
-BcryptPassword4jPasswordEncoder customEncoder = new BcryptPassword4jPasswordEncoder(customBcrypt);
-----
+.BcryptPassword4jPasswordEncoder
+include-code::./BcryptUsageTests[tag=default-params,indent=0]
 
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-// Create an encoder with default settings
-val encoder = BcryptPassword4jPasswordEncoder()
-val result: String = encoder.encode("myPassword")
-assertTrue(encoder.matches("myPassword", result))
+Create an encoder with custom bcrypt parameters:
 
-// Create an encoder with custom round count
-val customBcrypt = BcryptFunction.getInstance(12)
-val customEncoder = BcryptPassword4jPasswordEncoder(customBcrypt)
-----
-======
+.BcryptPassword4jPasswordEncoder Custom
+include-code::./BcryptUsageTests[tag=custom-params,indent=0]
 
 [[password4j-scrypt]]
 === ScryptPassword4jPasswordEncoder
@@ -565,37 +521,16 @@ This provides an alternative to Spring Security's built-in `SCryptPasswordEncode
 SCrypt is a memory-hard password hashing algorithm designed to be resistant to hardware brute-force attacks.
 This implementation leverages Password4j's SCrypt support which properly includes the salt in the output hash.
 
-.ScryptPassword4jPasswordEncoder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-// Create an encoder with default settings
-ScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();
-String result = encoder.encode("myPassword");
-assertTrue(encoder.matches("myPassword", result));
 
-// Create an encoder with custom SCrypt parameters
-ScryptFunction customScrypt = ScryptFunction.getInstance(32768, 8, 1, 32);
-ScryptPassword4jPasswordEncoder customEncoder = new ScryptPassword4jPasswordEncoder(customScrypt);
-----
+Create an encoder with default settings:
 
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-// Create an encoder with default settings
-val encoder = ScryptPassword4jPasswordEncoder()
-val result: String = encoder.encode("myPassword")
-assertTrue(encoder.matches("myPassword", result))
+.ScryptPassword4jPasswordEncoder
+include-code::./ScryptUsageTests[tag=default-params,indent=0]
 
-// Create an encoder with custom SCrypt parameters
-val customScrypt = ScryptFunction.getInstance(32768, 8, 1, 32)
-val customEncoder = ScryptPassword4jPasswordEncoder(customScrypt)
-----
-======
+Create an encoder with custom scrypt parameters:
+
+.ScryptPassword4jPasswordEncoder Custom
+include-code::./ScryptUsageTests[tag=custom-params,indent=0]
 
 [[password4j-pbkdf2]]
 === Pbkdf2Password4jPasswordEncoder
@@ -607,37 +542,15 @@ PBKDF2 is a key derivation function designed to be computationally expensive to
 This implementation handles salt management explicitly since Password4j's PBKDF2 implementation does not include the salt in the output hash.
 The encoded password format is: `+{salt}:{hash}+` where both salt and hash are Base64 encoded.
 
-.Pbkdf2Password4jPasswordEncoder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-// Create an encoder with default settings
-Pbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();
-String result = encoder.encode("myPassword");
-assertTrue(encoder.matches("myPassword", result));
+Create an encoder with default settings:
 
-// Create an encoder with custom PBKDF2 function and salt length
-PBKDF2Function customPbkdf2 = PBKDF2Function.getInstance(Algorithm.HMAC_SHA256, 100000, 256);
-Pbkdf2Password4jPasswordEncoder customEncoder = new Pbkdf2Password4jPasswordEncoder(customPbkdf2, 32);
-----
+.Pbkdf2Password4jPasswordEncoder
+include-code::./Pbkdf2UsageTests[tag=default-params,indent=0]
 
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-// Create an encoder with default settings
-val encoder = Pbkdf2Password4jPasswordEncoder()
-val result: String = encoder.encode("myPassword")
-assertTrue(encoder.matches("myPassword", result))
+Create an encoder with custom PBKDF2 parameters:
 
-// Create an encoder with custom PBKDF2 function and salt length
-val customPbkdf2 = PBKDF2Function.getInstance(Algorithm.HMAC_SHA256, 100000, 256)
-val customEncoder = Pbkdf2Password4jPasswordEncoder(customPbkdf2, 32)
-----
-======
+.Pbkdf2Password4jPasswordEncoder Custom
+include-code::./Pbkdf2UsageTests[tag=custom-params,indent=0]
 
 [[password4j-ballooning]]
 === BalloonHashingPassword4jPasswordEncoder
@@ -648,37 +561,16 @@ Balloon hashing is a memory-hard password hashing algorithm designed to be resis
 This implementation handles salt management explicitly since Password4j's Balloon hashing implementation does not include the salt in the output hash.
 The encoded password format is: `+{salt}:{hash}+` where both salt and hash are Base64 encoded.
 
-.BalloonHashingPassword4jPasswordEncoder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-// Create an encoder with default settings
-BalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();
-String result = encoder.encode("myPassword");
-assertTrue(encoder.matches("myPassword", result));
 
-// Create an encoder with custom Balloon hashing function and salt length
-BalloonHashingFunction customBalloon = BalloonHashingFunction.getInstance(1024, 3, 4, "SHA-256");
-BalloonHashingPassword4jPasswordEncoder customEncoder = new BalloonHashingPassword4jPasswordEncoder(customBalloon, 32);
-----
+Create an encoder with default settings:
 
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-// Create an encoder with default settings
-val encoder = BalloonHashingPassword4jPasswordEncoder()
-val result: String = encoder.encode("myPassword")
-assertTrue(encoder.matches("myPassword", result))
+.BalloonHashingPassword4jPasswordEncoder
+include-code::./BallooningHashingUsageTests[tag=default-params,indent=0]
 
-// Create an encoder with custom Balloon hashing function and salt length
-val customBalloon = BalloonHashingFunction.getInstance(1024, 3, 4, "SHA-256")
-val customEncoder = BalloonHashingPassword4jPasswordEncoder(customBalloon, 32)
-----
-======
+Create an encoder with custom parameters:
+
+.BalloonHashingPassword4jPasswordEncoder Custom
+include-code::./BallooningHashingUsageTests[tag=custom-params,indent=0]
 
 [[authentication-password-storage-configuration]]
 == Password Storage Configuration

+ 1 - 0
docs/spring-security-docs.gradle

@@ -39,6 +39,7 @@ dependencies {
 	testImplementation project(':spring-security-test')
 	testImplementation project(':spring-security-oauth2-client')
 	testImplementation 'com.squareup.okhttp3:mockwebserver'
+	testImplementation libs.com.password4j.password4j
 	testImplementation 'com.unboundid:unboundid-ldapsdk'
 	testImplementation libs.webauthn4j.core
 	testImplementation 'org.jetbrains.kotlin:kotlin-reflect'

+ 53 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/password4jargon2/Argon2UsageTests.java

@@ -0,0 +1,53 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.docs.features.authentication.password4jargon2;
+
+import com.password4j.Argon2Function;
+import com.password4j.types.Argon2;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password4j.Argon2Password4jPasswordEncoder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Rob Winch
+ */
+public class Argon2UsageTests {
+
+	@Test
+	void defaultParams() {
+		// tag::default-params[]
+		PasswordEncoder encoder = new Argon2Password4jPasswordEncoder();
+		String result = encoder.encode("myPassword");
+		assertThat(encoder.matches("myPassword", result)).isTrue();
+		// end::default-params[]
+	}
+
+	@Test
+	void customParameters() {
+		// tag::custom-params[]
+		Argon2Function argon2Fn = Argon2Function.getInstance(65536, 3, 4, 32,
+				Argon2.ID);
+		PasswordEncoder encoder = new Argon2Password4jPasswordEncoder(argon2Fn);
+		String result = encoder.encode("myPassword");
+		assertThat(encoder.matches("myPassword", result)).isTrue();
+		// end::custom-params[]
+	}
+
+}

+ 52 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/password4jballooning/BallooningHashingUsageTests.java

@@ -0,0 +1,52 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.docs.features.authentication.password4jballooning;
+
+import com.password4j.BalloonHashingFunction;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password4j.BalloonHashingPassword4jPasswordEncoder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Rob Winch
+ */
+public class BallooningHashingUsageTests {
+
+	@Test
+	void defaultParams() {
+		// tag::default-params[]
+		PasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();
+		String result = encoder.encode("myPassword");
+		assertThat(encoder.matches("myPassword", result)).isTrue();
+		// end::default-params[]
+	}
+
+	@Test
+	void customParameters() {
+		// tag::custom-params[]
+		BalloonHashingFunction ballooningHashingFn =
+			BalloonHashingFunction.getInstance("SHA-256", 1024, 3, 4, 3);
+		PasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder(ballooningHashingFn);
+		String result = encoder.encode("myPassword");
+		assertThat(encoder.matches("myPassword", result)).isTrue();
+		// end::custom-params[]
+	}
+
+}

+ 52 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/password4jbcrypt/BcryptUsageTests.java

@@ -0,0 +1,52 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.docs.features.authentication.password4jbcrypt;
+
+import com.password4j.BcryptFunction;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password4j.BcryptPassword4jPasswordEncoder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Rob Winch
+ */
+public class BcryptUsageTests {
+
+	@Test
+	void defaultParams() {
+		// tag::default-params[]
+		PasswordEncoder encoder = new BCryptPasswordEncoder();
+		String result = encoder.encode("myPassword");
+		assertThat(encoder.matches("myPassword", result)).isTrue();
+		// end::default-params[]
+	}
+
+	@Test
+	void customParameters() {
+		// tag::custom-params[]
+		BcryptFunction bcryptFn = BcryptFunction.getInstance(12);
+		PasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(bcryptFn);
+		String result = encoder.encode("myPassword");
+		assertThat(encoder.matches("myPassword", result)).isTrue();
+		// end::custom-params[]
+	}
+
+}

+ 52 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/password4jpbkdf2/Pbkdf2UsageTests.java

@@ -0,0 +1,52 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.docs.features.authentication.password4jpbkdf2;
+
+import com.password4j.PBKDF2Function;
+import com.password4j.types.Hmac;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password4j.Pbkdf2Password4jPasswordEncoder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Rob Winch
+ */
+public class Pbkdf2UsageTests {
+
+	@Test
+	void defaultParams() {
+		// tag::default-params[]
+		PasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();
+		String result = encoder.encode("myPassword");
+		assertThat(encoder.matches("myPassword", result)).isTrue();
+		// end::default-params[]
+	}
+
+	@Test
+	void customParameters() {
+		// tag::custom-params[]
+		PBKDF2Function pbkdf2Fn = PBKDF2Function.getInstance(Hmac.SHA256, 100000, 256);
+		PasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder(pbkdf2Fn);
+		String result = encoder.encode("myPassword");
+		assertThat(encoder.matches("myPassword", result)).isTrue();
+		// end::custom-params[]
+	}
+
+}

+ 51 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/password4jscrypt/ScryptUsageTests.java

@@ -0,0 +1,51 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.docs.features.authentication.password4jscrypt;
+
+import com.password4j.ScryptFunction;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password4j.ScryptPassword4jPasswordEncoder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Rob Winch
+ */
+public class ScryptUsageTests {
+
+	@Test
+	void defaultParams() {
+		// tag::default-params[]
+		PasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();
+		String result = encoder.encode("myPassword");
+		assertThat(encoder.matches("myPassword", result)).isTrue();
+		// end::default-params[]
+	}
+
+	@Test
+	void customParameters() {
+		// tag::custom-params[]
+		ScryptFunction scryptFn = ScryptFunction.getInstance(32768, 8, 1, 32);
+		PasswordEncoder encoder = new ScryptPassword4jPasswordEncoder(scryptFn);
+		String result = encoder.encode("myPassword");
+		assertThat(encoder.matches("myPassword", result)).isTrue();
+		// end::custom-params[]
+	}
+
+}

+ 51 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jargon2/Argon2UsageTests.kt

@@ -0,0 +1,51 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.kt.docs.features.authentication.password4jargon2
+
+import com.password4j.Argon2Function
+import com.password4j.types.Argon2
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import org.springframework.security.crypto.password.PasswordEncoder
+import org.springframework.security.crypto.password4j.Argon2Password4jPasswordEncoder
+
+/**
+ * @author Rob Winch
+ */
+class Argon2UsageTests {
+
+    @Test
+    fun defaultParams() {
+        // tag::default-params[]
+        val encoder: PasswordEncoder = Argon2Password4jPasswordEncoder()
+        val result = encoder.encode("myPassword")
+        assertThat(encoder.matches("myPassword", result)).isTrue()
+        // end::default-params[]
+    }
+
+    @Test
+    fun customParameters() {
+        // tag::custom-params[]
+        val argon2Fn = Argon2Function.getInstance(
+            65536, 3, 4, 32,
+            Argon2.ID
+        )
+        val encoder: PasswordEncoder = Argon2Password4jPasswordEncoder(argon2Fn)
+        val result = encoder.encode("myPassword")
+        assertThat(encoder.matches("myPassword", result)).isTrue()
+        // end::custom-params[]
+    }
+}

+ 47 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jballooning/BallooningHashingUsageTests.kt

@@ -0,0 +1,47 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.kt.docs.features.authentication.password4jballooning
+
+import com.password4j.BalloonHashingFunction
+import org.assertj.core.api.Assertions
+import org.junit.jupiter.api.Test
+import org.springframework.security.crypto.password.PasswordEncoder
+import org.springframework.security.crypto.password4j.BalloonHashingPassword4jPasswordEncoder
+
+/**
+ * @author Rob Winch
+ */
+class BallooningHashingUsageTests {
+    @Test
+    fun defaultParams() {
+        // tag::default-params[]
+        val encoder: PasswordEncoder = BalloonHashingPassword4jPasswordEncoder()
+        val result = encoder.encode("myPassword")
+        Assertions.assertThat(encoder.matches("myPassword", result)).isTrue()
+        // end::default-params[]
+    }
+
+    @Test
+    fun customParameters() {
+        // tag::custom-params[]
+        val ballooningHashingFn =
+            BalloonHashingFunction.getInstance("SHA-256", 1024, 3, 4, 3)
+        val encoder: PasswordEncoder = BalloonHashingPassword4jPasswordEncoder(ballooningHashingFn)
+        val result = encoder.encode("myPassword")
+        Assertions.assertThat(encoder.matches("myPassword", result)).isTrue()
+        // end::custom-params[]
+    }
+}

+ 32 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jbcrypt/BcryptUsageTests.kt

@@ -0,0 +1,32 @@
+package org.springframework.security.kt.docs.features.authentication.password4jbcrypt
+
+import com.password4j.BcryptFunction
+import org.assertj.core.api.Assertions
+import org.junit.jupiter.api.Test
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
+import org.springframework.security.crypto.password.PasswordEncoder
+import org.springframework.security.crypto.password4j.BcryptPassword4jPasswordEncoder
+
+/**
+ * @author Rob Winch
+ */
+class BcryptUsageTests {
+    @Test
+    fun defaultParams() {
+        // tag::default-params[]
+        val encoder: PasswordEncoder = BCryptPasswordEncoder()
+        val result = encoder.encode("myPassword")
+        Assertions.assertThat(encoder.matches("myPassword", result)).isTrue()
+        // end::default-params[]
+    }
+
+    @Test
+    fun customParameters() {
+        // tag::custom-params[]
+        val bcryptFunction = BcryptFunction.getInstance(12)
+        val encoder: PasswordEncoder = BcryptPassword4jPasswordEncoder(bcryptFunction)
+        val result = encoder.encode("myPassword")
+        Assertions.assertThat(encoder.matches("myPassword", result)).isTrue()
+        // end::custom-params[]
+    }
+}

+ 32 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jpbkdf2/Pbkdf2UsageTests.kt

@@ -0,0 +1,32 @@
+package org.springframework.security.kt.docs.features.authentication.password4jpbkdf2
+
+import com.password4j.PBKDF2Function
+import com.password4j.types.Hmac
+import org.assertj.core.api.Assertions
+import org.junit.jupiter.api.Test
+import org.springframework.security.crypto.password.PasswordEncoder
+import org.springframework.security.crypto.password4j.Pbkdf2Password4jPasswordEncoder
+
+/**
+ * @author Rob Winch
+ */
+class Pbkdf2UsageTests {
+    @Test
+    fun defaultParams() {
+        // tag::default-params[]
+        val encoder: PasswordEncoder = Pbkdf2Password4jPasswordEncoder()
+        val result = encoder.encode("myPassword")
+        Assertions.assertThat(encoder.matches("myPassword", result)).isTrue()
+        // end::default-params[]
+    }
+
+    @Test
+    fun customParameters() {
+        // tag::custom-params[]
+        val pbkdf2Fn = PBKDF2Function.getInstance(Hmac.SHA256, 100000, 256)
+        val encoder: PasswordEncoder = Pbkdf2Password4jPasswordEncoder(pbkdf2Fn)
+        val result = encoder.encode("myPassword")
+        Assertions.assertThat(encoder.matches("myPassword", result)).isTrue()
+        // end::custom-params[]
+    }
+}

+ 31 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jscrypt/ScryptUsageTests.kt

@@ -0,0 +1,31 @@
+package org.springframework.security.kt.docs.features.authentication.password4jscrypt
+
+import com.password4j.ScryptFunction
+import org.assertj.core.api.Assertions
+import org.junit.jupiter.api.Test
+import org.springframework.security.crypto.password.PasswordEncoder
+import org.springframework.security.crypto.password4j.ScryptPassword4jPasswordEncoder
+
+/**
+ * @author Rob Winch
+ */
+class ScryptUsageTests {
+    @Test
+    fun defaultParams() {
+        // tag::default-params[]
+        val encoder: PasswordEncoder = ScryptPassword4jPasswordEncoder()
+        val result = encoder.encode("myPassword")
+        Assertions.assertThat(encoder.matches("myPassword", result)).isTrue()
+        // end::default-params[]
+    }
+
+    @Test
+    fun customParameters() {
+        // tag::custom-params[]
+        val scryptFn = ScryptFunction.getInstance(32768, 8, 1, 32)
+        val encoder: PasswordEncoder = ScryptPassword4jPasswordEncoder(scryptFn)
+        val result = encoder.encode("myPassword")
+        Assertions.assertThat(encoder.matches("myPassword", result)).isTrue()
+        // end::custom-params[]
+    }
+}