Information Security 23 min read

Spring Security Guide: Authentication, Authorization, and Web Security

This comprehensive Spring Security guide explains core concepts of authentication and access control, details the AuthenticationManager and ProviderManager interfaces, shows how to customize authentication managers, configure authorization with AccessDecisionManager, secure web requests with filter chains, and apply method-level security, including asynchronous contexts.

Top Architect
Top Architect
Top Architect
Spring Security Guide: Authentication, Authorization, and Web Security

This guide is an introductory tutorial for Spring Security, providing insight into the framework's design and core building blocks. It covers the basics of application security to clear common confusions for developers using Spring Security, focusing on authentication, authorization, and related concepts.

Although not a complete reference for every basic issue (many other resources exist), the guide is useful for both beginners and experts. Spring Boot is frequently mentioned because it supplies default security behavior, and understanding its relationship to the overall architecture helps even when not using Spring Boot.

Authentication and Access Control

Application security can be divided into two independent problems: authentication (who you are) and authorization (what you can do). Spring Security’s architecture separates authentication from authorization and provides many strategies and extension points.

Authentication

The primary strategy interface for authentication is AuthenticationManager , which defines a single method:

public interface AuthenticationManager {

  Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

The authenticate() method can result in three outcomes:

If authentication succeeds, it returns an Authentication object (typically with its authenticated flag set to true).

If authentication fails, it throws an AuthenticationException .

If it cannot determine success or failure, it returns null .

AuthenticationException is a runtime exception usually handled in a generic way by the application. For example, a web UI may display a failure page, while a backend HTTP service returns a 401 response.

The most common implementation of AuthenticationManager is ProviderManager , which delegates to a chain of AuthenticationProvider instances. AuthenticationProvider adds an extra method to query whether it supports a given Authentication type:

public interface AuthenticationProvider {

  Authentication authenticate(Authentication authentication) throws AuthenticationException;

  boolean supports(Class
authentication);
}

The supports() method actually receives Class . By delegating to a chain of providers, ProviderManager can support multiple authentication mechanisms within the same application. If none of the providers recognize the authentication type, the request is passed to an optional parent manager.

ProviderManager may have an optional parent; if all providers return null , the parent is consulted. If the parent is unavailable, an empty Authentication results in an AuthenticationException .

Often, applications protect logical groups of resources (e.g., all web resources matching /api/** ) and each group can have its own dedicated AuthenticationManager . Typically each group uses a ProviderManager that shares a common parent, which acts as a global fallback.

Using ProviderManager’s AuthenticationManager hierarchy

Customizing Authentication Managers

Spring Security provides configuration helpers to quickly set up common AuthenticationManager functionality. The most used helper is AuthenticationManagerBuilder , which is suitable for configuring in‑memory, JDBC, or LDAP user details, or adding a custom UserDetailsService . Below is an example of configuring a global (parent) authentication manager in a Spring Boot application:

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

    // ... web stuff here

    @Autowired
    public void initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {
        builder.jdbcAuthentication()
               .dataSource(dataSource)
               .withUser("dave")
               .password("secret")
               .roles("USER");
    }
}

When the @Autowired method receives the builder, it builds the global (parent) AuthenticationManager . If you instead define the builder in an @Override configure(HttpSecurity) method, the builder only creates a local manager that is a child of the global manager.

Authorization or Access Control

After successful authentication, authorization is handled by the AccessDecisionManager . The framework provides three implementations, all delegating to an AccessDecisionVoter chain, similar to how ProviderManager delegates to providers.

AccessDecisionVoter evaluates an Authentication (the principal) and a secured object annotated with ConfigAttribute :

boolean supports(ConfigAttribute attribute);

boolean supports(Class
clazz);

int vote(Authentication authentication, S object, Collection
attributes);

The secured object can be any resource (e.g., a web URL or a method). ConfigAttribute usually carries role names like ROLE_ADMIN or SpEL expressions such as isFullyAuthenticated() && hasRole('FOO') . The default AccessDecisionManager is AffirmativeBased , granting access if any voter returns an affirmative vote.

Web Security

Spring Security for the web layer is based on Servlet filters. Understanding filter behavior is essential because a single HTTP request passes through a chain of filters. The diagram below shows a typical layered handling of an HTTP request.

Spring Security is installed as a single filter of type FilterChainProxy . In a Spring Boot application, the security filter is a bean that is automatically applied to every request. The filter chain order is important; filters can be ordered via @Order or by implementing Ordered . Some built‑in filters expose constants (e.g., SessionRepositoryFilter.DEFAULT_ORDER ) to indicate relative positioning.

Spring Security is a single filter that delegates processing to a series of internal filters.

The FilterChainProxy contains one or more internal filter chains. The container dispatches a request to the first matching chain (e.g., a chain matching /foo/** ). Only one chain processes the request.

Note: The container does not know about Spring Security’s internal filters; therefore, custom filters should not be registered as regular @Bean filters unless explicitly intended.

Creating and Customizing Filter Chains

The default backup filter chain (matching /** ) has a predefined order SecurityProperties.BASIC_AUTH_ORDER . You can disable it with security.basic.enabled=false or add a higher‑priority chain by defining a WebSecurityConfigurerAdapter bean with an @Order annotation:

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/foo/**")
            // ... custom rules ...
    }
}

This bean adds a new filter chain that is placed before the default backup chain.

Combining Application Security Rules with Actuator Rules

If you use Spring Boot Actuator, its endpoints are secured by default. Adding a custom filter chain for actuator endpoints uses ManagementServerProperties.BASIC_AUTH_ORDER , which is ordered before the default backup chain. To apply your own rules to actuator endpoints, add a chain with an order earlier than the actuator chain or just rely on the default security.

@Configuration
@Order(ManagementServerProperties.BASIC_AUTH_ORDER + 1)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/foo/**")
            // ... custom rules ...
    }
}
Note: Spring Security’s web layer is tied to the Servlet API, so it only works in servlet containers, but it does not depend on Spring MVC.

Method Security

Beyond web security, Spring Security can secure method invocations. Enable it with @EnableGlobalMethodSecurity :

@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SampleSecureApplication {
}

Then annotate methods with security annotations such as @Secured :

@Service
public class MyService {

    @Secured("ROLE_USER")
    public String secure() {
        return "Hello Security";
    }
}

When a bean is proxied, calls to the secured method are intercepted; unauthorized access results in an AccessDeniedException . Other annotations like @PreAuthorize and @PostAuthorize allow expression‑based security on parameters and return values.

Tip: Combining web security (filter chain) with method security provides layered protection.

Working with Threads

Spring Security is thread‑bound; the current authenticated principal is stored in a SecurityContext (backed by ThreadLocal ). You can access it via SecurityContextHolder :

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
assert(authentication.isAuthenticated());

Although application code rarely accesses the context directly, it is useful when writing custom authentication filters.

You can also obtain the current user in a controller method:

@RequestMapping("/foo")
public String foo(@AuthenticationPrincipal User user) {
    // use the user object
}

Or retrieve the Authentication from the servlet request’s Principal :

@RequestMapping("/foo")
public String foo(Principal principal) {
    Authentication authentication = (Authentication) principal;
    User user = (User) authentication.getPrincipal();
    // use the user object
}

Processing Secure Methods Asynchronously

Because the SecurityContext is thread‑bound, asynchronous execution (e.g., @Async ) requires propagating the context. Spring Security provides wrappers for Runnable and Callable . Configure an AsyncConfigurer that returns an executor wrapping tasks with a delegating security context:

@Configuration
public class ApplicationConfiguration extends AsyncConfigurerSupport {

    @Override
    public Executor getAsyncExecutor() {
        return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5));
    }
}

With this configuration, security information is correctly propagated to asynchronous tasks.

JavaSpring BootAuthenticationWeb SecurityauthorizationSpring SecurityMethod Security
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.