16
16
17
17
package org .springframework .security .config .annotation .web .configurers ;
18
18
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 ;
19
29
import org .springframework .security .config .annotation .web .HttpSecurityBuilder ;
20
30
import org .springframework .security .config .annotation .web .RequestMatcherFactory ;
31
+ import org .springframework .security .core .userdetails .UserDetails ;
32
+ import org .springframework .security .crypto .factory .PasswordEncoderFactories ;
33
+ import org .springframework .security .crypto .password .PasswordEncoder ;
21
34
import org .springframework .security .web .RequestMatcherRedirectFilter ;
22
35
import org .springframework .security .web .authentication .UsernamePasswordAuthenticationFilter ;
36
+ import org .springframework .security .web .authentication .password .ChangeCompromisedPasswordAdvisor ;
37
+ import org .springframework .security .web .authentication .password .ChangePasswordAdviceHandler ;
38
+ import org .springframework .security .web .authentication .password .ChangePasswordAdviceRepository ;
39
+ import org .springframework .security .web .authentication .password .ChangePasswordAdvisingFilter ;
40
+ import org .springframework .security .web .authentication .password .ChangePasswordProcessingFilter ;
41
+ import org .springframework .security .web .authentication .password .DefaultChangePasswordPageGeneratingFilter ;
42
+ import org .springframework .security .web .authentication .password .HttpSessionChangePasswordAdviceRepository ;
43
+ import org .springframework .security .web .authentication .password .SimpleChangePasswordAdviceHandler ;
44
+ import org .springframework .security .web .savedrequest .RequestCacheAwareFilter ;
45
+ import org .springframework .security .web .servlet .util .matcher .PathPatternRequestMatcher ;
23
46
import org .springframework .util .Assert ;
24
47
25
48
/**
29
52
* @since 5.6
30
53
*/
31
54
public final class PasswordManagementConfigurer <B extends HttpSecurityBuilder <B >>
32
- extends AbstractHttpConfigurer <PasswordManagementConfigurer <B >, B > {
55
+ extends AbstractHttpConfigurer <PasswordManagementConfigurer <B >, B > implements ApplicationContextAware {
33
56
34
57
private static final String WELL_KNOWN_CHANGE_PASSWORD_PATTERN = "/.well-known/change-password" ;
35
58
36
- private static final String DEFAULT_CHANGE_PASSWORD_PAGE = "/change-password" ;
59
+ private static final String DEFAULT_CHANGE_PASSWORD_PAGE = DefaultChangePasswordPageGeneratingFilter .DEFAULT_CHANGE_PASSWORD_URL ;
60
+
61
+ private ApplicationContext context ;
62
+
63
+ private boolean customChangePasswordPage = false ;
37
64
38
65
private String changePasswordPage = DEFAULT_CHANGE_PASSWORD_PAGE ;
39
66
67
+ private String changePasswordProcessingUrl = ChangePasswordProcessingFilter .DEFAULT_PASSWORD_CHANGE_PROCESSING_URL ;
68
+
69
+ private ChangePasswordAdviceRepository changePasswordAdviceRepository ;
70
+
71
+ private ChangePasswordAdvisor changePasswordAdvisor ;
72
+
73
+ private ChangePasswordAdviceHandler changePasswordAdviceHandler ;
74
+
75
+ private UserDetailsPasswordManager userDetailsPasswordManager ;
76
+
40
77
/**
41
78
* Sets the change password page. Defaults to
42
79
* {@link PasswordManagementConfigurer#DEFAULT_CHANGE_PASSWORD_PAGE}.
@@ -46,9 +83,76 @@ public final class PasswordManagementConfigurer<B extends HttpSecurityBuilder<B>
46
83
public PasswordManagementConfigurer <B > changePasswordPage (String changePasswordPage ) {
47
84
Assert .hasText (changePasswordPage , "changePasswordPage cannot be empty" );
48
85
this .changePasswordPage = changePasswordPage ;
86
+ this .customChangePasswordPage = true ;
87
+ return this ;
88
+ }
89
+
90
+ public PasswordManagementConfigurer <B > changePasswordProcessingUrl (String changePasswordProcessingUrl ) {
91
+ this .changePasswordProcessingUrl = changePasswordProcessingUrl ;
92
+ return this ;
93
+ }
94
+
95
+ public PasswordManagementConfigurer <B > changePasswordAdviceRepository (
96
+ ChangePasswordAdviceRepository changePasswordAdviceRepository ) {
97
+ this .changePasswordAdviceRepository = changePasswordAdviceRepository ;
98
+ return this ;
99
+ }
100
+
101
+ public PasswordManagementConfigurer <B > changePasswordAdvisor (ChangePasswordAdvisor changePasswordAdvisor ) {
102
+ this .changePasswordAdvisor = changePasswordAdvisor ;
103
+ return this ;
104
+ }
105
+
106
+ public PasswordManagementConfigurer <B > changePasswordAdviceHandler (
107
+ ChangePasswordAdviceHandler changePasswordAdviceHandler ) {
108
+ this .changePasswordAdviceHandler = changePasswordAdviceHandler ;
49
109
return this ;
50
110
}
51
111
112
+ public PasswordManagementConfigurer <B > userDetailsPasswordManager (
113
+ UserDetailsPasswordManager userDetailsPasswordManager ) {
114
+ this .userDetailsPasswordManager = userDetailsPasswordManager ;
115
+ return this ;
116
+ }
117
+
118
+ @ Override
119
+ public void init (B http ) throws Exception {
120
+ UserDetailsPasswordManager passwordManager = (this .userDetailsPasswordManager == null )
121
+ ? this .context .getBeanProvider (UserDetailsPasswordManager .class ).getIfUnique ()
122
+ : this .userDetailsPasswordManager ;
123
+
124
+ if (passwordManager == null ) {
125
+ return ;
126
+ }
127
+
128
+ ChangePasswordAdviceRepository changePasswordAdviceRepository = (this .changePasswordAdviceRepository != null )
129
+ ? this .changePasswordAdviceRepository
130
+ : this .context .getBeanProvider (ChangePasswordAdviceRepository .class )
131
+ .getIfUnique (HttpSessionChangePasswordAdviceRepository ::new );
132
+
133
+ ChangePasswordAdvisor changePasswordAdvisor = (this .changePasswordAdvisor != null ) ? this .changePasswordAdvisor
134
+ : this .context .getBeanProvider (ChangePasswordAdvisor .class ).getIfUnique (() -> {
135
+ List <ChangePasswordAdvisor > advisors = new ArrayList <>();
136
+ advisors .add (new ChangeCompromisedPasswordAdvisor ());
137
+ advisors .add (new ChangePasswordServiceAdvisor (passwordManager ));
138
+ return new DelegatingChangePasswordAdvisor (advisors );
139
+ });
140
+
141
+ http .setSharedObject (ChangePasswordAdviceRepository .class , changePasswordAdviceRepository );
142
+ http .setSharedObject (UserDetailsPasswordManager .class , passwordManager );
143
+ http .setSharedObject (ChangePasswordAdvisor .class , changePasswordAdvisor );
144
+
145
+ FormLoginConfigurer form = http .getConfigurer (FormLoginConfigurer .class );
146
+ String passwordParameter = (form != null ) ? form .getPasswordParameter () : "password" ;
147
+ http .getConfigurer (SessionManagementConfigurer .class )
148
+ .addSessionAuthenticationStrategy ((authentication , request , response ) -> {
149
+ UserDetails user = (UserDetails ) authentication .getPrincipal ();
150
+ String password = request .getParameter (passwordParameter );
151
+ ChangePasswordAdvice advice = changePasswordAdvisor .advise (user , password );
152
+ changePasswordAdviceRepository .savePasswordAdvice (request , response , advice );
153
+ });
154
+ }
155
+
52
156
/**
53
157
* {@inheritDoc}
54
158
*/
@@ -57,6 +161,42 @@ public void configure(B http) throws Exception {
57
161
RequestMatcherRedirectFilter changePasswordFilter = new RequestMatcherRedirectFilter (
58
162
RequestMatcherFactory .matcher (WELL_KNOWN_CHANGE_PASSWORD_PATTERN ), this .changePasswordPage );
59
163
http .addFilterBefore (postProcess (changePasswordFilter ), UsernamePasswordAuthenticationFilter .class );
164
+
165
+ if (http .getSharedObject (UserDetailsPasswordManager .class ) == null ) {
166
+ return ;
167
+ }
168
+
169
+ PasswordEncoder passwordEncoder = this .context .getBeanProvider (PasswordEncoder .class )
170
+ .getIfUnique (PasswordEncoderFactories ::createDelegatingPasswordEncoder );
171
+
172
+ ChangePasswordAdviceHandler changePasswordAdviceHandler = (this .changePasswordAdviceHandler != null )
173
+ ? this .changePasswordAdviceHandler : this .context .getBeanProvider (ChangePasswordAdviceHandler .class )
174
+ .getIfUnique (() -> new SimpleChangePasswordAdviceHandler (this .changePasswordPage ));
175
+
176
+ if (!this .customChangePasswordPage ) {
177
+ DefaultChangePasswordPageGeneratingFilter page = new DefaultChangePasswordPageGeneratingFilter ();
178
+ http .addFilterBefore (page , RequestCacheAwareFilter .class );
179
+ }
180
+
181
+ ChangePasswordProcessingFilter processing = new ChangePasswordProcessingFilter (
182
+ http .getSharedObject (UserDetailsPasswordManager .class ));
183
+ processing
184
+ .setRequestMatcher (PathPatternRequestMatcher .withDefaults ().matcher (this .changePasswordProcessingUrl ));
185
+ processing .setChangePasswordAdvisor (http .getSharedObject (ChangePasswordAdvisor .class ));
186
+ processing .setChangePasswordAdviceRepository (http .getSharedObject (ChangePasswordAdviceRepository .class ));
187
+ processing .setPasswordEncoder (passwordEncoder );
188
+ processing .setSecurityContextHolderStrategy (getSecurityContextHolderStrategy ());
189
+ http .addFilterBefore (processing , RequestCacheAwareFilter .class );
190
+
191
+ ChangePasswordAdvisingFilter advising = new ChangePasswordAdvisingFilter ();
192
+ advising .setChangePasswordAdviceRepository (http .getSharedObject (ChangePasswordAdviceRepository .class ));
193
+ advising .setChangePasswordAdviceHandler (changePasswordAdviceHandler );
194
+ http .addFilterBefore (advising , RequestCacheAwareFilter .class );
195
+ }
196
+
197
+ @ Override
198
+ public void setApplicationContext (ApplicationContext context ) {
199
+ this .context = context ;
60
200
}
61
201
62
202
}
0 commit comments