16
16
17
17
package org .springframework .security .config .annotation .web .configurers ;
18
18
19
- import java .io .IOException ;
20
- import java .util .Collection ;
21
19
import java .util .LinkedHashMap ;
22
- import java .util .List ;
23
- import java .util .Map ;
20
+ import java .util .function .Consumer ;
24
21
25
- import jakarta .servlet .ServletException ;
26
- import jakarta .servlet .http .HttpServletRequest ;
27
- import jakarta .servlet .http .HttpServletResponse ;
28
22
import org .jspecify .annotations .Nullable ;
29
23
30
- import org .springframework .security .access .AccessDeniedException ;
31
- import org .springframework .security .authentication .InsufficientAuthenticationException ;
32
- import org .springframework .security .authorization .AuthorityAuthorizationDecision ;
33
- import org .springframework .security .authorization .AuthorizationDeniedException ;
34
24
import org .springframework .security .config .Customizer ;
35
25
import org .springframework .security .config .annotation .web .HttpSecurityBuilder ;
36
26
import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
37
- import org .springframework .security .core .AuthenticationException ;
38
27
import org .springframework .security .core .GrantedAuthority ;
39
28
import org .springframework .security .web .AuthenticationEntryPoint ;
40
29
import org .springframework .security .web .access .AccessDeniedHandler ;
41
30
import org .springframework .security .web .access .AccessDeniedHandlerImpl ;
31
+ import org .springframework .security .web .access .DelegatingMissingAuthorityAccessDeniedHandler ;
42
32
import org .springframework .security .web .access .ExceptionTranslationFilter ;
43
33
import org .springframework .security .web .access .RequestMatcherDelegatingAccessDeniedHandler ;
44
34
import org .springframework .security .web .authentication .DelegatingAuthenticationEntryPoint ;
45
35
import org .springframework .security .web .authentication .Http403ForbiddenEntryPoint ;
46
36
import org .springframework .security .web .savedrequest .HttpSessionRequestCache ;
47
- import org .springframework .security .web .savedrequest .NullRequestCache ;
48
37
import org .springframework .security .web .savedrequest .RequestCache ;
49
- import org .springframework .security .web .util .ThrowableAnalyzer ;
50
- import org .springframework .security .web .util .matcher .AnyRequestMatcher ;
51
38
import org .springframework .security .web .util .matcher .RequestMatcher ;
52
- import org .springframework .util .Assert ;
53
39
54
40
/**
55
41
* Adds exception handling for Spring Security related exceptions to an application. All
@@ -94,7 +80,8 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
94
80
95
81
private LinkedHashMap <RequestMatcher , AccessDeniedHandler > defaultDeniedHandlerMappings = new LinkedHashMap <>();
96
82
97
- private Map <String , LinkedHashMap <RequestMatcher , AuthenticationEntryPoint >> entryPoints = new LinkedHashMap <>();
83
+ private final DelegatingMissingAuthorityAccessDeniedHandler .Builder missingAuthoritiesHandlerBuilder = DelegatingMissingAuthorityAccessDeniedHandler
84
+ .builder ();
98
85
99
86
/**
100
87
* Creates a new instance
@@ -146,6 +133,37 @@ public ExceptionHandlingConfigurer<H> defaultAccessDeniedHandlerFor(AccessDenied
146
133
return this ;
147
134
}
148
135
136
+ /**
137
+ * Sets a default {@link AuthenticationEntryPoint} to be used which prefers being
138
+ * invoked for the provided missing {@link GrantedAuthority}.
139
+ * @param entryPoint the {@link AuthenticationEntryPoint} to use for the given
140
+ * {@code authority}
141
+ * @param authority the authority
142
+ * @return the {@link ExceptionHandlingConfigurer} for further customizations
143
+ * @since 7.0
144
+ */
145
+ public ExceptionHandlingConfigurer <H > defaultAuthenticationEntryPointFor (AuthenticationEntryPoint entryPoint ,
146
+ String authority ) {
147
+ this .missingAuthoritiesHandlerBuilder .addEntryPointFor (entryPoint , authority );
148
+ return this ;
149
+ }
150
+
151
+ /**
152
+ * Sets a default {@link AuthenticationEntryPoint} to be used which prefers being
153
+ * invoked for the provided missing {@link GrantedAuthority}.
154
+ * @param entryPoint a consumer of a
155
+ * {@link DelegatingAuthenticationEntryPoint.Builder} to use for the given
156
+ * {@code authority}
157
+ * @param authority the authority
158
+ * @return the {@link ExceptionHandlingConfigurer} for further customizations
159
+ * @since 7.0
160
+ */
161
+ public ExceptionHandlingConfigurer <H > defaultAuthenticationEntryPointFor (
162
+ Consumer <DelegatingAuthenticationEntryPoint .Builder > entryPoint , String authority ) {
163
+ this .missingAuthoritiesHandlerBuilder .addEntryPointFor (entryPoint , authority );
164
+ return this ;
165
+ }
166
+
149
167
/**
150
168
* Sets the {@link AuthenticationEntryPoint} to be used.
151
169
*
@@ -189,26 +207,6 @@ public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(Authent
189
207
return this ;
190
208
}
191
209
192
- public ExceptionHandlingConfigurer <H > defaultAuthenticationEntryPointFor (AuthenticationEntryPoint entryPoint ,
193
- RequestMatcher preferredMatcher , String authority ) {
194
- this .defaultEntryPointMappings .put (preferredMatcher , entryPoint );
195
- LinkedHashMap <RequestMatcher , AuthenticationEntryPoint > byMatcher = this .entryPoints .get (authority );
196
- if (byMatcher == null ) {
197
- byMatcher = new LinkedHashMap <>();
198
- }
199
- byMatcher .put (preferredMatcher , entryPoint );
200
- this .entryPoints .put (authority , byMatcher );
201
- return this ;
202
- }
203
-
204
- public ExceptionHandlingConfigurer <H > defaultAuthenticationEntryPointFor (AuthenticationEntryPoint entryPoint ,
205
- String authority ) {
206
- LinkedHashMap <RequestMatcher , AuthenticationEntryPoint > byMatcher = new LinkedHashMap <>();
207
- byMatcher .put (AnyRequestMatcher .INSTANCE , entryPoint );
208
- this .entryPoints .put (authority , byMatcher );
209
- return this ;
210
- }
211
-
212
210
/**
213
211
* Gets any explicitly configured {@link AuthenticationEntryPoint}
214
212
* @return
@@ -269,22 +267,10 @@ AuthenticationEntryPoint getAuthenticationEntryPoint(H http) {
269
267
270
268
private AccessDeniedHandler createDefaultDeniedHandler (H http ) {
271
269
AccessDeniedHandler defaults = createDefaultAccessDeniedHandler (http );
272
- if (this .entryPoints .isEmpty ()) {
273
- return defaults ;
274
- }
275
- Map <String , AccessDeniedHandler > deniedHandlers = new LinkedHashMap <>();
276
- for (Map .Entry <String , LinkedHashMap <RequestMatcher , AuthenticationEntryPoint >> entry : this .entryPoints
277
- .entrySet ()) {
278
- AuthenticationEntryPoint entryPoint = entryPointFrom (entry .getValue ());
279
- AuthenticationEntryPointAccessDeniedHandlerAdapter deniedHandler = new AuthenticationEntryPointAccessDeniedHandlerAdapter (
280
- entryPoint );
281
- RequestCache requestCache = http .getSharedObject (RequestCache .class );
282
- if (requestCache != null ) {
283
- deniedHandler .setRequestCache (requestCache );
284
- }
285
- deniedHandlers .put (entry .getKey (), deniedHandler );
286
- }
287
- return new AuthenticationFactorDelegatingAccessDeniedHandler (deniedHandlers , defaults );
270
+ DelegatingMissingAuthorityAccessDeniedHandler deniedHandler = this .missingAuthoritiesHandlerBuilder .build ();
271
+ deniedHandler .setRequestCache (getRequestCache (http ));
272
+ deniedHandler .setDefaultAccessDeniedHandler (defaults );
273
+ return deniedHandler ;
288
274
}
289
275
290
276
private AccessDeniedHandler createDefaultAccessDeniedHandler (H http ) {
@@ -299,29 +285,10 @@ private AccessDeniedHandler createDefaultAccessDeniedHandler(H http) {
299
285
}
300
286
301
287
private AuthenticationEntryPoint createDefaultEntryPoint (H http ) {
302
- AuthenticationEntryPoint defaults = entryPointFrom (this .defaultEntryPointMappings );
303
- if (this .entryPoints .isEmpty ()) {
304
- return defaults ;
305
- }
306
- Map <String , AuthenticationEntryPoint > entryPoints = new LinkedHashMap <>();
307
- for (Map .Entry <String , LinkedHashMap <RequestMatcher , AuthenticationEntryPoint >> entry : this .entryPoints
308
- .entrySet ()) {
309
- entryPoints .put (entry .getKey (), entryPointFrom (entry .getValue ()));
310
- }
311
- return new AuthenticationFactorDelegatingAuthenticationEntryPoint (entryPoints , defaults );
312
- }
313
-
314
- private AuthenticationEntryPoint entryPointFrom (
315
- LinkedHashMap <RequestMatcher , AuthenticationEntryPoint > entryPoints ) {
316
- if (entryPoints .isEmpty ()) {
288
+ if (this .defaultEntryPoint == null ) {
317
289
return new Http403ForbiddenEntryPoint ();
318
290
}
319
- if (entryPoints .size () == 1 ) {
320
- return entryPoints .values ().iterator ().next ();
321
- }
322
- DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint (entryPoints );
323
- entryPoint .setDefaultEntryPoint (entryPoints .values ().iterator ().next ());
324
- return entryPoint ;
291
+ return this .defaultEntryPoint .build ();
325
292
}
326
293
327
294
/**
@@ -340,128 +307,4 @@ private RequestCache getRequestCache(H http) {
340
307
return new HttpSessionRequestCache ();
341
308
}
342
309
343
- private static final class AuthenticationFactorDelegatingAuthenticationEntryPoint
344
- implements AuthenticationEntryPoint {
345
-
346
- private final ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer ();
347
-
348
- private final Map <String , AuthenticationEntryPoint > entryPoints ;
349
-
350
- private final AuthenticationEntryPoint defaults ;
351
-
352
- private AuthenticationFactorDelegatingAuthenticationEntryPoint (
353
- Map <String , AuthenticationEntryPoint > entryPoints , AuthenticationEntryPoint defaults ) {
354
- this .entryPoints = new LinkedHashMap <>(entryPoints );
355
- this .defaults = defaults ;
356
- }
357
-
358
- @ Override
359
- public void commence (HttpServletRequest request , HttpServletResponse response , AuthenticationException ex )
360
- throws IOException , ServletException {
361
- Collection <GrantedAuthority > authorization = authorizationRequest (ex );
362
- entryPoint (authorization ).commence (request , response , ex );
363
- }
364
-
365
- private AuthenticationEntryPoint entryPoint (Collection <GrantedAuthority > authorities ) {
366
- if (authorities == null ) {
367
- return this .defaults ;
368
- }
369
- for (GrantedAuthority needed : authorities ) {
370
- AuthenticationEntryPoint entryPoint = this .entryPoints .get (needed .getAuthority ());
371
- if (entryPoint != null ) {
372
- return entryPoint ;
373
- }
374
- }
375
- return this .defaults ;
376
- }
377
-
378
- private Collection <GrantedAuthority > authorizationRequest (Exception ex ) {
379
- Throwable [] chain = this .throwableAnalyzer .determineCauseChain (ex );
380
- AuthorizationDeniedException denied = (AuthorizationDeniedException ) this .throwableAnalyzer
381
- .getFirstThrowableOfType (AuthorizationDeniedException .class , chain );
382
- if (denied == null ) {
383
- return List .of ();
384
- }
385
- if (!(denied .getAuthorizationResult () instanceof AuthorityAuthorizationDecision authorization )) {
386
- return List .of ();
387
- }
388
- return authorization .getAuthorities ();
389
- }
390
-
391
- }
392
-
393
- private static final class AuthenticationEntryPointAccessDeniedHandlerAdapter implements AccessDeniedHandler {
394
-
395
- private final AuthenticationEntryPoint entryPoint ;
396
-
397
- private RequestCache requestCache = new NullRequestCache ();
398
-
399
- private AuthenticationEntryPointAccessDeniedHandlerAdapter (AuthenticationEntryPoint entryPoint ) {
400
- this .entryPoint = entryPoint ;
401
- }
402
-
403
- void setRequestCache (RequestCache requestCache ) {
404
- Assert .notNull (requestCache , "requestCache cannot be null" );
405
- this .requestCache = requestCache ;
406
- }
407
-
408
- @ Override
409
- public void handle (HttpServletRequest request , HttpServletResponse response , AccessDeniedException denied )
410
- throws IOException , ServletException {
411
- AuthenticationException ex = new InsufficientAuthenticationException ("access denied" , denied );
412
- this .requestCache .saveRequest (request , response );
413
- this .entryPoint .commence (request , response , ex );
414
- }
415
-
416
- }
417
-
418
- private static final class AuthenticationFactorDelegatingAccessDeniedHandler implements AccessDeniedHandler {
419
-
420
- private final ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer ();
421
-
422
- private final Map <String , AccessDeniedHandler > deniedHandlers ;
423
-
424
- private final AccessDeniedHandler defaults ;
425
-
426
- private AuthenticationFactorDelegatingAccessDeniedHandler (Map <String , AccessDeniedHandler > deniedHandlers ,
427
- AccessDeniedHandler defaults ) {
428
- this .deniedHandlers = new LinkedHashMap <>(deniedHandlers );
429
- this .defaults = defaults ;
430
- }
431
-
432
- @ Override
433
- public void handle (HttpServletRequest request , HttpServletResponse response , AccessDeniedException ex )
434
- throws IOException , ServletException {
435
- Collection <GrantedAuthority > authorization = authorizationRequest (ex );
436
- deniedHandler (authorization ).handle (request , response , ex );
437
- }
438
-
439
- private AccessDeniedHandler deniedHandler (Collection <GrantedAuthority > authorities ) {
440
- if (authorities == null ) {
441
- return this .defaults ;
442
- }
443
- for (GrantedAuthority needed : authorities ) {
444
- AccessDeniedHandler deniedHandler = this .deniedHandlers .get (needed .getAuthority ());
445
- if (deniedHandler != null ) {
446
- return deniedHandler ;
447
- }
448
- }
449
- return this .defaults ;
450
- }
451
-
452
- private Collection <GrantedAuthority > authorizationRequest (Exception ex ) {
453
- Throwable [] chain = this .throwableAnalyzer .determineCauseChain (ex );
454
- AuthorizationDeniedException denied = (AuthorizationDeniedException ) this .throwableAnalyzer
455
- .getFirstThrowableOfType (AuthorizationDeniedException .class , chain );
456
- if (denied == null ) {
457
- return List .of ();
458
- }
459
- if (!(denied .getAuthorizationResult () instanceof AuthorityAuthorizationDecision authorization )) {
460
- return List .of ();
461
- }
462
- return authorization .getAuthorities ();
463
- }
464
-
465
- }
466
-
467
310
}
0 commit comments