Testing Spring Security with @WithMockUser and SecurityMockMvcRequestPostProcessors.jwt() verifies that your authorization annotations are wired correctly, but it does not test JWT validation, token signing algorithms, issuer URL verification, or clock skew handling. These require a real OAuth2 provider. KeycloakContainer runs a real Keycloak instance in Docker, giving you a complete OIDC server for integration tests. What You’ll Learn KeycloakContainer setup from the community module Importing a Keycloak realm for tests Retrieving JWT access tokens programmatically Testing protected endpoints with real JWTs Testing role-based access control (RBAC) Testing token expiry and refresh flows Dependencies KeycloakContainer is not in the Testcontainers core library.
Continue reading »Spring-Security
39 posts in this section
Actuator Security and Production Hardening
The Actuator Security Problem Spring Boot Actuator exposes endpoints that reveal sensitive information about your application — environment variables, configuration properties, heap dumps, thread dumps, and the ability to shut down the application remotely. An exposed /actuator/env endpoint can leak database passwords, API keys, and JWT signing secrets. An exposed /actuator/shutdown is a denial-of-service button. Actuator security is not optional in production. Actuator Endpoints and Their Risk Level Endpoint Exposes Risk /actuator/health Application health Low — often public /actuator/info App metadata Low /actuator/metrics JVM/HTTP metrics Medium — business data /actuator/env All configuration properties (including secrets) Critical /actuator/configprops All @ConfigurationProperties values Critical /actuator/loggers Log levels (writable) High /actuator/heapdump Full JVM heap as a file Critical /actuator/threaddump Thread state Medium /actuator/mappings All URL mappings Medium — reveals API surface /actuator/shutdown Kills the JVM Critical /actuator/auditevents Security events High Step 1: Expose Only What You Need By default, only health is exposed over HTTP.
Continue reading »AuthenticationManager, AuthenticationProvider, and UserDetailsService
The Authentication Delegation Chain Spring Security separates requesting authentication from performing authentication. A filter receives a username and password — but it does not check them itself. It passes the request to AuthenticationManager, which delegates to one or more AuthenticationProvider instances: flowchart LR F[Authentication Filter\nForm Login / Basic / JWT] -->|UsernamePasswordToken| AM[AuthenticationManager\nProviderManager] AM -->|try each provider| AP1[DaoAuthenticationProvider\nfor DB users] AM -->|try each provider| AP2[LdapAuthenticationProvider\nfor LDAP users] AM -->|try each provider| AP3[Custom Provider\nfor API key] AP1 -->|if supported| UDS[UserDetailsService\nloadUserByUsername] UDS --> DB[(Database)] AP1 -->|verify password| PE[PasswordEncoder] AP1 -->|success| Token[Authenticated Token] Token --> F This chain is the core of Spring Security’s extensibility — you can plug in any number of authentication mechanisms without touching the filter or the rest of the framework.
Continue reading »CORS: Cross-Origin Requests and Preflight Configuration
What Is CORS? The Same-Origin Policy (SOP) is a browser security rule: JavaScript on app.example.com cannot read responses from api.example.com — different subdomain, different origin. Without this, any website could use a visitor’s authenticated session to silently call other sites on their behalf. CORS (Cross-Origin Resource Sharing) is the mechanism by which a server explicitly relaxes SOP for trusted origins. When a browser sees a cross-origin request, it checks whether the server has granted permission before allowing JavaScript to read the response.
Continue reading »CSRF Protection: How It Works and When to Disable It
What Is a CSRF Attack? Cross-Site Request Forgery (CSRF) tricks an authenticated user’s browser into making an unintended request to your application. The attack: Alice is logged into bank.com — her browser holds a valid session cookie Alice visits evil.com evil.com contains <img src="https://bank.com/transfer?to=attacker&amount=5000"> Alice’s browser fires the request, automatically attaching her bank.com session cookie bank.com receives an authenticated request that Alice never intended to make The attack works because browsers automatically send cookies with cross-origin requests.
Continue reading »Domain Object Security: Access Control Lists (ACLs)
What ACLs Solve Role-based access control answers: “Can this user perform this action?” ACLs answer: “Can this user perform this action on this specific object?” Consider a document management system: Alice owns Document #42 — she can read and edit it Bob is a reviewer on Document #42 — he can read it but not edit Carol has no permission on Document #42 — she gets a 403 This cannot be expressed with roles alone.
Continue reading »Form Login Authentication: From Request to Session
Form Login: The Classic Web Authentication Flow Form login is the traditional authentication mechanism for server-rendered web applications. The user fills in a username and password form, the server verifies credentials, creates a session, and returns a session cookie. All subsequent requests carry that cookie. sequenceDiagram participant Browser participant SSF as Spring Security Filters participant AM as AuthenticationManager participant SS as HTTP Session Store participant Controller Browser->>SSF: GET /dashboard (unauthenticated) SSF->>SS: Save original request (RequestCache) SSF-->>Browser: 302 Redirect to /login Browser->>SSF: GET /login SSF-->>Browser: 200 HTML login page Browser->>SSF: POST /login {username, password} SSF->>AM: authenticate(UsernamePasswordToken) AM-->>SSF: Authentication (success) SSF->>SS: Store SecurityContext in session SSF-->>Browser: 302 Redirect to /dashboard\nSet-Cookie: JSESSIONID=abc123 Browser->>SSF: GET /dashboard (JSESSIONID cookie) SSF->>SS: Load SecurityContext from session SSF->>Controller: Request with authenticated user Controller-->>Browser: 200 Dashboard HTML Minimal Configuration @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .
Continue reading »How Spring Security Works: The Big Picture
Why Spring Security Is Hard to Learn Spring Security is not complex because the concepts are complex. Authentication and authorization are straightforward ideas. Spring Security is hard because it hides its machinery: a request comes in, something happens, an endpoint is secured or not — and without knowing the internal architecture, the behaviour feels magical and the configuration feels arbitrary. This article removes the magic. By the end, you will have the mental model needed to understand every other concept in this series.
Continue reading »HTTP Authorization: Securing Endpoints with requestMatchers
HTTP Authorization: The Last Line of Defense AuthorizationFilter is the last filter in the Spring Security chain. After all authentication filters have run (and either set an Authentication or left it as anonymous), AuthorizationFilter makes the final access decision: allow or deny. The rules you write in authorizeHttpRequests() are evaluated in order, first match wins. flowchart TD Request[HTTP Request] --> Auth[AuthorizationFilter] Auth --> Rules{Evaluate rules\nin order} Rules -->|Rule 1 matches| Decision1{Allowed?
Continue reading »HTTP Basic Authentication and Stateless APIs
What Is HTTP Basic Authentication? HTTP Basic is the simplest authentication scheme defined in the HTTP specification (RFC 7617). The client encodes username:password in Base64 and sends it in the Authorization header with every request: Authorization: Basic YWxpY2U6c2VjcmV0 ↑ Base64("alice:secret") Important: Base64 is encoding, not encryption. Anyone who intercepts the request can decode the credentials instantly. HTTP Basic must only be used over HTTPS. sequenceDiagram participant Client as API Client participant SSF as Spring Security Filters participant AM as AuthenticationManager Client->>SSF: GET /api/data\n(no Authorization header) SSF-->>Client: 401 Unauthorized\nWWW-Authenticate: Basic realm="
Continue reading »