mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-11-10 19:48:50 +00:00
Add Include-Code to the Password Storage page
References gh-16226 Signed-off-by: Himanshu Pareek <himanshupareekiit01@gmail.com>
This commit is contained in:
parent
56a23d9ddc
commit
dcb4e47cd5
@ -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]
|
||||
|
||||
@ -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[]
|
||||
}
|
||||
@ -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[]
|
||||
}
|
||||
}
|
||||
@ -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[]
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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[]
|
||||
}
|
||||
}
|
||||
@ -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[]
|
||||
}
|
||||
}
|
||||
@ -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[]
|
||||
}
|
||||
@ -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[]
|
||||
}
|
||||
}
|
||||
@ -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[]
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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[]
|
||||
}
|
||||
}
|
||||
@ -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[]
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user