MindIQ Academy

08 - Spring Security Notes

A beginner-to-advanced guide to Spring Security for Spring Professional Certification candidates. Covers authentication, authorization, the security filter chain, JWT, OAuth2, method security, CSRF, CORS, interview questions, and a quick cheat sheet for Spring Security 6 and Spring Boot 3.


Table of Contents

  1. What Is Spring Security?
  2. Authentication
  3. Authorization
  4. Security Filter Chain
  5. JWT
  6. OAuth2
  7. Method Security
  8. CSRF
  9. CORS
  10. Common Spring Boot Configuration
  11. Common Mistakes
  12. Certification Traps
  13. Interview Questions
  14. Cheat Sheet

1. What Is Spring Security?

Spring Security is the Spring framework module responsible for authentication, authorization, protection against common web attacks, and integration with standards such as OAuth2, OpenID Connect, LDAP, and SAML.

It protects applications at multiple levels:

LevelExample
Web requestProtect /admin/** URLs
Method invocationProtect @PreAuthorize("hasRole('ADMIN')") methods
Object accessCheck whether the current user owns a resource
Protocol integrationOAuth2 login, JWT resource server, OIDC
Web attack defenseCSRF, security headers, session protection

Spring Security is based on servlet filters in Spring MVC applications and web filters in Spring WebFlux applications.


2. Authentication

Authentication answers: Who is the current user?

It verifies identity using credentials such as:

  • username and password
  • HTTP Basic credentials
  • form login credentials
  • JWT bearer token
  • OAuth2 or OpenID Connect login
  • LDAP credentials
  • client certificate

Core Authentication Types

TypeDescription
AuthenticationRepresents the current principal, credentials, and authorities
AuthenticationManagerMain strategy interface for authenticating requests
AuthenticationProviderPerforms a specific authentication mechanism
UserDetailsServiceLoads user-specific data by username
UserDetailsRepresents application user information
PasswordEncoderEncodes and verifies passwords
SecurityContextStores the current Authentication
SecurityContextHolderHolds the SecurityContext, usually in a thread-local

Authentication Flow

Request enters application
        |
        v
Authentication filter extracts credentials
        |
        v
AuthenticationManager delegates to AuthenticationProvider
        |
        v
AuthenticationProvider validates credentials
        |
        v
Authenticated Authentication object is created
        |
        v
Authentication is stored in SecurityContext

Example: In-Memory Users

@Bean
UserDetailsService users(PasswordEncoder passwordEncoder) {
    UserDetails user = User.builder()
            .username("user")
            .password(passwordEncoder.encode("password"))
            .roles("USER")
            .build();

    UserDetails admin = User.builder()
            .username("admin")
            .password(passwordEncoder.encode("password"))
            .roles("ADMIN")
            .build();

    return new InMemoryUserDetailsManager(user, admin);
}

@Bean
PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

Password Encoding

Never store plain-text passwords. Spring Security commonly uses BCryptPasswordEncoder.

@Bean
PasswordEncoder passwordEncoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

DelegatingPasswordEncoder stores the encoding id with the password:

{bcrypt}$2a$10$...

This allows applications to support password encoding upgrades over time.

Authentication vs Principal

TermMeaning
PrincipalThe identity of the user, often username or user object
CredentialsProof of identity, such as password or token
AuthoritiesPermissions or roles granted to the user
AuthenticationFull security object containing principal, credentials, and authorities

3. Authorization

Authorization answers: What is the current user allowed to do?

Authorization happens after authentication.

Authorities and Roles

Spring Security uses authorities internally.

new SimpleGrantedAuthority("READ_PRIVILEGE");
new SimpleGrantedAuthority("ROLE_ADMIN");

A role is a special authority with the ROLE_ prefix.

ExpressionChecks For
hasAuthority("READ_PRIVILEGE")Exact authority READ_PRIVILEGE
hasRole("ADMIN")Authority ROLE_ADMIN
hasAnyRole("ADMIN", "USER")Either ROLE_ADMIN or ROLE_USER
authenticated()Any authenticated user
permitAll()Everyone, including anonymous users
denyAll()Nobody

URL-Based Authorization

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/", "/login", "/public/**").permitAll()
                    .requestMatchers("/admin/**").hasRole("ADMIN")
                    .requestMatchers("/api/**").authenticated()
                    .anyRequest().denyAll()
            )
            .formLogin(Customizer.withDefaults())
            .build();
}

Rule Ordering Matters

Spring evaluates authorization rules in the order they are declared.

.authorizeHttpRequests(auth -> auth
        .requestMatchers("/api/admin/**").hasRole("ADMIN")
        .requestMatchers("/api/**").authenticated()
        .anyRequest().denyAll()
)

Specific matchers should usually appear before broad matchers.


4. Security Filter Chain

Spring Security works through a chain of filters.

In servlet applications, requests pass through DelegatingFilterProxy, which delegates to Spring Security's FilterChainProxy.

High-Level Flow

HTTP request
    |
    v
Servlet container filter chain
    |
    v
DelegatingFilterProxy
    |
    v
FilterChainProxy
    |
    v
SecurityFilterChain
    |
    v
Application controller

Important Filters

FilterResponsibility
SecurityContextHolderFilterLoads and clears the security context
UsernamePasswordAuthenticationFilterHandles form login authentication
BasicAuthenticationFilterHandles HTTP Basic authentication
BearerTokenAuthenticationFilterHandles bearer token authentication
AuthorizationFilterPerforms authorization checks
CsrfFilterApplies CSRF protection
CorsFilterHandles CORS preflight and headers
LogoutFilterHandles logout requests

Defining a SecurityFilterChain

Spring Security 6 no longer recommends extending WebSecurityConfigurerAdapter.

@Configuration
@EnableWebSecurity
class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/public/**").permitAll()
                        .anyRequest().authenticated()
                )
                .httpBasic(Customizer.withDefaults())
                .build();
    }
}

Multiple Filter Chains

Applications can define multiple chains with different matchers.

@Bean
@Order(1)
SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
    return http
            .securityMatcher("/api/**")
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
            .build();
}

@Bean
SecurityFilterChain webSecurity(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
            .formLogin(Customizer.withDefaults())
            .build();
}

The first matching SecurityFilterChain handles the request.


5. JWT

JWT stands for JSON Web Token. It is a compact token format commonly used for stateless API authentication.

JWT Structure

header.payload.signature
PartPurpose
HeaderToken type and signing algorithm
PayloadClaims such as subject, issuer, expiry, and scopes
SignatureVerifies token integrity

Common Claims

ClaimMeaning
subSubject, usually user id or username
issIssuer
audAudience
expExpiration time
iatIssued-at time
scope or scpPermissions or scopes

JWT Resource Server

Spring Security can validate JWTs for protected APIs.

@Bean
SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/api/public/**").permitAll()
                    .requestMatchers("/api/admin/**").hasAuthority("SCOPE_admin")
                    .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
            .build();
}

Example configuration:

spring.security.oauth2.resourceserver.jwt.issuer-uri=https://issuer.example.com

or:

spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://issuer.example.com/.well-known/jwks.json

JWT Authentication Flow

Client sends Authorization: Bearer <token>
        |
        v
BearerTokenAuthenticationFilter extracts token
        |
        v
JwtDecoder validates signature, issuer, audience, and expiry
        |
        v
JwtAuthenticationConverter maps claims to authorities
        |
        v
SecurityContext stores authenticated principal

JWT Best Practices

PracticeReason
Use HTTPSPrevent token interception
Keep access tokens short-livedLimit damage if leaked
Validate issuer and audiencePrevent accepting tokens from wrong systems
Do not store secrets in JWT claimsJWT payload is readable even when signed
Prefer asymmetric signing for distributed systemsResource servers can verify without private signing key
Use refresh tokens carefullyRefresh tokens require stronger storage and revocation strategy

6. OAuth2

OAuth2 is an authorization framework. It allows a client application to access protected resources on behalf of a user or itself.

OpenID Connect builds on OAuth2 and adds authentication and identity information.

OAuth2 Roles

RoleDescription
Resource OwnerUser who owns the data
ClientApplication requesting access
Authorization ServerIssues tokens
Resource ServerHosts protected APIs

Common Grant Types

Grant TypeUse Case
Authorization CodeWeb apps and mobile apps
Authorization Code with PKCEPublic clients such as SPAs and mobile apps
Client CredentialsMachine-to-machine communication
Refresh TokenObtain new access token without user login

Password grant is deprecated and should generally not be used.

OAuth2 Login

Used when the application lets users sign in through an external provider.

@Bean
SecurityFilterChain webSecurity(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/", "/error").permitAll()
                    .anyRequest().authenticated()
            )
            .oauth2Login(Customizer.withDefaults())
            .build();
}

Example client registration:

spring.security.oauth2.client.registration.github.client-id=your-client-id
spring.security.oauth2.client.registration.github.client-secret=your-client-secret

OAuth2 Resource Server

Used when the application exposes APIs protected by access tokens.

@Bean
SecurityFilterChain resourceServer(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
            .build();
}

OAuth2 Login vs Resource Server

FeatureOAuth2 LoginOAuth2 Resource Server
Main purposeLog users into the applicationProtect APIs with access tokens
InputAuthorization code flowBearer token
SessionUsually uses server-side sessionUsually stateless
Common clientBrowser web appAPI client

7. Method Security

Method security protects service methods, repository methods, or controller methods using annotations.

Enable it with:

@Configuration
@EnableMethodSecurity
class MethodSecurityConfig {
}

Common Annotations

AnnotationDescription
@PreAuthorizeChecks authorization before method execution
@PostAuthorizeChecks authorization after method execution
@PreFilterFilters method arguments before execution
@PostFilterFilters return collections after execution
@SecuredRole-based method security
@RolesAllowedJSR-250 role-based method security

Examples

@Service
class AccountService {

    @PreAuthorize("hasRole('ADMIN')")
    List<Account> findAllAccounts() {
        return List.of();
    }

    @PreAuthorize("#accountId == authentication.name")
    Account findOwnAccount(String accountId) {
        return new Account(accountId);
    }

    @PostAuthorize("returnObject.owner == authentication.name")
    Account findAccount(String accountId) {
        return new Account(accountId);
    }
}

Method Security Notes

PointExplanation
It uses Spring AOPCalls must usually go through a Spring proxy
Self-invocation can bypass checksA method calling another method on the same object may not pass through proxy
Prefer service-layer checksBusiness authorization belongs close to business logic
URL security is still neededMethod security complements web security

8. CSRF

CSRF stands for Cross-Site Request Forgery.

It tricks an authenticated user's browser into sending unwanted state-changing requests to a trusted site.

Why CSRF Works

Browsers automatically attach cookies to requests for the cookie's domain. If an application uses cookie-based authentication, a malicious site may trigger requests that include the user's session cookie.

Spring Security Default

CSRF protection is enabled by default for browser-based applications.

It usually applies to unsafe HTTP methods:

  • POST
  • PUT
  • PATCH
  • DELETE

It does not normally apply to safe methods:

  • GET
  • HEAD
  • OPTIONS
  • TRACE

CSRF Token Flow

Server generates CSRF token
        |
        v
Client receives token
        |
        v
Client sends token with state-changing request
        |
        v
Server validates token against expected value

When CSRF Is Important

Application TypeCSRF Needed?
Server-rendered web app using sessions and cookiesYes
Form login applicationYes
Stateless REST API using bearer tokens in Authorization headerUsually no
API authenticated only by cookiesYes

Disabling CSRF for Stateless APIs

@Bean
SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
    return http
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
            .build();
}

Do not disable CSRF for browser session applications without understanding the risk.


9. CORS

CORS stands for Cross-Origin Resource Sharing.

It controls whether browsers allow JavaScript running on one origin to call APIs on another origin.

Origin

An origin is the combination of:

scheme + host + port

Examples:

URLOrigin
https://example.com/apphttps://example.com
https://example.com:8443/apphttps://example.com:8443
http://example.com/apphttp://example.com

CORS vs CSRF

TopicCORSCSRF
Main concernBrowser cross-origin reads and callsForged authenticated state changes
Enforced byBrowserServer
Protects againstUnauthorized cross-origin access from JavaScriptMalicious requests using existing credentials
Applies toCross-origin browser requestsCookie/session-based authenticated requests

CORS Configuration

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowedOrigins(List.of("https://app.example.com"));
    config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
    config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
    config.setAllowCredentials(true);

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/api/**", config);
    return source;
}

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .cors(Customizer.withDefaults())
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .build();
}

Preflight Request

For non-simple cross-origin requests, the browser sends an OPTIONS request first.

OPTIONS /api/orders
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type

The server must respond with allowed origins, methods, and headers.


10. Common Spring Boot Configuration

Basic Web Application

@Bean
SecurityFilterChain webSecurity(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/", "/css/**", "/js/**").permitAll()
                    .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults())
            .logout(Customizer.withDefaults())
            .build();
}

Stateless API

@Bean
SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
    return http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session ->
                    session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/api/public/**").permitAll()
                    .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
            .build();
}

Public Static Resources

.authorizeHttpRequests(auth -> auth
        .requestMatchers("/css/**", "/js/**", "/images/**", "/webjars/**").permitAll()
        .anyRequest().authenticated()
)

Prefer permitAll() for public endpoints instead of ignoring Spring Security entirely, unless the resource truly should bypass all security filters.


11. Common Mistakes

MistakeWhy It Is a Problem
Storing plain-text passwordsPasswords are exposed if database leaks
Confusing roles and authoritieshasRole("ADMIN") checks ROLE_ADMIN, not ADMIN
Disabling CSRF everywhereBreaks protection for browser session apps
Allowing * origin with credentialsInvalid and unsafe CORS configuration
Trusting JWT claims without signature validationAttackers can forge unsigned or invalid tokens
Using long-lived access tokensToken theft has longer impact
Putting broad matchers before specific matchersSpecific rules may never run
Relying only on frontend checksClient-side authorization can be bypassed
Forgetting method security proxiesSelf-invocation can bypass method annotations

12. Certification Traps

TrapCorrect Understanding
Authentication and authorization are the sameAuthentication identifies the user; authorization checks permissions
Roles are stored without prefixSpring stores roles as authorities with ROLE_ prefix
hasRole("ADMIN") means authority ADMINIt checks authority ROLE_ADMIN
JWT is encrypted by defaultJWT is usually signed, not encrypted
CORS protects APIs from all non-browser clientsCORS is enforced by browsers
CSRF is only a frontend concernCSRF must be validated server-side
WebSecurityConfigurerAdapter is current styleSpring Security 6 uses SecurityFilterChain beans
Method security replaces URL securityThey complement each other
OAuth2 is only for loginOAuth2 also protects APIs and machine-to-machine calls

13. Interview Questions

Authentication

Q1. What is authentication in Spring Security?

Authentication is the process of verifying a user's identity. Spring Security represents the result using an Authentication object stored in the SecurityContext.

Q2. What is the role of AuthenticationManager?

AuthenticationManager is the main interface that attempts to authenticate an Authentication request. It commonly delegates to one or more AuthenticationProvider implementations.

Q3. What is UserDetailsService used for?

It loads user-specific data, usually by username, during authentication.

Q4. Why should passwords be encoded?

Encoded passwords reduce the risk of credential exposure if the database is compromised. BCrypt is a common adaptive hashing algorithm.

Authorization

Q5. What is authorization?

Authorization determines whether an authenticated principal has permission to access a resource or perform an action.

Q6. What is the difference between role and authority?

An authority is a granted permission. A role is a convention-based authority prefixed with ROLE_.

Q7. What does hasRole("ADMIN") check?

It checks for the authority ROLE_ADMIN.

Security Filter Chain

Q8. What is SecurityFilterChain?

It is the set of Spring Security filters applied to matching HTTP requests.

Q9. What replaced WebSecurityConfigurerAdapter?

Spring Security 6 uses component-based configuration with one or more SecurityFilterChain beans.

Q10. Why does filter order matter?

Security filters perform different responsibilities in sequence. Authentication must happen before authorization can make decisions based on the authenticated user.

JWT

Q11. What is JWT used for?

JWT is commonly used as a stateless bearer token for API authentication and authorization.

Q12. Is JWT encrypted by default?

No. A standard JWT is usually signed, not encrypted. Its payload can be read by anyone who has the token.

Q13. How does Spring Security validate JWTs?

It uses a JwtDecoder to validate the token signature, expiration, issuer, and other configured claims.

OAuth2

Q14. What is the difference between OAuth2 and OpenID Connect?

OAuth2 is for delegated authorization. OpenID Connect adds authentication and identity information on top of OAuth2.

Q15. What is the client credentials grant used for?

It is used for machine-to-machine communication where no end user is involved.

Q16. What is PKCE?

PKCE is an extension to the authorization code flow that protects public clients from authorization code interception attacks.

Method Security

Q17. How do you enable method security?

Use @EnableMethodSecurity in a configuration class.

Q18. What does @PreAuthorize do?

It evaluates an authorization expression before the method runs.

Q19. Why can self-invocation bypass method security?

Method security is typically implemented using Spring AOP proxies. A method call inside the same object may not go through the proxy.

CSRF and CORS

Q20. What is CSRF?

CSRF is an attack where a malicious site causes a user's browser to send an unwanted authenticated request to another site.

Q21. When can CSRF often be disabled?

It is often disabled for stateless APIs that authenticate using bearer tokens in the Authorization header and do not rely on cookies.

Q22. What is CORS?

CORS is a browser security mechanism that controls whether JavaScript from one origin can access resources from another origin.

Q23. Does CORS replace authentication?

No. CORS is not an authentication or authorization mechanism.


14. Cheat Sheet

Core Concepts

ConceptQuick Meaning
AuthenticationVerifies who the user is
AuthorizationChecks what the user can access
PrincipalCurrent user's identity
CredentialsSecret or proof of identity
AuthorityPermission granted to a user
RoleAuthority prefixed with ROLE_
Security contextStores current authentication
Filter chainSecurity filters applied to HTTP requests

Essential Classes and Interfaces

TypePurpose
SecurityFilterChainDefines HTTP security configuration
HttpSecurityFluent API for configuring web security
AuthenticationRepresents authentication state
AuthenticationManagerAuthenticates authentication requests
AuthenticationProviderImplements a specific authentication strategy
UserDetailsServiceLoads users
PasswordEncoderEncodes and verifies passwords
GrantedAuthorityRepresents permission or role

Common Configuration Snippets

.authorizeHttpRequests(auth -> auth
        .requestMatchers("/public/**").permitAll()
        .requestMatchers("/admin/**").hasRole("ADMIN")
        .anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults())
.oauth2Login(Customizer.withDefaults())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
        session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

Authorization Expressions

ExpressionMeaning
permitAll()Allow everyone
denyAll()Deny everyone
authenticated()Require authenticated user
anonymous()Require anonymous user
hasRole("ADMIN")Require ROLE_ADMIN
hasAuthority("SCOPE_read")Require exact authority
hasAnyRole("ADMIN", "USER")Require any listed role

Security Defaults

DefaultMeaning
CSRF enabledProtects browser session apps
Secure headers enabledAdds common security headers
Form login generatedBoot may generate a default login form
Default user generatedBoot creates a user when no user config exists
Password logged at startupBoot logs generated password for development

Quick Decision Guide

RequirementUse
Browser login with sessionsForm login or OAuth2 login
API protected by bearer tokensOAuth2 resource server with JWT
Machine-to-machine accessOAuth2 client credentials
Service-layer authorization@EnableMethodSecurity and @PreAuthorize
Cookie-based authenticationKeep CSRF enabled
Stateless bearer-token APIUsually disable CSRF and use stateless sessions
Cross-origin frontend calls APIConfigure CORS explicitly

Must Remember

  • Authentication happens before authorization.
  • hasRole("ADMIN") checks for ROLE_ADMIN.
  • JWTs are usually signed, not encrypted.
  • CORS is enforced by browsers, not by API clients such as curl.
  • CSRF matters most for cookie/session-based browser applications.
  • Spring Security 6 uses SecurityFilterChain beans instead of WebSecurityConfigurerAdapter.
  • Method security depends on Spring proxies, so self-invocation can bypass checks.
  • Do not rely on frontend checks for security decisions.