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`:
|
You can easily construct an instance of `DelegatingPasswordEncoder` by using `PasswordEncoderFactories`:
|
||||||
|
|
||||||
.Create Default DelegatingPasswordEncoder
|
.Create Default DelegatingPasswordEncoder
|
||||||
[tabs]
|
include-code::./DelegatingPasswordEncoderUsage[tag=createDefaultPasswordEncoder,indent=0]
|
||||||
======
|
|
||||||
Java::
|
|
||||||
+
|
|
||||||
[source,java,role="primary"]
|
|
||||||
----
|
|
||||||
PasswordEncoder passwordEncoder =
|
|
||||||
PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
|
||||||
----
|
|
||||||
|
|
||||||
Kotlin::
|
|
||||||
+
|
|
||||||
[source,kotlin,role="secondary"]
|
|
||||||
----
|
|
||||||
val passwordEncoder: PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
Alternatively, you can create your own custom instance:
|
Alternatively, you can create your own custom instance:
|
||||||
|
|
||||||
.Create Custom DelegatingPasswordEncoder
|
.Create Custom DelegatingPasswordEncoder
|
||||||
[tabs]
|
include-code::./DelegatingPasswordEncoderUsage[tag=createCustomPasswordEncoder,indent=0]
|
||||||
======
|
|
||||||
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)
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
[[authentication-password-storage-dpe-format]]
|
[[authentication-password-storage-dpe-format]]
|
||||||
=== Password Storage 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.
|
There are convenience mechanisms to make this easier, but this is still not intended for production.
|
||||||
|
|
||||||
.withDefaultPasswordEncoder Example
|
.withDefaultPasswordEncoder Example
|
||||||
[tabs]
|
include-code::./WithDefaultPasswordEncoderUsage[tag=createSingleUser,indent=0]
|
||||||
======
|
|
||||||
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
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
If you are creating multiple users, you can also reuse the builder:
|
If you are creating multiple users, you can also reuse the builder:
|
||||||
|
|
||||||
.withDefaultPasswordEncoder Reusing the Builder
|
.withDefaultPasswordEncoder Reusing the Builder
|
||||||
[tabs]
|
include-code::./WithDefaultPasswordEncoderUsage[tag=createMultipleUsers,indent=0]
|
||||||
======
|
|
||||||
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()
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
This does hash the password that is stored, but the passwords are still exposed in memory and in the compiled source code.
|
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.
|
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.
|
tune and test the strength parameter on your own system so that it takes roughly 1 second to verify a password.
|
||||||
|
|
||||||
.BCryptPasswordEncoder
|
.BCryptPasswordEncoder
|
||||||
[tabs]
|
include-code::./BCryptPasswordEncoderUsage[tag=bcryptPasswordEncoder,indent=0]
|
||||||
======
|
|
||||||
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))
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
[[authentication-password-storage-argon2]]
|
[[authentication-password-storage-argon2]]
|
||||||
== Argon2PasswordEncoder
|
== 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.
|
The current implementation of the `Argon2PasswordEncoder` requires BouncyCastle.
|
||||||
|
|
||||||
.Argon2PasswordEncoder
|
.Argon2PasswordEncoder
|
||||||
[tabs]
|
include-code::./Argon2PasswordEncoderUsage[tag=argon2PasswordEncoder,indent=0]
|
||||||
======
|
|
||||||
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))
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
[[authentication-password-storage-pbkdf2]]
|
[[authentication-password-storage-pbkdf2]]
|
||||||
== Pbkdf2PasswordEncoder
|
== 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.
|
This algorithm is a good choice when FIPS certification is required.
|
||||||
|
|
||||||
.Pbkdf2PasswordEncoder
|
.Pbkdf2PasswordEncoder
|
||||||
[tabs]
|
include-code::./Pbkdf2PasswordEncoderUsage[tag=pbkdf2PasswordEncoder,indent=0]
|
||||||
======
|
|
||||||
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))
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
[[authentication-password-storage-scrypt]]
|
[[authentication-password-storage-scrypt]]
|
||||||
== SCryptPasswordEncoder
|
== 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.
|
Like other adaptive one-way functions, it should be tuned to take about 1 second to verify a password on your system.
|
||||||
|
|
||||||
.SCryptPasswordEncoder
|
.SCryptPasswordEncoder
|
||||||
[tabs]
|
include-code::./SCryptPasswordEncoderUsage[tag=sCryptPasswordEncoder,indent=0]
|
||||||
======
|
|
||||||
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))
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|
||||||
[[authentication-password-storage-other]]
|
[[authentication-password-storage-other]]
|
||||||
== Other ``PasswordEncoder``s
|
== 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:
|
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
|
.Using CompromisedPasswordChecker
|
||||||
[tabs]
|
include-code::./CompromisedPasswordCheckerUsage[tag=configuration,indent=0]
|
||||||
======
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
----
|
|
||||||
======
|
|
||||||
|
|||||||
@ -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