Skip to content

Commit dcb4e47

Browse files
himanshu-pareekjzheaux
authored andcommitted
Add Include-Code to the Password Storage page
References gh-16226 Signed-off-by: Himanshu Pareek <[email protected]>
1 parent 56a23d9 commit dcb4e47

File tree

15 files changed

+401
-293
lines changed

15 files changed

+401
-293
lines changed

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

Lines changed: 9 additions & 293 deletions
Original file line numberDiff line numberDiff line change
@@ -67,68 +67,12 @@ Instead Spring Security introduces `DelegatingPasswordEncoder`, which solves all
6767
You can easily construct an instance of `DelegatingPasswordEncoder` by using `PasswordEncoderFactories`:
6868

6969
.Create Default DelegatingPasswordEncoder
70-
[tabs]
71-
======
72-
Java::
73-
+
74-
[source,java,role="primary"]
75-
----
76-
PasswordEncoder passwordEncoder =
77-
PasswordEncoderFactories.createDelegatingPasswordEncoder();
78-
----
79-
80-
Kotlin::
81-
+
82-
[source,kotlin,role="secondary"]
83-
----
84-
val passwordEncoder: PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()
85-
----
86-
======
70+
include-code::./DelegatingPasswordEncoderUsage[tag=createDefaultPasswordEncoder,indent=0]
8771

8872
Alternatively, you can create your own custom instance:
8973

9074
.Create Custom DelegatingPasswordEncoder
91-
[tabs]
92-
======
93-
Java::
94-
+
95-
[source,java,role="primary"]
96-
----
97-
String idForEncode = "bcrypt";
98-
Map encoders = new HashMap<>();
99-
encoders.put(idForEncode, new BCryptPasswordEncoder());
100-
encoders.put("noop", NoOpPasswordEncoder.getInstance());
101-
encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());
102-
encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
103-
encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
104-
encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
105-
encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
106-
encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
107-
encoders.put("sha256", new StandardPasswordEncoder());
108-
109-
PasswordEncoder passwordEncoder =
110-
new DelegatingPasswordEncoder(idForEncode, encoders);
111-
----
112-
113-
Kotlin::
114-
+
115-
[source,kotlin,role="secondary"]
116-
----
117-
val idForEncode = "bcrypt"
118-
val encoders: MutableMap<String, PasswordEncoder> = mutableMapOf()
119-
encoders[idForEncode] = BCryptPasswordEncoder()
120-
encoders["noop"] = NoOpPasswordEncoder.getInstance()
121-
encoders["pbkdf2"] = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5()
122-
encoders["pbkdf2@SpringSecurity_v5_8"] = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()
123-
encoders["scrypt"] = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1()
124-
encoders["scrypt@SpringSecurity_v5_8"] = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()
125-
encoders["argon2"] = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2()
126-
encoders["argon2@SpringSecurity_v5_8"] = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()
127-
encoders["sha256"] = StandardPasswordEncoder()
128-
129-
val passwordEncoder: PasswordEncoder = DelegatingPasswordEncoder(idForEncode, encoders)
130-
----
131-
======
75+
include-code::./DelegatingPasswordEncoderUsage[tag=createCustomPasswordEncoder,indent=0]
13276

13377
[[authentication-password-storage-dpe-format]]
13478
=== 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
209153
There are convenience mechanisms to make this easier, but this is still not intended for production.
210154

211155
.withDefaultPasswordEncoder Example
212-
[tabs]
213-
======
214-
Java::
215-
+
216-
[source,java,role="primary",attrs="-attributes"]
217-
----
218-
UserDetails user = User.withDefaultPasswordEncoder()
219-
.username("user")
220-
.password("password")
221-
.roles("user")
222-
.build();
223-
System.out.println(user.getPassword());
224-
// {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
225-
----
226-
227-
Kotlin::
228-
+
229-
[source,kotlin,role="secondary",attrs="-attributes"]
230-
----
231-
val user = User.withDefaultPasswordEncoder()
232-
.username("user")
233-
.password("password")
234-
.roles("user")
235-
.build()
236-
println(user.password)
237-
// {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
238-
----
239-
======
156+
include-code::./WithDefaultPasswordEncoderUsage[tag=createSingleUser,indent=0]
240157

241158
If you are creating multiple users, you can also reuse the builder:
242159

243160
.withDefaultPasswordEncoder Reusing the Builder
244-
[tabs]
245-
======
246-
Java::
247-
+
248-
[source,java,role="primary"]
249-
----
250-
UserBuilder users = User.withDefaultPasswordEncoder();
251-
UserDetails user = users
252-
.username("user")
253-
.password("password")
254-
.roles("USER")
255-
.build();
256-
UserDetails admin = users
257-
.username("admin")
258-
.password("password")
259-
.roles("USER","ADMIN")
260-
.build();
261-
----
262-
263-
Kotlin::
264-
+
265-
[source,kotlin,role="secondary"]
266-
----
267-
val users = User.withDefaultPasswordEncoder()
268-
val user = users
269-
.username("user")
270-
.password("password")
271-
.roles("USER")
272-
.build()
273-
val admin = users
274-
.username("admin")
275-
.password("password")
276-
.roles("USER", "ADMIN")
277-
.build()
278-
----
279-
======
161+
include-code::./WithDefaultPasswordEncoderUsage[tag=createMultipleUsers,indent=0]
280162

281163
This does hash the password that is stored, but the passwords are still exposed in memory and in the compiled source code.
282164
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
337219
tune and test the strength parameter on your own system so that it takes roughly 1 second to verify a password.
338220

339221
.BCryptPasswordEncoder
340-
[tabs]
341-
======
342-
Java::
343-
+
344-
[source,java,role="primary"]
345-
----
346-
// Create an encoder with strength 16
347-
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
348-
String result = encoder.encode("myPassword");
349-
assertTrue(encoder.matches("myPassword", result));
350-
----
351-
352-
Kotlin::
353-
+
354-
[source,kotlin,role="secondary"]
355-
----
356-
// Create an encoder with strength 16
357-
val encoder = BCryptPasswordEncoder(16)
358-
val result: String = encoder.encode("myPassword")
359-
assertTrue(encoder.matches("myPassword", result))
360-
----
361-
======
222+
include-code::./BCryptPasswordEncoderUsage[tag=bcryptPasswordEncoder,indent=0]
362223

363224
[[authentication-password-storage-argon2]]
364225
== Argon2PasswordEncoder
@@ -370,28 +231,7 @@ Like other adaptive one-way functions, it should be tuned to take about 1 second
370231
The current implementation of the `Argon2PasswordEncoder` requires BouncyCastle.
371232

372233
.Argon2PasswordEncoder
373-
[tabs]
374-
======
375-
Java::
376-
+
377-
[source,java,role="primary"]
378-
----
379-
// Create an encoder with all the defaults
380-
Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
381-
String result = encoder.encode("myPassword");
382-
assertTrue(encoder.matches("myPassword", result));
383-
----
384-
385-
Kotlin::
386-
+
387-
[source,kotlin,role="secondary"]
388-
----
389-
// Create an encoder with all the defaults
390-
val encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()
391-
val result: String = encoder.encode("myPassword")
392-
assertTrue(encoder.matches("myPassword", result))
393-
----
394-
======
234+
include-code::./Argon2PasswordEncoderUsage[tag=argon2PasswordEncoder,indent=0]
395235

396236
[[authentication-password-storage-pbkdf2]]
397237
== Pbkdf2PasswordEncoder
@@ -402,28 +242,7 @@ Like other adaptive one-way functions, it should be tuned to take about 1 second
402242
This algorithm is a good choice when FIPS certification is required.
403243

404244
.Pbkdf2PasswordEncoder
405-
[tabs]
406-
======
407-
Java::
408-
+
409-
[source,java,role="primary"]
410-
----
411-
// Create an encoder with all the defaults
412-
Pbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();
413-
String result = encoder.encode("myPassword");
414-
assertTrue(encoder.matches("myPassword", result));
415-
----
416-
417-
Kotlin::
418-
+
419-
[source,kotlin,role="secondary"]
420-
----
421-
// Create an encoder with all the defaults
422-
val encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()
423-
val result: String = encoder.encode("myPassword")
424-
assertTrue(encoder.matches("myPassword", result))
425-
----
426-
======
245+
include-code::./Pbkdf2PasswordEncoderUsage[tag=pbkdf2PasswordEncoder,indent=0]
427246

428247
[[authentication-password-storage-scrypt]]
429248
== SCryptPasswordEncoder
@@ -433,28 +252,7 @@ To defeat password cracking on custom hardware, scrypt is a deliberately slow al
433252
Like other adaptive one-way functions, it should be tuned to take about 1 second to verify a password on your system.
434253

435254
.SCryptPasswordEncoder
436-
[tabs]
437-
======
438-
Java::
439-
+
440-
[source,java,role="primary"]
441-
----
442-
// Create an encoder with all the defaults
443-
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8();
444-
String result = encoder.encode("myPassword");
445-
assertTrue(encoder.matches("myPassword", result));
446-
----
447-
448-
Kotlin::
449-
+
450-
[source,kotlin,role="secondary"]
451-
----
452-
// Create an encoder with all the defaults
453-
val encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()
454-
val result: String = encoder.encode("myPassword")
455-
assertTrue(encoder.matches("myPassword", result))
456-
----
457-
======
255+
include-code::./SCryptPasswordEncoderUsage[tag=sCryptPasswordEncoder,indent=0]
458256

459257
[[authentication-password-storage-other]]
460258
== Other ``PasswordEncoder``s
@@ -606,86 +404,4 @@ However, just a 401 or the redirect is not so useful in that case, it will cause
606404
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:
607405

608406
.Using CompromisedPasswordChecker
609-
[tabs]
610-
======
611-
Java::
612-
+
613-
[source,java,role="primary"]
614-
----
615-
@Bean
616-
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
617-
http
618-
.authorizeHttpRequests(authorize -> authorize
619-
.anyRequest().authenticated()
620-
)
621-
.formLogin((login) -> login
622-
.failureHandler(new CompromisedPasswordAuthenticationFailureHandler())
623-
);
624-
return http.build();
625-
}
626-
627-
@Bean
628-
public CompromisedPasswordChecker compromisedPasswordChecker() {
629-
return new HaveIBeenPwnedRestApiPasswordChecker();
630-
}
631-
632-
static class CompromisedPasswordAuthenticationFailureHandler implements AuthenticationFailureHandler {
633-
634-
private final SimpleUrlAuthenticationFailureHandler defaultFailureHandler = new SimpleUrlAuthenticationFailureHandler(
635-
"/login?error");
636-
637-
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
638-
639-
@Override
640-
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
641-
AuthenticationException exception) throws IOException, ServletException {
642-
if (exception instanceof CompromisedPasswordException) {
643-
this.redirectStrategy.sendRedirect(request, response, "/reset-password");
644-
return;
645-
}
646-
this.defaultFailureHandler.onAuthenticationFailure(request, response, exception);
647-
}
648-
649-
}
650-
----
651-
652-
Kotlin::
653-
+
654-
[source,kotlin,role="secondary"]
655-
----
656-
@Bean
657-
open fun filterChain(http:HttpSecurity): SecurityFilterChain {
658-
http {
659-
authorizeHttpRequests {
660-
authorize(anyRequest, authenticated)
661-
}
662-
formLogin {
663-
failureHandler = CompromisedPasswordAuthenticationFailureHandler()
664-
}
665-
}
666-
return http.build()
667-
}
668-
669-
@Bean
670-
open fun compromisedPasswordChecker(): CompromisedPasswordChecker {
671-
return HaveIBeenPwnedRestApiPasswordChecker()
672-
}
673-
674-
class CompromisedPasswordAuthenticationFailureHandler : AuthenticationFailureHandler {
675-
private val defaultFailureHandler = SimpleUrlAuthenticationFailureHandler("/login?error")
676-
private val redirectStrategy = DefaultRedirectStrategy()
677-
678-
override fun onAuthenticationFailure(
679-
request: HttpServletRequest,
680-
response: HttpServletResponse,
681-
exception: AuthenticationException
682-
) {
683-
if (exception is CompromisedPasswordException) {
684-
redirectStrategy.sendRedirect(request, response, "/reset-password")
685-
return
686-
}
687-
defaultFailureHandler.onAuthenticationFailure(request, response, exception)
688-
}
689-
}
690-
----
691-
======
407+
include-code::./CompromisedPasswordCheckerUsage[tag=configuration,indent=0]

0 commit comments

Comments
 (0)