skip to content

HTTP Authorization

Deciding what an authenticated caller may do at the HTTP layer: the request-matching DSL, the AuthorizationManager SPI, roles versus authorities, and the access-denied path. Interviewers care because rule ordering here is a classic source of accidental exposure.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

18

What is Spring Security's AccessDeniedHandler and when does it run?

level: juniorimportance: must knowfreq 45%

answer

  1. 403 for authenticated-but-unauthorized
  2. one method: handle(req,res,ex)
  3. called by ExceptionTranslationFilter
  4. default = AccessDeniedHandlerImpl
  5. 401 vs 403 split

basics

~10 s

It's the component that produces the HTTP 403 (Forbidden) response when a logged-in user tries to access something they're not allowed to. It runs after an AccessDeniedException is thrown.

solid answer

~30 s

AccessDeniedHandler is a Spring Security interface with one method, handle(request, response, AccessDeniedException). It is invoked by the ExceptionTranslationFilter when authorization fails for a request whose user is already authenticated — meaning they're logged in but lack the required role/authority. The default implementation, AccessDeniedHandlerImpl, sends a 403 Forbidden. This is distinct from authentication failure (not logged in), which yields 401 via a different component. You customize it to return a friendly error page or a JSON body instead of the default blank 403. Configure it through http.exceptionHandling(e -> e.accessDeniedHandler(...)).

code

java · 17 lines
java
// The interface you implement
public interface AccessDeniedHandler {
    void handle(HttpServletRequest request,
                HttpServletResponse response,
                AccessDeniedException accessDeniedException)
            throws IOException, ServletException;
}

// Registering the default page-based variant
@Bean
SecurityFilterChain chain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(a -> a
            .requestMatchers("/admin/**").hasRole("ADMIN")
            .anyRequest().authenticated())
        .exceptionHandling(ex -> ex.accessDeniedPage("/error/403"));
    return http.build();
}

go deeper

for a junior

Know it produces 403 for an authenticated user who lacks permission, and that 401 is the separate not-logged-in case.

for a middle

Be able to name ExceptionTranslationFilter as the caller and configure a custom handler via exceptionHandling().

for a senior

Explain the anonymous-vs-authenticated routing done by AuthenticationTrustResolver and why anonymous denials become 401, not 403.

for a principal

Reason about where in the request lifecycle the exception is caught and how that determines whether your handler even runs versus MVC exception handling.

**The problem it solves.** In Spring Security there are two very different failure modes: (1) *authentication* failure — the caller is not logged in / has no valid credentials, which should produce **401 Unauthorized**; and (2) *authorization* failure — the caller **is** authenticated but is not permitted to perform this action, which should produce **403 Forbidden**. `AccessDeniedHandler` owns case (2). **The interface.** `org.springframework.security.web.access.AccessDeniedHandler` has a single method: ```java void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException; ``` Its job is to write the 403 response — status code, and optionally a body, headers, or a forward to an error page. **Who calls it.** The `ExceptionTranslationFilter` sits in the security filter chain. It wraps the rest of the chain in a try/catch. When a downstream authorization check (e.g. the `AuthorizationFilter`) throws an `AccessDeniedException`, the filter decides what to do: if the current authentication represents a **real, authenticated** user, it delegates to the `AccessDeniedHandler` (403). If the user is **anonymous** (or authenticated only via remember-me), it instead delegates to the `AuthenticationEntryPoint` to start authentication (401 / redirect to login). That anonymous-vs-authenticated decision is made by an `AuthenticationTrustResolver`. **The default.** If you configure nothing, `AccessDeniedHandlerImpl` runs. It calls `response.sendError(403)` (a blank server 403 page), or, if you set an error page via `accessDeniedPage(...)`, forwards the request there. **When to customize.** REST APIs almost always replace the default so a 403 returns a structured JSON error envelope instead of an HTML page. You provide a `@Component` implementing `AccessDeniedHandler` and register it. **Configuration (Spring Security 6, component-based DSL):** ```java http.exceptionHandling(ex -> ex.accessDeniedHandler(myAccessDeniedHandler)); ``` or the simpler page form: ```java http.exceptionHandling(ex -> ex.accessDeniedPage("/error/403")); ``` **Key terms.** *AccessDeniedException* — a `RuntimeException` in `org.springframework.security.access` thrown when an authorization decision denies access. *ExceptionTranslationFilter* — the filter that catches security exceptions and routes them to the handler or entry point. *AuthenticationEntryPoint* — the sibling component that handles the 401 case (out of scope here). **Common gotcha.** Candidates conflate 401 and 403. Remember: 401 = *who are you?* (not authenticated → AuthenticationEntryPoint); 403 = *I know who you are, you still can't* (authenticated but unauthorized → AccessDeniedHandler).

  • What is the difference between what AccessDeniedHandler and AuthenticationEntryPoint handle?
    AuthenticationEntryPoint handles the 401 case (request is not authenticated — start authentication), AccessDeniedHandler handles the 403 case (request is authenticated but lacks authority). ExceptionTranslationFilter picks between them based on whether the user is anonymous.
  • What HTTP status does the default AccessDeniedHandler return?
    403 Forbidden — via AccessDeniedHandlerImpl, which calls response.sendError(403) or forwards to a configured error page.

saying these in an interview costs you the question

  • Saying AccessDeniedHandler returns 401
  • Thinking it fires when the user is not logged in
  • Confusing it with AuthenticationEntryPoint
  • Believing it handles login/credential failures

context

open as a page

What is the AuthorizationManager interface in Spring Security, and what did it replace?

level: juniorimportance: must knowfreq 70%

basics

~10 s

AuthorizationManager is Spring Security's interface that decides whether an authenticated user is allowed to access something. In Spring Security 6 it replaced the older AccessDecisionManager and voter system.

open as a page

What is the authorizeHttpRequests DSL in Spring Security, and how do you use requestMatchers with permitAll and authenticated?

level: juniorimportance: must knowfreq 85%

basics

~10 s

authorizeHttpRequests is where you declare which URLs need authorization. You list rules with requestMatchers(path) and choose an access rule like permitAll() (open to everyone) or authenticated() (must be logged in).

open as a page

What is the difference between hasRole and hasAuthority in Spring Security, and how does the ROLE_ prefix affect them?

level: juniorimportance: must knowfreq 82%

basics

~10 s

hasRole('ADMIN') automatically checks for the authority 'ROLE_ADMIN' by adding the ROLE_ prefix. hasAuthority('ROLE_ADMIN') checks the exact string you pass, with no prefix added. They match the same authority only if you include ROLE_ yourself.

open as a page

How do you make a Spring Security REST API return a custom JSON body on a 403 instead of the default blank page?

level: middleimportance: must knowfreq 40%

basics

~10 s

Implement AccessDeniedHandler, set the response status to 403, set content type to application/json, and write your JSON body. Register it via http.exceptionHandling(e -> e.accessDeniedHandler(yourHandler)).

open as a page

Explain the first-match-wins ordering rule in authorizeHttpRequests. How does rule ordering affect security?

level: middleimportance: must knowfreq 80%

basics

~10 s

Rules are evaluated top to bottom and the first matcher that matches a request decides access; later rules are ignored. So specific rules must come before broad ones, and anyRequest goes last.

open as a page

What does the default AccessDeniedHandlerImpl do, and how does accessDeniedPage() differ from providing your own AccessDeniedHandler?

level: middleimportance: should knowfreq 25%

basics

~20 s

The default AccessDeniedHandlerImpl sends a 403, either as a blank server error or by forwarding to a configured error page. accessDeniedPage() is a shortcut that just sets that forward path; a custom AccessDeniedHandler lets you fully control the status, body, and headers.

open as a page

Compare AuthorityAuthorizationManager and AuthenticatedAuthorizationManager. When does each apply?

level: middleimportance: should knowfreq 55%

basics

~10 s

AuthorityAuthorizationManager checks whether the user has specific roles/authorities (like hasRole('ADMIN')). AuthenticatedAuthorizationManager only checks whether the user is logged in — authenticated, fully authenticated, remember-me, or anonymous — regardless of roles.

open as a page

How does hasAnyRole work, and what is a GrantedAuthority in Spring Security?

level: middleimportance: should knowfreq 58%

basics

~10 s

GrantedAuthority is an interface representing one permission as a String via getAuthority(). hasAnyRole('ADMIN','MANAGER') grants access if the user has ANY of the listed roles, prepending ROLE_ to each — equivalent to hasAnyAuthority('ROLE_ADMIN','ROLE_MANAGER').

open as a page

What is AuthorizationDeniedException in Spring Security 6, and how does it relate to AccessDeniedException and the AccessDeniedHandler?

level: seniorimportance: should knowfreq 30%

basics

~10 s

AuthorizationDeniedException is the exception the modern AuthorizationManager-based checks (like @PreAuthorize and AuthorizationFilter) throw when access is denied. It extends AccessDeniedException, so it flows through the same ExceptionTranslationFilter and AccessDeniedHandler to a 403.

open as a page

How do you write a custom AuthorizationManager and plug it into method security or HTTP authorization?

level: seniorimportance: should knowfreq 40%

basics

~10 s

Implement AuthorizationManager<T> (T = RequestAuthorizationContext for web or MethodInvocation for methods) and return an AuthorizationDecision. Wire it via .access(manager) in authorizeHttpRequests, or register an AuthorizationManagerBeforeMethodInterceptor for method security.

open as a page

Explain AuthorizationDecision and how abstaining works when composing AuthorizationManagers with allOf/anyOf.

level: seniorimportance: should knowfreq 45%

basics

~10 s

AuthorizationDecision wraps a boolean 'granted' result. An AuthorizationManager can also return null to abstain (no opinion). AuthorizationManagers.anyOf grants if any manager grants; allOf grants only if all grant and none deny.

open as a page

What is the difference between Ant-style and MVC request matchers, and why does Spring recommend MvcRequestMatcher for servlet apps?

level: seniorimportance: should knowfreq 55%

basics

~10 s

AntPathRequestMatcher matches on the raw URL path pattern. MvcRequestMatcher matches using Spring MVC's path-matching, so it understands things like trailing slashes and servlet path mapping the same way your controllers do, avoiding bypass mismatches.

open as a page

How do you apply a SpEL access rule to an HTTP request in Spring Security 6, including IP-based restrictions with hasIpAddress?

level: seniorimportance: should knowfreq 47%

basics

~10 s

In Spring Security 6 the String access("...") method was removed. Pass an AuthorizationManager to access(...). Wrap a SpEL string in WebExpressionAuthorizationManager, e.g. access(new WebExpressionAuthorizationManager("hasRole('ADMIN') and hasIpAddress('10.0.0.0/8')")).

open as a page

Why does an anonymous user hitting a protected URL get 401 (redirect to login) rather than a 403 from the AccessDeniedHandler, even though authorization technically failed?

level: principalimportance: should knowfreq 22%

basics

~20 s

Because the ExceptionTranslationFilter checks whether the user is anonymous. If they are, it triggers authentication (401/login) via the AuthenticationEntryPoint instead of the AccessDeniedHandler, since the right fix is to log in, not to show a 403.

open as a page

How do you design authorization across multiple SecurityFilterChains, and what default-deny practices apply to authorizeHttpRequests?

level: principalimportance: should knowfreq 35%

basics

~10 s

Split concerns into multiple SecurityFilterChain beans, each scoped with securityMatcher, ordered with @Order. Within each chain, enforce default-deny by ending authorizeHttpRequests with anyRequest().denyAll() or authenticated().

open as a page

What changed architecturally moving from AccessDecisionManager/voters to AuthorizationManager, and what migration risks does deny-by-default introduce?

level: principalimportance: nice to knowfreq 30%

basics

~10 s

Spring Security replaced the voter-based AccessDecisionManager with a single AuthorizationManager SPI, and swapped FilterSecurityInterceptor for AuthorizationFilter. Since 6.0, web authorization is deny-by-default: any request not explicitly matched is denied, so incomplete rules break access.

open as a page

As an architect, how do you decide between roles, granular authorities, SpEL expressions, and custom AuthorizationManagers — and what are the maintainability trade-offs?

level: principalimportance: nice to knowfreq 24%

basics

~20 s

Use coarse roles for broad access tiers and granular authorities for fine permissions. Use SpEL for simple declarative combinations, but move complex, testable, or data-dependent logic into a custom AuthorizationManager. Avoid embedding business rules in stringly-typed SpEL.

open as a page