2424
2525import org .springframework .context .ApplicationContext ;
2626import org .springframework .http .HttpMethod ;
27- import org .springframework .security .authentication .AuthenticationManager ;
2827import org .springframework .security .authentication .AuthenticationProvider ;
2928import org .springframework .security .authentication .ott .GenerateOneTimeTokenRequest ;
3029import org .springframework .security .authentication .ott .InMemoryOneTimeTokenService ;
3332import org .springframework .security .authentication .ott .OneTimeTokenService ;
3433import org .springframework .security .config .Customizer ;
3534import org .springframework .security .config .annotation .web .HttpSecurityBuilder ;
35+ import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
36+ import org .springframework .security .config .annotation .web .configuration .EnableWebSecurity ;
37+ import org .springframework .security .config .annotation .web .configurers .AbstractAuthenticationFilterConfigurer ;
3638import org .springframework .security .config .annotation .web .configurers .AbstractHttpConfigurer ;
3739import org .springframework .security .core .Authentication ;
3840import org .springframework .security .core .userdetails .UserDetailsService ;
3941import org .springframework .security .web .authentication .AuthenticationConverter ;
4042import org .springframework .security .web .authentication .AuthenticationFailureHandler ;
41- import org .springframework .security .web .authentication .AuthenticationFilter ;
4243import org .springframework .security .web .authentication .AuthenticationSuccessHandler ;
4344import org .springframework .security .web .authentication .SavedRequestAwareAuthenticationSuccessHandler ;
4445import org .springframework .security .web .authentication .SimpleUrlAuthenticationFailureHandler ;
4546import org .springframework .security .web .authentication .ott .DefaultGenerateOneTimeTokenRequestResolver ;
4647import org .springframework .security .web .authentication .ott .GenerateOneTimeTokenFilter ;
4748import org .springframework .security .web .authentication .ott .GenerateOneTimeTokenRequestResolver ;
4849import org .springframework .security .web .authentication .ott .OneTimeTokenAuthenticationConverter ;
50+ import org .springframework .security .web .authentication .ott .OneTimeTokenAuthenticationFilter ;
4951import org .springframework .security .web .authentication .ott .OneTimeTokenGenerationSuccessHandler ;
5052import org .springframework .security .web .authentication .ui .DefaultLoginPageGeneratingFilter ;
5153import org .springframework .security .web .authentication .ui .DefaultOneTimeTokenSubmitPageGeneratingFilter ;
5254import org .springframework .security .web .authentication .ui .DefaultResourcesFilter ;
53- import org .springframework .security .web .context .HttpSessionSecurityContextRepository ;
54- import org .springframework .security .web .context .SecurityContextRepository ;
5555import org .springframework .security .web .csrf .CsrfToken ;
56+ import org .springframework .security .web .util .matcher .RequestMatcher ;
5657import org .springframework .util .Assert ;
5758import org .springframework .util .StringUtils ;
5859
5960import static org .springframework .security .web .util .matcher .AntPathRequestMatcher .antMatcher ;
6061
61- public final class OneTimeTokenLoginConfigurer <H extends HttpSecurityBuilder <H >>
62- extends AbstractHttpConfigurer <OneTimeTokenLoginConfigurer <H >, H > {
62+ /**
63+ * An {@link AbstractHttpConfigurer} for One-Time Token Login.
64+ *
65+ * <p>
66+ * One-Time Token Login provides an application with the capability to have users log in
67+ * by obtaining a single-use token out of band, for example through email.
68+ *
69+ * <p>
70+ * Defaults are provided for all configuration options, with the only required
71+ * configuration being
72+ * {@link #tokenGenerationSuccessHandler(OneTimeTokenGenerationSuccessHandler)}.
73+ * Alternatively, a {@link OneTimeTokenGenerationSuccessHandler} {@code @Bean} may be
74+ * registered instead.
75+ *
76+ * <h2>Security Filters</h2>
77+ *
78+ * The following {@code Filter}s are populated:
79+ *
80+ * <ul>
81+ * <li>{@link DefaultOneTimeTokenSubmitPageGeneratingFilter}</li>
82+ * <li>{@link GenerateOneTimeTokenFilter}</li>
83+ * <li>{@link OneTimeTokenAuthenticationFilter}</li>
84+ * </ul>
85+ *
86+ * <h2>Shared Objects Used</h2>
87+ *
88+ * The following shared objects are used:
89+ *
90+ * <ul>
91+ * <li>{@link DefaultLoginPageGeneratingFilter} - if {@link #loginPage(String)} is not
92+ * configured and {@code DefaultLoginPageGeneratingFilter} is available, then a default
93+ * login page will be made available</li>
94+ * </ul>
95+ *
96+ * @author Marcus Da Coregio
97+ * @author Daniel Garnier-Moiroux
98+ * @since 6.4
99+ * @see HttpSecurity#oneTimeTokenLogin(Customizer)
100+ * @see DefaultOneTimeTokenSubmitPageGeneratingFilter
101+ * @see GenerateOneTimeTokenFilter
102+ * @see OneTimeTokenAuthenticationFilter
103+ * @see AbstractAuthenticationFilterConfigurer
104+ */
105+ public final class OneTimeTokenLoginConfigurer <H extends HttpSecurityBuilder <H >> extends
106+ AbstractAuthenticationFilterConfigurer <H , OneTimeTokenLoginConfigurer <H >, OneTimeTokenAuthenticationFilter > {
63107
64108 private final ApplicationContext context ;
65109
66110 private OneTimeTokenService oneTimeTokenService ;
67111
68- private AuthenticationConverter authenticationConverter = new OneTimeTokenAuthenticationConverter ();
69-
70- private AuthenticationFailureHandler authenticationFailureHandler ;
71-
72- private AuthenticationSuccessHandler authenticationSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler ();
73-
74- private String defaultSubmitPageUrl = "/login/ott" ;
112+ private String defaultSubmitPageUrl = DefaultOneTimeTokenSubmitPageGeneratingFilter .DEFAULT_SUBMIT_PAGE_URL ;
75113
76114 private boolean submitPageEnabled = true ;
77115
78- private String loginProcessingUrl = "/login/ott" ;
79-
80- private String tokenGeneratingUrl = "/ott/generate" ;
116+ private String tokenGeneratingUrl = GenerateOneTimeTokenFilter .DEFAULT_GENERATE_URL ;
81117
82118 private OneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler ;
83119
@@ -86,55 +122,51 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
86122 private GenerateOneTimeTokenRequestResolver requestResolver ;
87123
88124 public OneTimeTokenLoginConfigurer (ApplicationContext context ) {
125+ super (new OneTimeTokenAuthenticationFilter (), OneTimeTokenAuthenticationFilter .DEFAULT_LOGIN_PROCESSING_URL );
89126 this .context = context ;
90127 }
91128
92129 @ Override
93- public void init (H http ) {
130+ public void init (H http ) throws Exception {
131+ super .init (http );
94132 AuthenticationProvider authenticationProvider = getAuthenticationProvider (http );
95133 http .authenticationProvider (postProcess (authenticationProvider ));
96- configureDefaultLoginPage (http );
134+ intiDefaultLoginFilter (http );
135+ }
136+
137+ private AuthenticationProvider getAuthenticationProvider (H http ) {
138+ if (this .authenticationProvider != null ) {
139+ return this .authenticationProvider ;
140+ }
141+ UserDetailsService userDetailsService = getContext ().getBean (UserDetailsService .class );
142+ this .authenticationProvider = new OneTimeTokenAuthenticationProvider (getOneTimeTokenService (http ),
143+ userDetailsService );
144+ return this .authenticationProvider ;
97145 }
98146
99- private void configureDefaultLoginPage (H http ) {
147+ private void intiDefaultLoginFilter (H http ) {
100148 DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
101149 .getSharedObject (DefaultLoginPageGeneratingFilter .class );
102- if (loginPageGeneratingFilter == null ) {
150+ if (loginPageGeneratingFilter == null || isCustomLoginPage () ) {
103151 return ;
104152 }
105153 loginPageGeneratingFilter .setOneTimeTokenEnabled (true );
106154 loginPageGeneratingFilter .setOneTimeTokenGenerationUrl (this .tokenGeneratingUrl );
107- if (this .authenticationFailureHandler == null
108- && StringUtils .hasText (loginPageGeneratingFilter .getLoginPageUrl ())) {
109- this .authenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler (
110- loginPageGeneratingFilter .getLoginPageUrl () + "?error" );
155+
156+ if (!StringUtils .hasText (loginPageGeneratingFilter .getLoginPageUrl ())) {
157+ loginPageGeneratingFilter .setLoginPageUrl (DefaultLoginPageGeneratingFilter .DEFAULT_LOGIN_PAGE_URL );
158+ loginPageGeneratingFilter .setFailureUrl (DefaultLoginPageGeneratingFilter .DEFAULT_LOGIN_PAGE_URL + "?"
159+ + DefaultLoginPageGeneratingFilter .ERROR_PARAMETER_NAME );
160+ loginPageGeneratingFilter
161+ .setLogoutSuccessUrl (DefaultLoginPageGeneratingFilter .DEFAULT_LOGIN_PAGE_URL + "?logout" );
111162 }
112163 }
113164
114165 @ Override
115- public void configure (H http ) {
166+ public void configure (H http ) throws Exception {
167+ super .configure (http );
116168 configureSubmitPage (http );
117169 configureOttGenerateFilter (http );
118- configureOttAuthenticationFilter (http );
119- }
120-
121- private void configureOttAuthenticationFilter (H http ) {
122- AuthenticationManager authenticationManager = http .getSharedObject (AuthenticationManager .class );
123- AuthenticationFilter oneTimeTokenAuthenticationFilter = new AuthenticationFilter (authenticationManager ,
124- this .authenticationConverter );
125- oneTimeTokenAuthenticationFilter .setSecurityContextRepository (getSecurityContextRepository (http ));
126- oneTimeTokenAuthenticationFilter .setRequestMatcher (antMatcher (HttpMethod .POST , this .loginProcessingUrl ));
127- oneTimeTokenAuthenticationFilter .setFailureHandler (getAuthenticationFailureHandler ());
128- oneTimeTokenAuthenticationFilter .setSuccessHandler (this .authenticationSuccessHandler );
129- http .addFilter (postProcess (oneTimeTokenAuthenticationFilter ));
130- }
131-
132- private SecurityContextRepository getSecurityContextRepository (H http ) {
133- SecurityContextRepository securityContextRepository = http .getSharedObject (SecurityContextRepository .class );
134- if (securityContextRepository != null ) {
135- return securityContextRepository ;
136- }
137- return new HttpSessionSecurityContextRepository ();
138170 }
139171
140172 private void configureOttGenerateFilter (H http ) {
@@ -166,18 +198,13 @@ private void configureSubmitPage(H http) {
166198 DefaultOneTimeTokenSubmitPageGeneratingFilter submitPage = new DefaultOneTimeTokenSubmitPageGeneratingFilter ();
167199 submitPage .setResolveHiddenInputs (this ::hiddenInputs );
168200 submitPage .setRequestMatcher (antMatcher (HttpMethod .GET , this .defaultSubmitPageUrl ));
169- submitPage .setLoginProcessingUrl (this .loginProcessingUrl );
201+ submitPage .setLoginProcessingUrl (this .getLoginProcessingUrl () );
170202 http .addFilter (postProcess (submitPage ));
171203 }
172204
173- private AuthenticationProvider getAuthenticationProvider (H http ) {
174- if (this .authenticationProvider != null ) {
175- return this .authenticationProvider ;
176- }
177- UserDetailsService userDetailsService = getContext ().getBean (UserDetailsService .class );
178- this .authenticationProvider = new OneTimeTokenAuthenticationProvider (getOneTimeTokenService (http ),
179- userDetailsService );
180- return this .authenticationProvider ;
205+ @ Override
206+ protected RequestMatcher createLoginProcessingUrlMatcher (String loginProcessingUrl ) {
207+ return antMatcher (HttpMethod .POST , loginProcessingUrl );
181208 }
182209
183210 /**
@@ -217,14 +244,25 @@ public OneTimeTokenLoginConfigurer<H> tokenGenerationSuccessHandler(
217244 * Only POST requests are processed, for that reason make sure that you pass a valid
218245 * CSRF token if CSRF protection is enabled.
219246 * @param loginProcessingUrl
220- * @see org.springframework.security.config.annotation.web.builders. HttpSecurity#csrf(Customizer)
247+ * @see HttpSecurity#csrf(Customizer)
221248 */
222249 public OneTimeTokenLoginConfigurer <H > loginProcessingUrl (String loginProcessingUrl ) {
223250 Assert .hasText (loginProcessingUrl , "loginProcessingUrl cannot be null or empty" );
224- this .loginProcessingUrl = loginProcessingUrl ;
251+ super .loginProcessingUrl ( loginProcessingUrl ) ;
225252 return this ;
226253 }
227254
255+ /**
256+ * Specifies the URL to send users to if login is required. If used with
257+ * {@link EnableWebSecurity} a default login page will be generated when this
258+ * attribute is not specified.
259+ * @param loginPage
260+ */
261+ @ Override
262+ public OneTimeTokenLoginConfigurer <H > loginPage (String loginPage ) {
263+ return super .loginPage (loginPage );
264+ }
265+
228266 /**
229267 * Configures whether the default one-time token submit page should be shown. This
230268 * will prevent the {@link DefaultOneTimeTokenSubmitPageGeneratingFilter} to be
@@ -269,7 +307,7 @@ public OneTimeTokenLoginConfigurer<H> tokenService(OneTimeTokenService oneTimeTo
269307 */
270308 public OneTimeTokenLoginConfigurer <H > authenticationConverter (AuthenticationConverter authenticationConverter ) {
271309 Assert .notNull (authenticationConverter , "authenticationConverter cannot be null" );
272- this .authenticationConverter = authenticationConverter ;
310+ this .getAuthenticationFilter (). setAuthenticationConverter ( authenticationConverter ) ;
273311 return this ;
274312 }
275313
@@ -279,11 +317,13 @@ public OneTimeTokenLoginConfigurer<H> authenticationConverter(AuthenticationConv
279317 * {@link SimpleUrlAuthenticationFailureHandler}
280318 * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} to use
281319 * when authentication fails.
320+ * @deprecated Use {@link #failureHandler(AuthenticationFailureHandler)} instead
282321 */
322+ @ Deprecated (since = "6.5" )
283323 public OneTimeTokenLoginConfigurer <H > authenticationFailureHandler (
284324 AuthenticationFailureHandler authenticationFailureHandler ) {
285325 Assert .notNull (authenticationFailureHandler , "authenticationFailureHandler cannot be null" );
286- this . authenticationFailureHandler = authenticationFailureHandler ;
326+ super . failureHandler ( authenticationFailureHandler ) ;
287327 return this ;
288328 }
289329
@@ -292,22 +332,16 @@ public OneTimeTokenLoginConfigurer<H> authenticationFailureHandler(
292332 * {@link SavedRequestAwareAuthenticationSuccessHandler} with no additional properties
293333 * set.
294334 * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler}.
335+ * @deprecated Use {@link #successHandler(AuthenticationSuccessHandler)} instead
295336 */
337+ @ Deprecated (since = "6.5" )
296338 public OneTimeTokenLoginConfigurer <H > authenticationSuccessHandler (
297339 AuthenticationSuccessHandler authenticationSuccessHandler ) {
298340 Assert .notNull (authenticationSuccessHandler , "authenticationSuccessHandler cannot be null" );
299- this . authenticationSuccessHandler = authenticationSuccessHandler ;
341+ super . successHandler ( authenticationSuccessHandler ) ;
300342 return this ;
301343 }
302344
303- private AuthenticationFailureHandler getAuthenticationFailureHandler () {
304- if (this .authenticationFailureHandler != null ) {
305- return this .authenticationFailureHandler ;
306- }
307- this .authenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler ("/login?error" );
308- return this .authenticationFailureHandler ;
309- }
310-
311345 /**
312346 * Use this {@link GenerateOneTimeTokenRequestResolver} when resolving
313347 * {@link GenerateOneTimeTokenRequest} from {@link HttpServletRequest}. By default,
0 commit comments