1616
1717package org .springframework .security .config .annotation .web .configurers ;
1818
19- import java .io .IOException ;
20- import java .util .Collection ;
2119import java .util .LinkedHashMap ;
22- import java .util .List ;
2320import java .util .Map ;
21+ import java .util .function .Consumer ;
2422
25- import jakarta .servlet .ServletException ;
26- import jakarta .servlet .http .HttpServletRequest ;
27- import jakarta .servlet .http .HttpServletResponse ;
2823import org .jspecify .annotations .Nullable ;
2924
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 ;
3425import org .springframework .security .config .Customizer ;
3526import org .springframework .security .config .annotation .web .HttpSecurityBuilder ;
3627import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
37- import org .springframework .security .core .AuthenticationException ;
38- import org .springframework .security .core .GrantedAuthority ;
3928import org .springframework .security .web .AuthenticationEntryPoint ;
4029import org .springframework .security .web .access .AccessDeniedHandler ;
4130import org .springframework .security .web .access .AccessDeniedHandlerImpl ;
31+ import org .springframework .security .web .access .DelegatingMissingAuthorityAccessDeniedHandler ;
4232import org .springframework .security .web .access .ExceptionTranslationFilter ;
4333import org .springframework .security .web .access .RequestMatcherDelegatingAccessDeniedHandler ;
4434import org .springframework .security .web .authentication .DelegatingAuthenticationEntryPoint ;
4535import org .springframework .security .web .authentication .Http403ForbiddenEntryPoint ;
4636import org .springframework .security .web .savedrequest .HttpSessionRequestCache ;
47- import org .springframework .security .web .savedrequest .NullRequestCache ;
4837import org .springframework .security .web .savedrequest .RequestCache ;
49- import org .springframework .security .web .util .ThrowableAnalyzer ;
50- import org .springframework .security .web .util .matcher .AnyRequestMatcher ;
5138import org .springframework .security .web .util .matcher .RequestMatcher ;
52- import org .springframework .util .Assert ;
5339
5440/**
5541 * Adds exception handling for Spring Security related exceptions to an application. All
@@ -96,6 +82,9 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
9682
9783 private Map <String , LinkedHashMap <RequestMatcher , AuthenticationEntryPoint >> entryPoints = new LinkedHashMap <>();
9884
85+ private DelegatingMissingAuthorityAccessDeniedHandler .Builder missingAuthoritiesHandlerBuilder =
86+ DelegatingMissingAuthorityAccessDeniedHandler .builder ();
87+
9988 /**
10089 * Creates a new instance
10190 * @see HttpSecurity#exceptionHandling(Customizer)
@@ -117,6 +106,11 @@ public ExceptionHandlingConfigurer<H> accessDeniedPage(String accessDeniedUrl) {
117106 return accessDeniedHandler (accessDeniedHandler );
118107 }
119108
109+ public ExceptionHandlingConfigurer <H > missingAuthoritiesHandler (Consumer <DelegatingMissingAuthorityAccessDeniedHandler .Builder > consumer ) {
110+ consumer .accept (this .missingAuthoritiesHandlerBuilder );
111+ return this ;
112+ }
113+
120114 /**
121115 * Specifies the {@link AccessDeniedHandler} to be used
122116 * @param accessDeniedHandler the {@link AccessDeniedHandler} to be used
@@ -189,26 +183,6 @@ public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(Authent
189183 return this ;
190184 }
191185
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-
212186 /**
213187 * Gets any explicitly configured {@link AuthenticationEntryPoint}
214188 * @return
@@ -272,19 +246,10 @@ private AccessDeniedHandler createDefaultDeniedHandler(H http) {
272246 if (this .entryPoints .isEmpty ()) {
273247 return defaults ;
274248 }
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 );
249+ DelegatingMissingAuthorityAccessDeniedHandler deniedHandler = this .missingAuthoritiesHandlerBuilder .build ();
250+ deniedHandler .setRequestCache (getRequestCache (http ));
251+ deniedHandler .setDefaultAccessDeniedHandler (defaults );
252+ return deniedHandler ;
288253 }
289254
290255 private AccessDeniedHandler createDefaultAccessDeniedHandler (H http ) {
@@ -299,29 +264,10 @@ private AccessDeniedHandler createDefaultAccessDeniedHandler(H http) {
299264 }
300265
301266 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 ()) {
267+ if (this .defaultEntryPoint == null ) {
317268 return new Http403ForbiddenEntryPoint ();
318269 }
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 ;
270+ return this .defaultEntryPoint .build ();
325271 }
326272
327273 /**
@@ -340,128 +286,4 @@ private RequestCache getRequestCache(H http) {
340286 return new HttpSessionRequestCache ();
341287 }
342288
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-
467289}
0 commit comments