2323
2424import org .springframework .context .ApplicationContext ;
2525import org .springframework .http .HttpMethod ;
26- import org .springframework .security .authentication .AuthenticationManager ;
2726import org .springframework .security .authentication .AuthenticationProvider ;
2827import org .springframework .security .authentication .ott .GenerateOneTimeTokenRequest ;
2928import org .springframework .security .authentication .ott .InMemoryOneTimeTokenService ;
3231import org .springframework .security .authentication .ott .OneTimeTokenService ;
3332import org .springframework .security .config .Customizer ;
3433import org .springframework .security .config .annotation .web .HttpSecurityBuilder ;
34+ import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
35+ import org .springframework .security .config .annotation .web .configuration .EnableWebSecurity ;
36+ import org .springframework .security .config .annotation .web .configurers .AbstractAuthenticationFilterConfigurer ;
3537import org .springframework .security .config .annotation .web .configurers .AbstractHttpConfigurer ;
3638import org .springframework .security .core .Authentication ;
3739import org .springframework .security .core .userdetails .UserDetailsService ;
4951import org .springframework .security .web .authentication .ui .DefaultLoginPageGeneratingFilter ;
5052import org .springframework .security .web .authentication .ui .DefaultOneTimeTokenSubmitPageGeneratingFilter ;
5153import org .springframework .security .web .authentication .ui .DefaultResourcesFilter ;
52- import org .springframework .security .web .context .HttpSessionSecurityContextRepository ;
53- import org .springframework .security .web .context .SecurityContextRepository ;
5454import org .springframework .security .web .csrf .CsrfToken ;
55+ import org .springframework .security .web .util .matcher .RequestMatcher ;
5556import org .springframework .util .Assert ;
5657import org .springframework .util .StringUtils ;
5758
5859import static org .springframework .security .web .util .matcher .AntPathRequestMatcher .antMatcher ;
5960
60- public final class OneTimeTokenLoginConfigurer <H extends HttpSecurityBuilder <H >>
61- extends AbstractHttpConfigurer <OneTimeTokenLoginConfigurer <H >, H > {
61+ /**
62+ * An {@link AbstractHttpConfigurer} for One-Time Token Login.
63+ *
64+ * <p>
65+ * One-Time Token Login provides an application with the capability to have users log in
66+ * by obtaining a single-use token out of band, for example through email.
67+ *
68+ * <p>
69+ * Defaults are provided for all configuration options, with the only required
70+ * configuration being
71+ * {@link #tokenGenerationSuccessHandler(OneTimeTokenGenerationSuccessHandler)}.
72+ * Alternatively, a {@link OneTimeTokenGenerationSuccessHandler} {@code @Bean} may be
73+ * registered instead.
74+ *
75+ * <h2>Security Filters</h2>
76+ *
77+ * The following {@code Filter}s are populated:
78+ *
79+ * <ul>
80+ * <li>{@link DefaultOneTimeTokenSubmitPageGeneratingFilter}</li>
81+ * <li>{@link GenerateOneTimeTokenFilter}</li>
82+ * <li>{@link OneTimeTokenAuthenticationFilter}</li>
83+ * </ul>
84+ *
85+ * <h2>Shared Objects Used</h2>
86+ *
87+ * The following shared objects are used:
88+ *
89+ * <ul>
90+ * <li>{@link DefaultLoginPageGeneratingFilter} - if {@link #loginPage(String)} is not
91+ * configured and {@code DefaultLoginPageGeneratingFilter} is available, then a default
92+ * login page will be made available</li>
93+ * </ul>
94+ *
95+ * @author Marcus Da Coregio
96+ * @author Daniel Garnier-Moiroux
97+ * @since 6.4
98+ * @see HttpSecurity#oneTimeTokenLogin(Customizer)
99+ * @see DefaultOneTimeTokenSubmitPageGeneratingFilter
100+ * @see GenerateOneTimeTokenFilter
101+ * @see OneTimeTokenAuthenticationFilter
102+ * @see AbstractAuthenticationFilterConfigurer
103+ */
104+ public final class OneTimeTokenLoginConfigurer <H extends HttpSecurityBuilder <H >> extends
105+ AbstractAuthenticationFilterConfigurer <H , OneTimeTokenLoginConfigurer <H >, OneTimeTokenAuthenticationFilter > {
62106
63107 private final ApplicationContext context ;
64108
65109 private OneTimeTokenService oneTimeTokenService ;
66110
67- private AuthenticationConverter authenticationConverter = new OneTimeTokenAuthenticationConverter ();
68-
69- private AuthenticationFailureHandler authenticationFailureHandler ;
70-
71- private AuthenticationSuccessHandler authenticationSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler ();
72-
73- private String defaultSubmitPageUrl = "/login/ott" ;
111+ private String defaultSubmitPageUrl = DefaultOneTimeTokenSubmitPageGeneratingFilter .DEFAULT_SUBMIT_PAGE_URL ;
74112
75113 private boolean submitPageEnabled = true ;
76114
77115 private String loginProcessingUrl = OneTimeTokenAuthenticationFilter .DEFAULT_LOGIN_PROCESSING_URL ;
78116
79- private String tokenGeneratingUrl = "/ott/generate" ;
117+ private String tokenGeneratingUrl = GenerateOneTimeTokenFilter . DEFAULT_GENERATE_URL ;
80118
81119 private OneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler ;
82120
@@ -85,58 +123,41 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
85123 private GenerateOneTimeTokenRequestResolver requestResolver ;
86124
87125 public OneTimeTokenLoginConfigurer (ApplicationContext context ) {
126+ super (new OneTimeTokenAuthenticationFilter (), OneTimeTokenAuthenticationFilter .DEFAULT_LOGIN_PROCESSING_URL );
88127 this .context = context ;
89128 }
90129
91130 @ Override
92- public void init (H http ) {
131+ public void init (H http ) throws Exception {
132+ super .init (http );
93133 AuthenticationProvider authenticationProvider = getAuthenticationProvider ();
94134 http .authenticationProvider (postProcess (authenticationProvider ));
95- configureDefaultLoginPage (http );
135+ intiDefaultLoginFilter (http );
96136 }
97137
98- private void configureDefaultLoginPage (H http ) {
138+ private void intiDefaultLoginFilter (H http ) {
99139 DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
100140 .getSharedObject (DefaultLoginPageGeneratingFilter .class );
101- if (loginPageGeneratingFilter == null ) {
141+ if (loginPageGeneratingFilter == null || isCustomLoginPage () ) {
102142 return ;
103143 }
104144 loginPageGeneratingFilter .setOneTimeTokenEnabled (true );
105145 loginPageGeneratingFilter .setOneTimeTokenGenerationUrl (this .tokenGeneratingUrl );
106- if (this .authenticationFailureHandler == null
107- && StringUtils .hasText (loginPageGeneratingFilter .getLoginPageUrl ())) {
108- this .authenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler (
109- loginPageGeneratingFilter .getLoginPageUrl () + "?error" );
146+
147+ if (!StringUtils .hasText (loginPageGeneratingFilter .getLoginPageUrl ())) {
148+ loginPageGeneratingFilter .setLoginPageUrl (DefaultLoginPageGeneratingFilter .DEFAULT_LOGIN_PAGE_URL );
149+ loginPageGeneratingFilter .setFailureUrl (DefaultLoginPageGeneratingFilter .DEFAULT_LOGIN_PAGE_URL + "?"
150+ + DefaultLoginPageGeneratingFilter .ERROR_PARAMETER_NAME );
151+ loginPageGeneratingFilter
152+ .setLogoutSuccessUrl (DefaultLoginPageGeneratingFilter .DEFAULT_LOGIN_PAGE_URL + "?logout" );
110153 }
111154 }
112155
113156 @ Override
114- public void configure (H http ) {
157+ public void configure (H http ) throws Exception {
158+ super .configure (http );
115159 configureSubmitPage (http );
116160 configureOttGenerateFilter (http );
117- configureOttAuthenticationFilter (http );
118- }
119-
120- private void configureOttAuthenticationFilter (H http ) {
121- AuthenticationManager authenticationManager = http .getSharedObject (AuthenticationManager .class );
122- OneTimeTokenAuthenticationFilter oneTimeTokenAuthenticationFilter = new OneTimeTokenAuthenticationFilter ();
123- oneTimeTokenAuthenticationFilter .setAuthenticationManager (authenticationManager );
124- if (this .loginProcessingUrl != null ) {
125- oneTimeTokenAuthenticationFilter
126- .setRequiresAuthenticationRequestMatcher (antMatcher (HttpMethod .POST , this .loginProcessingUrl ));
127- }
128- oneTimeTokenAuthenticationFilter .setAuthenticationSuccessHandler (this .authenticationSuccessHandler );
129- oneTimeTokenAuthenticationFilter .setAuthenticationFailureHandler (getAuthenticationFailureHandler ());
130- oneTimeTokenAuthenticationFilter .setSecurityContextRepository (getSecurityContextRepository (http ));
131- http .addFilter (postProcess (oneTimeTokenAuthenticationFilter ));
132- }
133-
134- private SecurityContextRepository getSecurityContextRepository (H http ) {
135- SecurityContextRepository securityContextRepository = http .getSharedObject (SecurityContextRepository .class );
136- if (securityContextRepository != null ) {
137- return securityContextRepository ;
138- }
139- return new HttpSessionSecurityContextRepository ();
140161 }
141162
142163 private void configureOttGenerateFilter (H http ) {
@@ -170,7 +191,7 @@ private void configureSubmitPage(H http) {
170191 DefaultOneTimeTokenSubmitPageGeneratingFilter submitPage = new DefaultOneTimeTokenSubmitPageGeneratingFilter ();
171192 submitPage .setResolveHiddenInputs (this ::hiddenInputs );
172193 submitPage .setRequestMatcher (antMatcher (HttpMethod .GET , this .defaultSubmitPageUrl ));
173- submitPage .setLoginProcessingUrl (this .loginProcessingUrl );
194+ submitPage .setLoginProcessingUrl (this .getLoginProcessingUrl () );
174195 http .addFilter (postProcess (submitPage ));
175196 }
176197
@@ -184,6 +205,11 @@ private AuthenticationProvider getAuthenticationProvider() {
184205 return this .authenticationProvider ;
185206 }
186207
208+ @ Override
209+ protected RequestMatcher createLoginProcessingUrlMatcher (String loginProcessingUrl ) {
210+ return antMatcher (HttpMethod .POST , loginProcessingUrl );
211+ }
212+
187213 /**
188214 * Specifies the {@link AuthenticationProvider} to use when authenticating the user.
189215 * @param authenticationProvider
@@ -221,14 +247,25 @@ public OneTimeTokenLoginConfigurer<H> tokenGenerationSuccessHandler(
221247 * Only POST requests are processed, for that reason make sure that you pass a valid
222248 * CSRF token if CSRF protection is enabled.
223249 * @param loginProcessingUrl
224- * @see org.springframework.security.config.annotation.web.builders. HttpSecurity#csrf(Customizer)
250+ * @see HttpSecurity#csrf(Customizer)
225251 */
226252 public OneTimeTokenLoginConfigurer <H > loginProcessingUrl (String loginProcessingUrl ) {
227253 Assert .hasText (loginProcessingUrl , "loginProcessingUrl cannot be null or empty" );
228- this .loginProcessingUrl = loginProcessingUrl ;
254+ super .loginProcessingUrl ( loginProcessingUrl ) ;
229255 return this ;
230256 }
231257
258+ /**
259+ * Specifies the URL to send users to if login is required. If used with
260+ * {@link EnableWebSecurity} a default login page will be generated when this
261+ * attribute is not specified.
262+ * @param loginPage
263+ */
264+ @ Override
265+ public OneTimeTokenLoginConfigurer <H > loginPage (String loginPage ) {
266+ return super .loginPage (loginPage );
267+ }
268+
232269 /**
233270 * Configures whether the default one-time token submit page should be shown. This
234271 * will prevent the {@link DefaultOneTimeTokenSubmitPageGeneratingFilter} to be
@@ -273,7 +310,7 @@ public OneTimeTokenLoginConfigurer<H> tokenService(OneTimeTokenService oneTimeTo
273310 */
274311 public OneTimeTokenLoginConfigurer <H > authenticationConverter (AuthenticationConverter authenticationConverter ) {
275312 Assert .notNull (authenticationConverter , "authenticationConverter cannot be null" );
276- this .authenticationConverter = authenticationConverter ;
313+ this .getAuthenticationFilter (). setAuthenticationConverter ( authenticationConverter ) ;
277314 return this ;
278315 }
279316
@@ -283,11 +320,13 @@ public OneTimeTokenLoginConfigurer<H> authenticationConverter(AuthenticationConv
283320 * {@link SimpleUrlAuthenticationFailureHandler}
284321 * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} to use
285322 * when authentication fails.
323+ * @deprecated Use {@link #failureHandler(AuthenticationFailureHandler)} instead
286324 */
325+ @ Deprecated (since = "6.5" )
287326 public OneTimeTokenLoginConfigurer <H > authenticationFailureHandler (
288327 AuthenticationFailureHandler authenticationFailureHandler ) {
289328 Assert .notNull (authenticationFailureHandler , "authenticationFailureHandler cannot be null" );
290- this . authenticationFailureHandler = authenticationFailureHandler ;
329+ super . failureHandler ( authenticationFailureHandler ) ;
291330 return this ;
292331 }
293332
@@ -296,22 +335,16 @@ public OneTimeTokenLoginConfigurer<H> authenticationFailureHandler(
296335 * {@link SavedRequestAwareAuthenticationSuccessHandler} with no additional properties
297336 * set.
298337 * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler}.
338+ * @deprecated Use {@link #successHandler(AuthenticationSuccessHandler)} instead
299339 */
340+ @ Deprecated (since = "6.5" )
300341 public OneTimeTokenLoginConfigurer <H > authenticationSuccessHandler (
301342 AuthenticationSuccessHandler authenticationSuccessHandler ) {
302343 Assert .notNull (authenticationSuccessHandler , "authenticationSuccessHandler cannot be null" );
303- this . authenticationSuccessHandler = authenticationSuccessHandler ;
344+ super . successHandler ( authenticationSuccessHandler ) ;
304345 return this ;
305346 }
306347
307- private AuthenticationFailureHandler getAuthenticationFailureHandler () {
308- if (this .authenticationFailureHandler != null ) {
309- return this .authenticationFailureHandler ;
310- }
311- this .authenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler ("/login?error" );
312- return this .authenticationFailureHandler ;
313- }
314-
315348 /**
316349 * Use this {@link GenerateOneTimeTokenRequestResolver} when resolving
317350 * {@link GenerateOneTimeTokenRequest} from {@link HttpServletRequest}. By default,
0 commit comments