Browse Source

Add Include-Code to the Password Storage page

References gh-16226

Signed-off-by: Himanshu Pareek <himanshupareekiit01@gmail.com>
Himanshu Pareek 1 month ago
parent
commit
dcb4e47cd5
15 changed files with 401 additions and 293 deletions
  1. 9 293
      docs/modules/ROOT/pages/features/authentication/password-storage.adoc
  2. 57 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationcompromisedpasswordcheck/CompromisedPasswordCheckerUsage.java
  3. 16 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstorageargon2/Argon2PasswordEncoderUsage.java
  4. 16 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragebcrypt/BCryptPasswordEncoderUsage.java
  5. 38 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragedepgettingstarted/WithDefaultPasswordEncoderUsage.java
  6. 43 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragedpe/DelegatingPasswordEncoderUsage.java
  7. 16 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragepbkdf2/Pbkdf2PasswordEncoderUsage.java
  8. 16 0
      docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragescrypt/SCryptPasswordEncoderUsage.java
  9. 55 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationcompromisedpasswordcheck/CompromisedPasswordCheckerUsage.kt
  10. 15 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstorageargon2/Argon2PasswordEncoderUsage.kt
  11. 15 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragebcrypt/BCryptPasswordEncoderUsage.kt
  12. 36 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragedepgettingstarted/WithDefaultPasswordEncoderUsage.kt
  13. 39 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragedpe/DelegatingPasswordEncoderUsage.kt
  14. 15 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragepbkdf2/Pbkdf2PasswordEncoderUsage.kt
  15. 15 0
      docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragescrypt/SCryptPasswordEncoderUsage.kt

+ 9 - 293
docs/modules/ROOT/pages/features/authentication/password-storage.adoc

@@ -67,68 +67,12 @@ Instead Spring Security introduces `DelegatingPasswordEncoder`, which solves all
 You can easily construct an instance of `DelegatingPasswordEncoder` by using `PasswordEncoderFactories`:
 
 .Create Default DelegatingPasswordEncoder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-PasswordEncoder passwordEncoder =
-    PasswordEncoderFactories.createDelegatingPasswordEncoder();
-----
-
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-val passwordEncoder: PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()
-----
-======
+include-code::./DelegatingPasswordEncoderUsage[tag=createDefaultPasswordEncoder,indent=0]
 
 Alternatively, you can create your own custom instance:
 
 .Create Custom DelegatingPasswordEncoder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-String idForEncode = "bcrypt";
-Map encoders = new HashMap<>();
-encoders.put(idForEncode, new BCryptPasswordEncoder());
-encoders.put("noop", NoOpPasswordEncoder.getInstance());
-encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());
-encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
-encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
-encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
-encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
-encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
-encoders.put("sha256", new StandardPasswordEncoder());
-
-PasswordEncoder passwordEncoder =
-    new DelegatingPasswordEncoder(idForEncode, encoders);
-----
-
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-val idForEncode = "bcrypt"
-val encoders: MutableMap<String, PasswordEncoder> = mutableMapOf()
-encoders[idForEncode] = BCryptPasswordEncoder()
-encoders["noop"] = NoOpPasswordEncoder.getInstance()
-encoders["pbkdf2"] = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5()
-encoders["pbkdf2@SpringSecurity_v5_8"] = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()
-encoders["scrypt"] = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1()
-encoders["scrypt@SpringSecurity_v5_8"] = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()
-encoders["argon2"] = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2()
-encoders["argon2@SpringSecurity_v5_8"] = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()
-encoders["sha256"] = StandardPasswordEncoder()
-
-val passwordEncoder: PasswordEncoder = DelegatingPasswordEncoder(idForEncode, encoders)
-----
-======
+include-code::./DelegatingPasswordEncoderUsage[tag=createCustomPasswordEncoder,indent=0]
 
 [[authentication-password-storage-dpe-format]]
 === Password Storage Format
@@ -209,74 +153,12 @@ If you are putting together a demo or a sample, it is a bit cumbersome to take t
 There are convenience mechanisms to make this easier, but this is still not intended for production.
 
 .withDefaultPasswordEncoder Example
-[tabs]
-======
-Java::
-+
-[source,java,role="primary",attrs="-attributes"]
-----
-UserDetails user = User.withDefaultPasswordEncoder()
-  .username("user")
-  .password("password")
-  .roles("user")
-  .build();
-System.out.println(user.getPassword());
-// {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
-----
-
-Kotlin::
-+
-[source,kotlin,role="secondary",attrs="-attributes"]
-----
-val user = User.withDefaultPasswordEncoder()
-    .username("user")
-    .password("password")
-    .roles("user")
-    .build()
-println(user.password)
-// {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
-----
-======
+include-code::./WithDefaultPasswordEncoderUsage[tag=createSingleUser,indent=0]
 
 If you are creating multiple users, you can also reuse the builder:
 
 .withDefaultPasswordEncoder Reusing the Builder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-UserBuilder users = User.withDefaultPasswordEncoder();
-UserDetails user = users
-  .username("user")
-  .password("password")
-  .roles("USER")
-  .build();
-UserDetails admin = users
-  .username("admin")
-  .password("password")
-  .roles("USER","ADMIN")
-  .build();
-----
-
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-val users = User.withDefaultPasswordEncoder()
-val user = users
-    .username("user")
-    .password("password")
-    .roles("USER")
-    .build()
-val admin = users
-    .username("admin")
-    .password("password")
-    .roles("USER", "ADMIN")
-    .build()
-----
-======
+include-code::./WithDefaultPasswordEncoderUsage[tag=createMultipleUsers,indent=0]
 
 This does hash the password that is stored, but the passwords are still exposed in memory and in the compiled source code.
 Therefore, it is still not considered secure for a production environment.
@@ -337,28 +219,7 @@ The default implementation of `BCryptPasswordEncoder` uses strength 10 as mentio
 tune and test the strength parameter on your own system so that it takes roughly 1 second to verify a password.
 
 .BCryptPasswordEncoder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-// Create an encoder with strength 16
-BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
-String result = encoder.encode("myPassword");
-assertTrue(encoder.matches("myPassword", result));
-----
-
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-// Create an encoder with strength 16
-val encoder = BCryptPasswordEncoder(16)
-val result: String = encoder.encode("myPassword")
-assertTrue(encoder.matches("myPassword", result))
-----
-======
+include-code::./BCryptPasswordEncoderUsage[tag=bcryptPasswordEncoder,indent=0]
 
 [[authentication-password-storage-argon2]]
 == Argon2PasswordEncoder
@@ -370,28 +231,7 @@ Like other adaptive one-way functions, it should be tuned to take about 1 second
 The current implementation of the `Argon2PasswordEncoder` requires BouncyCastle.
 
 .Argon2PasswordEncoder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-// Create an encoder with all the defaults
-Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
-String result = encoder.encode("myPassword");
-assertTrue(encoder.matches("myPassword", result));
-----
-
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-// Create an encoder with all the defaults
-val encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()
-val result: String = encoder.encode("myPassword")
-assertTrue(encoder.matches("myPassword", result))
-----
-======
+include-code::./Argon2PasswordEncoderUsage[tag=argon2PasswordEncoder,indent=0]
 
 [[authentication-password-storage-pbkdf2]]
 == Pbkdf2PasswordEncoder
@@ -402,28 +242,7 @@ Like other adaptive one-way functions, it should be tuned to take about 1 second
 This algorithm is a good choice when FIPS certification is required.
 
 .Pbkdf2PasswordEncoder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-// Create an encoder with all the defaults
-Pbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();
-String result = encoder.encode("myPassword");
-assertTrue(encoder.matches("myPassword", result));
-----
-
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-// Create an encoder with all the defaults
-val encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()
-val result: String = encoder.encode("myPassword")
-assertTrue(encoder.matches("myPassword", result))
-----
-======
+include-code::./Pbkdf2PasswordEncoderUsage[tag=pbkdf2PasswordEncoder,indent=0]
 
 [[authentication-password-storage-scrypt]]
 == SCryptPasswordEncoder
@@ -433,28 +252,7 @@ To defeat password cracking on custom hardware, scrypt is a deliberately slow al
 Like other adaptive one-way functions, it should be tuned to take about 1 second to verify a password on your system.
 
 .SCryptPasswordEncoder
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-// Create an encoder with all the defaults
-SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8();
-String result = encoder.encode("myPassword");
-assertTrue(encoder.matches("myPassword", result));
-----
-
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-// Create an encoder with all the defaults
-val encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()
-val result: String = encoder.encode("myPassword")
-assertTrue(encoder.matches("myPassword", result))
-----
-======
+include-code::./SCryptPasswordEncoderUsage[tag=sCryptPasswordEncoder,indent=0]
 
 [[authentication-password-storage-other]]
 == Other ``PasswordEncoder``s
@@ -606,86 +404,4 @@ However, just a 401 or the redirect is not so useful in that case, it will cause
 In such cases, you can handle the `CompromisedPasswordException` via the `AuthenticationFailureHandler` to perform your desired logic, like redirecting the user-agent to `/reset-password`, for example:
 
 .Using CompromisedPasswordChecker
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-@Bean
-public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
-    http
-        .authorizeHttpRequests(authorize -> authorize
-            .anyRequest().authenticated()
-        )
-        .formLogin((login) -> login
-            .failureHandler(new CompromisedPasswordAuthenticationFailureHandler())
-        );
-    return http.build();
-}
-
-@Bean
-public CompromisedPasswordChecker compromisedPasswordChecker() {
-    return new HaveIBeenPwnedRestApiPasswordChecker();
-}
-
-static class CompromisedPasswordAuthenticationFailureHandler implements AuthenticationFailureHandler {
-
-    private final SimpleUrlAuthenticationFailureHandler defaultFailureHandler = new SimpleUrlAuthenticationFailureHandler(
-            "/login?error");
-
-    private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
-
-    @Override
-    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
-            AuthenticationException exception) throws IOException, ServletException {
-        if (exception instanceof CompromisedPasswordException) {
-            this.redirectStrategy.sendRedirect(request, response, "/reset-password");
-            return;
-        }
-        this.defaultFailureHandler.onAuthenticationFailure(request, response, exception);
-    }
-
-}
-----
-
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-@Bean
-open fun filterChain(http:HttpSecurity): SecurityFilterChain {
-    http {
-        authorizeHttpRequests {
-            authorize(anyRequest, authenticated)
-        }
-        formLogin {
-            failureHandler = CompromisedPasswordAuthenticationFailureHandler()
-        }
-    }
-    return http.build()
-}
-
-@Bean
-open fun compromisedPasswordChecker(): CompromisedPasswordChecker {
-    return HaveIBeenPwnedRestApiPasswordChecker()
-}
-
-class CompromisedPasswordAuthenticationFailureHandler : AuthenticationFailureHandler {
-    private val defaultFailureHandler = SimpleUrlAuthenticationFailureHandler("/login?error")
-    private val redirectStrategy = DefaultRedirectStrategy()
-
-    override fun onAuthenticationFailure(
-        request: HttpServletRequest,
-        response: HttpServletResponse,
-        exception: AuthenticationException
-    ) {
-        if (exception is CompromisedPasswordException) {
-            redirectStrategy.sendRedirect(request, response, "/reset-password")
-            return
-        }
-        defaultFailureHandler.onAuthenticationFailure(request, response, exception)
-    }
-}
-----
-======
+include-code::./CompromisedPasswordCheckerUsage[tag=configuration,indent=0]

+ 57 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationcompromisedpasswordcheck/CompromisedPasswordCheckerUsage.java

@@ -0,0 +1,57 @@
+package org.springframework.security.docs.features.authentication.authenticationcompromisedpasswordcheck;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.authentication.password.CompromisedPasswordChecker;
+import org.springframework.security.authentication.password.CompromisedPasswordException;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.DefaultRedirectStrategy;
+import org.springframework.security.web.RedirectStrategy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
+import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker;
+
+public class CompromisedPasswordCheckerUsage {
+	// tag::configuration[]
+	@Bean
+	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+		http
+			.authorizeHttpRequests(authorize -> authorize
+				.anyRequest().authenticated()
+			)
+			.formLogin((login) -> login
+				.failureHandler(new CompromisedPasswordAuthenticationFailureHandler())
+			);
+		return http.build();
+	}
+
+	@Bean
+	public CompromisedPasswordChecker compromisedPasswordChecker() {
+		return new HaveIBeenPwnedRestApiPasswordChecker();
+	}
+
+	static class CompromisedPasswordAuthenticationFailureHandler implements AuthenticationFailureHandler {
+
+		private final SimpleUrlAuthenticationFailureHandler defaultFailureHandler = new SimpleUrlAuthenticationFailureHandler(
+				"/login?error");
+
+		private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
+
+		@Override
+		public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
+				AuthenticationException exception) throws IOException, ServletException {
+			if (exception instanceof CompromisedPasswordException) {
+				this.redirectStrategy.sendRedirect(request, response, "/reset-password");
+				return;
+			}
+			this.defaultFailureHandler.onAuthenticationFailure(request, response, exception);
+		}
+
+	}
+	// end::configuration[]
+}

+ 16 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstorageargon2/Argon2PasswordEncoderUsage.java

@@ -0,0 +1,16 @@
+package org.springframework.security.docs.features.authentication.authenticationpasswordstorageargon2;
+
+import static org.junit.Assert.assertTrue;
+
+import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
+
+public class Argon2PasswordEncoderUsage {
+	public void testArgon2PasswordEncoder() {
+		// tag::argon2PasswordEncoder[]
+		// Create an encoder with all the defaults
+		Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
+		String result = encoder.encode("myPassword");
+		assertTrue(encoder.matches("myPassword", result));
+		// end::argon2PasswordEncoder[]
+	}
+}

+ 16 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragebcrypt/BCryptPasswordEncoderUsage.java

@@ -0,0 +1,16 @@
+package org.springframework.security.docs.features.authentication.authenticationpasswordstoragebcrypt;
+
+import static org.junit.Assert.assertTrue;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+public class BCryptPasswordEncoderUsage {
+	public void testBCryptPasswordEncoder() {
+		// tag::bcryptPasswordEncoder[]
+		// Create an encoder with strength 16
+		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
+		String result = encoder.encode("myPassword");
+		assertTrue(encoder.matches("myPassword", result));
+		// end::bcryptPasswordEncoder[]
+	}
+}

+ 38 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragedepgettingstarted/WithDefaultPasswordEncoderUsage.java

@@ -0,0 +1,38 @@
+package org.springframework.security.docs.features.authentication.authenticationpasswordstoragedepgettingstarted;
+
+import java.util.List;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import static org.springframework.security.core.userdetails.User.UserBuilder;
+
+public class WithDefaultPasswordEncoderUsage {
+	public UserDetails createSingleUser() {
+		// tag::createSingleUser[]
+		UserDetails user = User.withDefaultPasswordEncoder()
+				.username("user")
+				.password("password")
+				.roles("user")
+				.build();
+		System.out.println(user.getPassword());
+		// {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
+		// end::createSingleUser[]
+		return user;
+	}
+
+	public List<UserDetails> createMultipleUsers() {
+		// tag::createMultipleUsers[]
+		UserBuilder users = User.withDefaultPasswordEncoder();
+		UserDetails user = users
+				.username("user")
+				.password("password")
+				.roles("USER")
+				.build();
+		UserDetails admin = users
+				.username("admin")
+				.password("password")
+				.roles("USER","ADMIN")
+				.build();
+		// end::createMultipleUsers[]
+		return List.of(user, admin);
+	}
+}

+ 43 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragedpe/DelegatingPasswordEncoderUsage.java

@@ -0,0 +1,43 @@
+package org.springframework.security.docs.features.authentication.authenticationpasswordstoragedpe;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
+import org.springframework.security.crypto.password.NoOpPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
+import org.springframework.security.crypto.password.StandardPasswordEncoder;
+import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
+
+public class DelegatingPasswordEncoderUsage {
+	PasswordEncoder defaultDelegatingPasswordEncoder() {
+		// tag::createDefaultPasswordEncoder[]
+		PasswordEncoder passwordEncoder =
+				PasswordEncoderFactories.createDelegatingPasswordEncoder();
+		// end::createDefaultPasswordEncoder[]
+		return passwordEncoder;
+	}
+
+	PasswordEncoder customDelegatingPasswordEncoder() {
+		// tag::createCustomPasswordEncoder[]
+		String idForEncode = "bcrypt";
+		Map encoders = new HashMap<>();
+		encoders.put(idForEncode, new BCryptPasswordEncoder());
+		encoders.put("noop", NoOpPasswordEncoder.getInstance());
+		encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());
+		encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
+		encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
+		encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
+		encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
+		encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
+		encoders.put("sha256", new StandardPasswordEncoder());
+
+		PasswordEncoder passwordEncoder =
+			new DelegatingPasswordEncoder(idForEncode, encoders);
+		// end::createCustomPasswordEncoder[]
+		return passwordEncoder;
+	}
+}

+ 16 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragepbkdf2/Pbkdf2PasswordEncoderUsage.java

@@ -0,0 +1,16 @@
+package org.springframework.security.docs.features.authentication.authenticationpasswordstoragepbkdf2;
+
+import static org.junit.Assert.assertTrue;
+
+import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
+
+public class Pbkdf2PasswordEncoderUsage {
+	void testPbkdf2PasswordEncoder() {
+		// tag::pbkdf2PasswordEncoder[]
+		// Create an encoder with all the defaults
+		Pbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();
+		String result = encoder.encode("myPassword");
+		assertTrue(encoder.matches("myPassword", result));
+		// end::pbkdf2PasswordEncoder[]
+	}
+}

+ 16 - 0
docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragescrypt/SCryptPasswordEncoderUsage.java

@@ -0,0 +1,16 @@
+package org.springframework.security.docs.features.authentication.authenticationpasswordstoragescrypt;
+
+import static org.junit.Assert.assertTrue;
+
+import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
+
+public class SCryptPasswordEncoderUsage {
+	void testSCryptPasswordEncoder() {
+		// tag::sCryptPasswordEncoder[]
+		// Create an encoder with all the defaults
+		SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8();
+		String result = encoder.encode("myPassword");
+		assertTrue(encoder.matches("myPassword", result));
+		// end::sCryptPasswordEncoder[]
+	}
+}

+ 55 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationcompromisedpasswordcheck/CompromisedPasswordCheckerUsage.kt

@@ -0,0 +1,55 @@
+package org.springframework.security.kt.docs.features.authentication.authenticationcompromisedpasswordcheck
+
+import jakarta.servlet.http.HttpServletRequest
+import jakarta.servlet.http.HttpServletResponse
+import org.springframework.context.annotation.Bean
+import org.springframework.security.authentication.password.CompromisedPasswordChecker
+import org.springframework.security.authentication.password.CompromisedPasswordException
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.invoke
+import org.springframework.security.core.AuthenticationException
+import org.springframework.security.web.DefaultRedirectStrategy
+import org.springframework.security.web.SecurityFilterChain
+import org.springframework.security.web.authentication.AuthenticationFailureHandler
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler
+import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker
+
+
+class CompromisedPasswordCheckerUsage {
+    // tag::configuration[]
+    @Bean
+    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
+        http {
+            authorizeHttpRequests {
+                authorize(anyRequest, authenticated)
+            }
+            formLogin {
+                authenticationFailureHandler = CompromisedPasswordAuthenticationFailureHandler()
+            }
+        }
+        return http.build()
+    }
+
+    @Bean
+    open fun compromisedPasswordChecker(): CompromisedPasswordChecker {
+        return HaveIBeenPwnedRestApiPasswordChecker()
+    }
+
+    class CompromisedPasswordAuthenticationFailureHandler : AuthenticationFailureHandler {
+        private val defaultFailureHandler = SimpleUrlAuthenticationFailureHandler("/login?error")
+        private val redirectStrategy = DefaultRedirectStrategy()
+
+        override fun onAuthenticationFailure(
+            request: HttpServletRequest,
+            response: HttpServletResponse,
+            exception: AuthenticationException
+        ) {
+            if (exception is CompromisedPasswordException) {
+                redirectStrategy.sendRedirect(request, response, "/reset-password")
+                return
+            }
+            defaultFailureHandler.onAuthenticationFailure(request, response, exception)
+        }
+    }
+    // end::configuration[]
+}

+ 15 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstorageargon2/Argon2PasswordEncoderUsage.kt

@@ -0,0 +1,15 @@
+package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstorageargon2
+
+import org.junit.Assert.assertTrue
+import org.springframework.security.crypto.argon2.Argon2PasswordEncoder
+
+class Argon2PasswordEncoderUsage {
+    fun testArgon2PasswordEncoder() {
+        // tag::argon2PasswordEncoder[]
+        // Create an encoder with all the defaults
+        val encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()
+        val result: String = encoder.encode("myPassword")
+        assertTrue(encoder.matches("myPassword", result))
+        // end::argon2PasswordEncoder[]
+    }
+}

+ 15 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragebcrypt/BCryptPasswordEncoderUsage.kt

@@ -0,0 +1,15 @@
+package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstoragebcrypt
+
+import org.junit.Assert.assertTrue
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
+
+class BCryptPasswordEncoderUsage {
+    fun testBCryptPasswordEncoder() {
+        // tag::bcryptPasswordEncoder[]
+        // Create an encoder with strength 16
+        val encoder = BCryptPasswordEncoder(16)
+        val result: String = encoder.encode("myPassword")
+        assertTrue(encoder.matches("myPassword", result))
+        // end::bcryptPasswordEncoder[]
+    }
+}

+ 36 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragedepgettingstarted/WithDefaultPasswordEncoderUsage.kt

@@ -0,0 +1,36 @@
+package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstoragedepgettingstarted
+
+import org.springframework.security.core.userdetails.User
+import org.springframework.security.core.userdetails.UserDetails
+
+class WithDefaultPasswordEncoderUsage {
+    fun createSingleUser(): UserDetails {
+        // tag::createSingleUser[]
+        val user = User.withDefaultPasswordEncoder()
+            .username("user")
+            .password("password")
+            .roles("user")
+            .build()
+        println(user.password)
+        // {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
+        // end::createSingleUser[]
+        return user
+    }
+
+    fun createMultipleUsers(): List<UserDetails> {
+        // tag::createMultipleUsers[]
+        val users = User.withDefaultPasswordEncoder()
+        val user = users
+            .username("user")
+            .password("password")
+            .roles("USER")
+            .build()
+        val admin = users
+            .username("admin")
+            .password("password")
+            .roles("USER", "ADMIN")
+            .build()
+        // end::createMultipleUsers[]
+        return listOf(user, admin)
+    }
+}

+ 39 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragedpe/DelegatingPasswordEncoderUsage.kt

@@ -0,0 +1,39 @@
+package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstoragedpe
+
+import org.springframework.security.crypto.argon2.Argon2PasswordEncoder
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
+import org.springframework.security.crypto.factory.PasswordEncoderFactories
+import org.springframework.security.crypto.password.DelegatingPasswordEncoder
+import org.springframework.security.crypto.password.NoOpPasswordEncoder
+import org.springframework.security.crypto.password.PasswordEncoder
+import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder
+import org.springframework.security.crypto.password.StandardPasswordEncoder
+import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder
+
+class DelegatingPasswordEncoderUsage {
+    fun defaultDelegatingPasswordEncoder(): PasswordEncoder {
+        // tag::createDefaultPasswordEncoder[]
+        val passwordEncoder: PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()
+        // end::createDefaultPasswordEncoder[]
+        return passwordEncoder
+    }
+
+    fun customDelegatingPasswordEncoder(): PasswordEncoder {
+        // tag::createCustomPasswordEncoder[]
+        val idForEncode = "bcrypt"
+        val encoders: MutableMap<String, PasswordEncoder> = mutableMapOf()
+        encoders[idForEncode] = BCryptPasswordEncoder()
+        encoders["noop"] = NoOpPasswordEncoder.getInstance()
+        encoders["pbkdf2"] = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5()
+        encoders["pbkdf2@SpringSecurity_v5_8"] = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()
+        encoders["scrypt"] = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1()
+        encoders["scrypt@SpringSecurity_v5_8"] = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()
+        encoders["argon2"] = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2()
+        encoders["argon2@SpringSecurity_v5_8"] = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()
+        encoders["sha256"] = StandardPasswordEncoder()
+
+        val passwordEncoder: PasswordEncoder = DelegatingPasswordEncoder(idForEncode, encoders)
+        // end::createCustomPasswordEncoder[]
+        return passwordEncoder
+    }
+}

+ 15 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragepbkdf2/Pbkdf2PasswordEncoderUsage.kt

@@ -0,0 +1,15 @@
+package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstoragepbkdf2
+
+import org.junit.Assert.assertTrue
+import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder
+
+class Pbkdf2PasswordEncoderUsage {
+    fun testPbkdf2PasswordEncoder() {
+        // tag::pbkdf2PasswordEncoder[]
+        // Create an encoder with all the defaults
+        val encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()
+        val result: String = encoder.encode("myPassword")
+        assertTrue(encoder.matches("myPassword", result))
+        // end::pbkdf2PasswordEncoder[]
+    }
+}

+ 15 - 0
docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragescrypt/SCryptPasswordEncoderUsage.kt

@@ -0,0 +1,15 @@
+package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstoragescrypt
+
+import org.junit.Assert.assertTrue
+import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder
+
+class SCryptPasswordEncoderUsage {
+    fun testSCryptPasswordEncoder() {
+        // tag::sCryptPasswordEncoder[]
+        // Create an encoder with all the defaults
+        val encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()
+        val result: String = encoder.encode("myPassword")
+        assertTrue(encoder.matches("myPassword", result))
+        // end::sCryptPasswordEncoder[]
+    }
+}