Skip to content

Commit dd74722

Browse files
Merge branch '6.3.x'
Closes gh-15306
2 parents 902aff4 + e16ce57 commit dd74722

File tree

1 file changed

+44
-41
lines changed

1 file changed

+44
-41
lines changed

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

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,11 @@ To facilitate that, Spring Security provides integration with the https://haveib
602602

603603
You can either use the `CompromisedPasswordChecker` API by yourself or, if you are using xref:servlet/authentication/passwords/dao-authentication-provider.adoc[the `DaoAuthenticationProvider]` via xref:servlet/authentication/passwords/index.adoc[Spring Security authentication mechanisms], you can provide a `CompromisedPasswordChecker` bean, and it will be automatically picked up by Spring Security configuration.
604604

605-
.Using CompromisedPasswordChecker as a bean
605+
By doing that, when you try to authenticate via Form Login using a weak password, let's say `123456`, you will receive a 401 or be redirected to the `/login?error` page (depending on your user-agent).
606+
However, just a 401 or the redirect is not so useful in that case, it will cause some confusion because the user provided the right password and still was not allowed to log in.
607+
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:
608+
609+
.Using CompromisedPasswordChecker
606610
[tabs]
607611
======
608612
Java::
@@ -615,15 +619,35 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
615619
.authorizeHttpRequests(authorize -> authorize
616620
.anyRequest().authenticated()
617621
)
618-
.formLogin(withDefaults())
619-
.httpBasic(withDefaults());
622+
.formLogin((login) -> login
623+
.failureHandler(new CompromisedPasswordAuthenticationFailureHandler())
624+
);
620625
return http.build();
621626
}
622627
623628
@Bean
624629
public CompromisedPasswordChecker compromisedPasswordChecker() {
625630
return new HaveIBeenPwnedRestApiPasswordChecker();
626631
}
632+
633+
static class CompromisedPasswordAuthenticationFailureHandler implements AuthenticationFailureHandler {
634+
635+
private final SimpleUrlAuthenticationFailureHandler defaultFailureHandler = new SimpleUrlAuthenticationFailureHandler(
636+
"/login?error");
637+
638+
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
639+
640+
@Override
641+
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
642+
AuthenticationException exception) throws IOException, ServletException {
643+
if (exception instanceof CompromisedPasswordException) {
644+
this.redirectStrategy.sendRedirect(request, response, "/reset-password");
645+
return;
646+
}
647+
this.defaultFailureHandler.onAuthenticationFailure(request, response, exception);
648+
}
649+
650+
}
627651
----
628652
629653
Kotlin::
@@ -636,8 +660,9 @@ open fun filterChain(http:HttpSecurity): SecurityFilterChain {
636660
authorizeHttpRequests {
637661
authorize(anyRequest, authenticated)
638662
}
639-
formLogin {}
640-
httpBasic {}
663+
formLogin {
664+
failureHandler = CompromisedPasswordAuthenticationFailureHandler()
665+
}
641666
}
642667
return http.build()
643668
}
@@ -646,44 +671,22 @@ open fun filterChain(http:HttpSecurity): SecurityFilterChain {
646671
open fun compromisedPasswordChecker(): CompromisedPasswordChecker {
647672
return HaveIBeenPwnedRestApiPasswordChecker()
648673
}
649-
----
650-
======
651-
652-
By doing that, when you try to authenticate via HTTP Basic or Form Login using a weak password, let's say `123456`, you will receive a 401 response status code.
653-
However, just a 401 is not so useful in that case, it will cause some confusion because the user provided the right password and still was not allowed to log in.
654-
In such cases, you can handle the `CompromisedPasswordException` to perform your desired logic, like redirecting the user-agent to `/reset-password`, for example:
655-
656-
[tabs]
657-
======
658-
Java::
659-
+
660-
[source,java,role="primary"]
661-
----
662-
@ControllerAdvice
663-
public class MyControllerAdvice {
664-
665-
@ExceptionHandler(CompromisedPasswordException.class)
666-
public String handleCompromisedPasswordException(CompromisedPasswordException ex, RedirectAttributes attributes) {
667-
attributes.addFlashAttribute("error", ex.message);
668-
return "redirect:/reset-password";
669-
}
670-
671-
}
672-
----
673-
674-
Kotlin::
675-
+
676-
[source,kotlin,role="secondary"]
677-
----
678-
@ControllerAdvice
679-
class MyControllerAdvice {
680674
681-
@ExceptionHandler(CompromisedPasswordException::class)
682-
fun handleCompromisedPasswordException(ex: CompromisedPasswordException, attributes: RedirectAttributes): RedirectView {
683-
attributes.addFlashAttribute("error", ex.message)
684-
return RedirectView("/reset-password")
675+
class CompromisedPasswordAuthenticationFailureHandler : AuthenticationFailureHandler {
676+
private val defaultFailureHandler = SimpleUrlAuthenticationFailureHandler("/login?error")
677+
private val redirectStrategy = DefaultRedirectStrategy()
678+
679+
override fun onAuthenticationFailure(
680+
request: HttpServletRequest,
681+
response: HttpServletResponse,
682+
exception: AuthenticationException
683+
) {
684+
if (exception is CompromisedPasswordException) {
685+
redirectStrategy.sendRedirect(request, response, "/reset-password")
686+
return
687+
}
688+
defaultFailureHandler.onAuthenticationFailure(request, response, exception)
685689
}
686-
687690
}
688691
----
689692
======

0 commit comments

Comments
 (0)