1616
1717package org .springframework .security .config .annotation .web .configurers ;
1818
19+ import java .util .ArrayList ;
20+ import java .util .List ;
21+
22+ import org .springframework .context .ApplicationContext ;
23+ import org .springframework .context .ApplicationContextAware ;
24+ import org .springframework .security .authentication .password .ChangePasswordAdvice ;
25+ import org .springframework .security .authentication .password .ChangePasswordAdvisor ;
26+ import org .springframework .security .authentication .password .ChangePasswordServiceAdvisor ;
27+ import org .springframework .security .authentication .password .DelegatingChangePasswordAdvisor ;
28+ import org .springframework .security .authentication .password .UserDetailsPasswordManager ;
1929import org .springframework .security .config .annotation .web .HttpSecurityBuilder ;
30+ import org .springframework .security .core .userdetails .UserDetails ;
31+ import org .springframework .security .crypto .factory .PasswordEncoderFactories ;
32+ import org .springframework .security .crypto .password .PasswordEncoder ;
2033import org .springframework .security .web .RequestMatcherRedirectFilter ;
2134import org .springframework .security .web .authentication .UsernamePasswordAuthenticationFilter ;
35+ import org .springframework .security .web .authentication .password .ChangeCompromisedPasswordAdvisor ;
36+ import org .springframework .security .web .authentication .password .ChangePasswordAdviceHandler ;
37+ import org .springframework .security .web .authentication .password .ChangePasswordAdviceRepository ;
38+ import org .springframework .security .web .authentication .password .ChangePasswordAdvisingFilter ;
39+ import org .springframework .security .web .authentication .password .ChangePasswordProcessingFilter ;
40+ import org .springframework .security .web .authentication .password .DefaultChangePasswordPageGeneratingFilter ;
41+ import org .springframework .security .web .authentication .password .HttpSessionChangePasswordAdviceRepository ;
42+ import org .springframework .security .web .authentication .password .SimpleChangePasswordAdviceHandler ;
43+ import org .springframework .security .web .savedrequest .RequestCacheAwareFilter ;
44+ import org .springframework .security .web .servlet .util .matcher .PathPatternRequestMatcher ;
2245import org .springframework .util .Assert ;
2346
2447/**
2851 * @since 5.6
2952 */
3053public final class PasswordManagementConfigurer <B extends HttpSecurityBuilder <B >>
31- extends AbstractHttpConfigurer <PasswordManagementConfigurer <B >, B > {
54+ extends AbstractHttpConfigurer <PasswordManagementConfigurer <B >, B > implements ApplicationContextAware {
3255
3356 private static final String WELL_KNOWN_CHANGE_PASSWORD_PATTERN = "/.well-known/change-password" ;
3457
35- private static final String DEFAULT_CHANGE_PASSWORD_PAGE = "/change-password" ;
58+ private static final String DEFAULT_CHANGE_PASSWORD_PAGE = DefaultChangePasswordPageGeneratingFilter .DEFAULT_CHANGE_PASSWORD_URL ;
59+
60+ private ApplicationContext context ;
61+
62+ private boolean customChangePasswordPage = false ;
3663
3764 private String changePasswordPage = DEFAULT_CHANGE_PASSWORD_PAGE ;
3865
66+ private String changePasswordProcessingUrl = ChangePasswordProcessingFilter .DEFAULT_PASSWORD_CHANGE_PROCESSING_URL ;
67+
68+ private ChangePasswordAdviceRepository changePasswordAdviceRepository ;
69+
70+ private ChangePasswordAdvisor changePasswordAdvisor ;
71+
72+ private ChangePasswordAdviceHandler changePasswordAdviceHandler ;
73+
74+ private UserDetailsPasswordManager userDetailsPasswordManager ;
75+
3976 /**
4077 * Sets the change password page. Defaults to
4178 * {@link PasswordManagementConfigurer#DEFAULT_CHANGE_PASSWORD_PAGE}.
@@ -45,9 +82,76 @@ public final class PasswordManagementConfigurer<B extends HttpSecurityBuilder<B>
4582 public PasswordManagementConfigurer <B > changePasswordPage (String changePasswordPage ) {
4683 Assert .hasText (changePasswordPage , "changePasswordPage cannot be empty" );
4784 this .changePasswordPage = changePasswordPage ;
85+ this .customChangePasswordPage = true ;
86+ return this ;
87+ }
88+
89+ public PasswordManagementConfigurer <B > changePasswordProcessingUrl (String changePasswordProcessingUrl ) {
90+ this .changePasswordProcessingUrl = changePasswordProcessingUrl ;
91+ return this ;
92+ }
93+
94+ public PasswordManagementConfigurer <B > changePasswordAdviceRepository (
95+ ChangePasswordAdviceRepository changePasswordAdviceRepository ) {
96+ this .changePasswordAdviceRepository = changePasswordAdviceRepository ;
97+ return this ;
98+ }
99+
100+ public PasswordManagementConfigurer <B > changePasswordAdvisor (ChangePasswordAdvisor changePasswordAdvisor ) {
101+ this .changePasswordAdvisor = changePasswordAdvisor ;
102+ return this ;
103+ }
104+
105+ public PasswordManagementConfigurer <B > changePasswordAdviceHandler (
106+ ChangePasswordAdviceHandler changePasswordAdviceHandler ) {
107+ this .changePasswordAdviceHandler = changePasswordAdviceHandler ;
48108 return this ;
49109 }
50110
111+ public PasswordManagementConfigurer <B > userDetailsPasswordManager (
112+ UserDetailsPasswordManager userDetailsPasswordManager ) {
113+ this .userDetailsPasswordManager = userDetailsPasswordManager ;
114+ return this ;
115+ }
116+
117+ @ Override
118+ public void init (B http ) throws Exception {
119+ UserDetailsPasswordManager passwordManager = (this .userDetailsPasswordManager == null )
120+ ? this .context .getBeanProvider (UserDetailsPasswordManager .class ).getIfUnique ()
121+ : this .userDetailsPasswordManager ;
122+
123+ if (passwordManager == null ) {
124+ return ;
125+ }
126+
127+ ChangePasswordAdviceRepository changePasswordAdviceRepository = (this .changePasswordAdviceRepository != null )
128+ ? this .changePasswordAdviceRepository
129+ : this .context .getBeanProvider (ChangePasswordAdviceRepository .class )
130+ .getIfUnique (HttpSessionChangePasswordAdviceRepository ::new );
131+
132+ ChangePasswordAdvisor changePasswordAdvisor = (this .changePasswordAdvisor != null ) ? this .changePasswordAdvisor
133+ : this .context .getBeanProvider (ChangePasswordAdvisor .class ).getIfUnique (() -> {
134+ List <ChangePasswordAdvisor > advisors = new ArrayList <>();
135+ advisors .add (new ChangeCompromisedPasswordAdvisor ());
136+ advisors .add (new ChangePasswordServiceAdvisor (passwordManager ));
137+ return new DelegatingChangePasswordAdvisor (advisors );
138+ });
139+
140+ http .setSharedObject (ChangePasswordAdviceRepository .class , changePasswordAdviceRepository );
141+ http .setSharedObject (UserDetailsPasswordManager .class , passwordManager );
142+ http .setSharedObject (ChangePasswordAdvisor .class , changePasswordAdvisor );
143+
144+ FormLoginConfigurer form = http .getConfigurer (FormLoginConfigurer .class );
145+ String passwordParameter = (form != null ) ? form .getPasswordParameter () : "password" ;
146+ http .getConfigurer (SessionManagementConfigurer .class )
147+ .addSessionAuthenticationStrategy ((authentication , request , response ) -> {
148+ UserDetails user = (UserDetails ) authentication .getPrincipal ();
149+ String password = request .getParameter (passwordParameter );
150+ ChangePasswordAdvice advice = changePasswordAdvisor .advise (user , password );
151+ changePasswordAdviceRepository .savePasswordAdvice (request , response , advice );
152+ });
153+ }
154+
51155 /**
52156 * {@inheritDoc}
53157 */
@@ -56,6 +160,42 @@ public void configure(B http) throws Exception {
56160 RequestMatcherRedirectFilter changePasswordFilter = new RequestMatcherRedirectFilter (
57161 getRequestMatcherBuilder ().matcher (WELL_KNOWN_CHANGE_PASSWORD_PATTERN ), this .changePasswordPage );
58162 http .addFilterBefore (postProcess (changePasswordFilter ), UsernamePasswordAuthenticationFilter .class );
163+
164+ if (http .getSharedObject (UserDetailsPasswordManager .class ) == null ) {
165+ return ;
166+ }
167+
168+ PasswordEncoder passwordEncoder = this .context .getBeanProvider (PasswordEncoder .class )
169+ .getIfUnique (PasswordEncoderFactories ::createDelegatingPasswordEncoder );
170+
171+ ChangePasswordAdviceHandler changePasswordAdviceHandler = (this .changePasswordAdviceHandler != null )
172+ ? this .changePasswordAdviceHandler : this .context .getBeanProvider (ChangePasswordAdviceHandler .class )
173+ .getIfUnique (() -> new SimpleChangePasswordAdviceHandler (this .changePasswordPage ));
174+
175+ if (!this .customChangePasswordPage ) {
176+ DefaultChangePasswordPageGeneratingFilter page = new DefaultChangePasswordPageGeneratingFilter ();
177+ http .addFilterBefore (page , RequestCacheAwareFilter .class );
178+ }
179+
180+ ChangePasswordProcessingFilter processing = new ChangePasswordProcessingFilter (
181+ http .getSharedObject (UserDetailsPasswordManager .class ));
182+ processing
183+ .setRequestMatcher (PathPatternRequestMatcher .withDefaults ().matcher (this .changePasswordProcessingUrl ));
184+ processing .setChangePasswordAdvisor (http .getSharedObject (ChangePasswordAdvisor .class ));
185+ processing .setChangePasswordAdviceRepository (http .getSharedObject (ChangePasswordAdviceRepository .class ));
186+ processing .setPasswordEncoder (passwordEncoder );
187+ processing .setSecurityContextHolderStrategy (getSecurityContextHolderStrategy ());
188+ http .addFilterBefore (processing , RequestCacheAwareFilter .class );
189+
190+ ChangePasswordAdvisingFilter advising = new ChangePasswordAdvisingFilter ();
191+ advising .setChangePasswordAdviceRepository (http .getSharedObject (ChangePasswordAdviceRepository .class ));
192+ advising .setChangePasswordAdviceHandler (changePasswordAdviceHandler );
193+ http .addFilterBefore (advising , RequestCacheAwareFilter .class );
194+ }
195+
196+ @ Override
197+ public void setApplicationContext (ApplicationContext context ) {
198+ this .context = context ;
59199 }
60200
61201}
0 commit comments