Spring Boot Security: A Complete Guide for 2026
Table of Contents

Spring Boot security requires more than adding a dependency. Since Spring Boot reached its first stable release in April 2014, secure production practice has meant layering authentication, explicit authorization, dependency hygiene, and policy-as-code verification so defaults don't become accidental exposure.
The counterintuitive risk is that an application can appear protected before its team has completed a security design. Spring Boot makes Spring Security available with relatively little code, and adding the relevant security dependency causes web applications to be secured by default. That convenience is valuable during development, but it can also hide unanswered questions about identity sources, business permissions, CSRF behavior, endpoint exposure, and upgrade impact.
A working login isn't proof of a secure application. A successful build isn't proof that authorization remained equivalent after a framework upgrade. Strong Spring Boot security comes from proving what the application permits, denies, and exposes, then repeating that proof as code, dependencies, and framework defaults change.
The Gap Between Default Security and Production Readiness
Default security and production security solve different problems. Spring Boot provides integration and sensible starting configuration, but it doesn't replace Spring Security or define the business policy for an application. The official Spring Boot security documentation describes support for capabilities including OAuth 2.0 and SAML 2.0, while its web-security guidance explains that adding the relevant dependency secures web applications by default.
That behavior is useful because a newly created service isn't left completely open while engineers are still wiring features. It also creates a trap. A team can mistake inherited authentication behavior for a completed security architecture, especially when the application has several routes, multiple client types, administrative endpoints, or service-layer authorization requirements.

Defaults need an explicit decision record
Start by identifying what the framework supplies and what the application must decide. Authentication answers who the caller is. Authorization answers what that caller may do. Neither answer automatically defines whether a particular business operation is safe, whether a token is valid for this audience, or whether an administrative endpoint should exist in the deployed service.
A production review should record decisions for:
- Identity: Which provider authenticates users, operators, services, and automated jobs?
- Boundaries: Which routes are public, authenticated, role-restricted, or inaccessible from a particular network path?
- State changes: Which operations create, modify, or delete data, and how does the client model affect CSRF protection?
- Business rules: Which permissions must be enforced inside services rather than only at the URL layer?
- Operations: Which Actuator or diagnostic endpoints exist, who can reach them, and what evidence proves that access is restricted?
Spring Boot applications also span materially different generations. Spring Boot 2.0 used Spring Framework 5.0, while Spring Boot 3.0 moved to Spring Framework 6.0 and the Jakarta namespace. The documentation archive now lists maintained lines including 3.3, 3.4, 3.5, 4.0, and 4.1, so the label “Spring Boot” isn't precise enough for a security inventory. The Spring Boot release documentation shows why teams must track the exact major and minor line rather than treating the framework as timeless.
Practical rule: Treat auto-configuration as an input to a security review, not as the security review itself.
The same discipline applies to people and process. Teams preparing for incident response should be able to explain which defaults were accepted, which were overridden, and how a suspicious request would be investigated. A useful supplement is this collection of incident response interview answers, particularly for testing whether engineers can describe ownership, evidence, and escalation without relying on vague statements such as “the framework handles it.”
Implementing Layered Authorization Methodology
URL rules are necessary, but they aren't sufficient for a REST service. A route can require an authenticated principal while still allowing that principal to access another customer's record, invoke an administrative operation, or modify an object outside its tenant boundary. The authorization model needs layers that fail safely when one rule is incomplete.
Start with the operation, not the annotation
Define the HTTP operation according to what it does. Read-only retrieval should use a read-oriented verb, while creation, updates, and deletion should use the appropriate state-changing verbs. This isn't cosmetic. The client, security filter chain, CSRF configuration, audit trail, and test suite all depend on the server communicating whether a request changes state.
The documented Spring Security sequence is straightforward:
- Use proper HTTP verbs. Don't disguise a state change as a retrieval request.
- Configure CSRF protection for the client model. Browser-backed applications generally need protection against forged state-changing requests. A deliberately stateless API may require a different design, but that decision must be explicit.
- Authenticate the request. Select OAuth2/OIDC, HTTP Basic, a session, or JWT according to the clients and trust boundaries.
- Authorize the business operation. Enforce resource ownership, tenant scope, role requirements, and other business rules at the service layer with method security.
Spring Security enables CSRF protection by default for unsafe methods such as POST, PUT, PATCH, and DELETE, with the expected token stored in the HTTP session by default. The Spring Security reference documentation documents this sequence and the importance of including a token where the client model requires one.
Put business authorization where data is controlled
Controller annotations can express coarse access policy, but service methods are closer to the operation that reads or changes protected data. A controller rule such as “authenticated users may call this endpoint” shouldn't be the only control protecting a method that returns records or performs a transfer.
Method security should reflect the business decision. For example, a service can require that a caller has a particular authority and that the requested resource belongs to the caller's tenant. The repository query should reinforce that boundary where practical. This combination reduces the chance that a new controller, scheduled job, event consumer, or direct method invocation bypasses a rule written only at the HTTP edge.
Authentication and authorization are often confused during design reviews. Engineers who want a compact explanation of the distinction can use this guide to authorization and authentication for engineers, then translate the distinction into test cases and service contracts.
Test denial as deliberately as access
A security test suite should prove both the allowed path and the denied paths. At minimum, test:
- Anonymous requests: Verify that unauthenticated callers receive the intended denial or login response.
- Underprivileged callers: Authenticate a user who lacks the required role or resource permission and expect rejection.
- Invalid credentials: Test expired, malformed, revoked, and incorrectly signed tokens where those cases apply.
- Cross-origin behavior: Verify that browser clients cannot trigger protected state changes without the expected safeguards.
- Direct service invocation: Call secured service methods without the controller and confirm that method-level policy still applies.
- Route changes: Add a negative test whenever a new endpoint or state-changing operation enters the codebase.
A broad matcher such as /** can be especially dangerous when it permits more than the design intended. Likewise, globally disabling CSRF in a browser-backed application, placing every rule in controllers, or assuming authentication implies authorization creates a false sense of completeness.
Configuring OAuth2 and JWT for Stateless APIs
A stateless API usually separates the identity provider from the resource server. The provider authenticates the user or client and issues a token. The Spring Boot service validates that token, translates its claims into authorities, and applies authorization to the requested resource.

Choose validation based on the trust model
With locally validated JWTs, the resource server checks the token signature and relevant claims using configured keys. That avoids a network call for every request, but the service must handle key rotation, issuer validation, audience validation, expiration, and revocation expectations correctly. A valid signature alone doesn't establish that the token is intended for this API or that the subject can access the requested object.
Remote introspection offers a different trade-off. The identity provider can provide current token state, but every uncached validation path depends on network availability, provider latency, and provider capacity. Frequent introspection can become a reliability problem, while an overly long cache can delay recognition of revocation.
Claims also need restraint. Oversized JWTs increase request overhead and can complicate logging, proxy limits, and downstream processing. Put stable identity and authorization data in the token, but don't treat the token as a complete copy of the user's permissions or application record.
A token proves an identity assertion. It doesn't prove that the requested business action is authorized.
Consider a mobile client requesting another user's account details. The JWT may contain a valid subject and a scope that permits account access. The service still needs to compare the requested account with the caller's allowed scope, tenant, or ownership relationship. If the code checks only that a JWT exists, login succeeds while data authorization fails.
For API design decisions that affect security boundaries, consistent resource naming, status behavior, and error handling matter alongside token configuration. Guidance on building predictable APIs can help teams make those contracts easier to test and audit. Teams can also connect the security contract to their REST API protection guidance, so endpoint behavior and authorization decisions remain visible during implementation.
Benchmark the whole authentication path
Security performance depends on the identity provider, token type, cryptographic algorithms, cache behavior, and network topology. A useful benchmark compares unsecured request processing, locally validated JWT requests, and flows involving a remote provider, using identical payloads and concurrency.
Record p50, p95, and p99 latency, throughput, CPU, heap allocation, and authentication-cache hit rate. Repeat the measurements after enabling method-level authorization and audit logging. One published 2025 study reported approximately 3–5% response-time overhead for properly configured Spring Boot security and more than 10,000 requests per second with sub-50 ms response times, but those figures are study-specific rather than universal production guarantees. The study is available in this Spring Boot security performance paper.
Validate cold-cache behavior, key rotation, revocation, provider failures, and oversized claims before accepting a favorable result. Set an agreed p95 and error-rate budget, then block a release when the measured service regresses beyond it.
Treating Framework Upgrades as Security Policy Migrations
A framework upgrade can compile cleanly while changing who gets access to what. That makes syntax conversion the least interesting part of the migration. The key deliverable is evidence that effective security decisions stayed safe, or that every intentional change received review.
Spring Boot's history illustrates the boundary. Spring Boot 1.0.0 arrived in April 2014, Spring Boot 2.0.0 followed in March 2018, and Spring Boot 3.0.0 arrived in November 2022. Spring Boot 2.0 used Spring Framework 5.0, while Spring Boot 3.0 adopted Spring Framework 6.0 and the Jakarta namespace. Each generation can change APIs, servlet integrations, authentication behavior, and security baselines.

Establish the old behavior before changing dependencies
Capture the effective policy before the upgrade. Inventory routes, HTTP methods, filter chains, public exceptions, authentication mechanisms, method-security annotations, Actuator exposure, and failure responses. Then create representative requests for anonymous users, valid users, users with insufficient privileges, invalid tokens, and cross-origin clients.
The inventory should include more than configuration files. Effective behavior can emerge from auto-configuration, bean ordering, annotations, provider settings, exception handlers, and endpoint-specific code. A migration plan that reads only SecurityFilterChain declarations can miss a rule enforced inside a service or a default applied by a changed framework version.
Compare decisions, not just test completion
Spring Security's versioning guidance states that major releases may include breaking changes to support improved security practices, while minor releases add functionality and patch releases are intended to remain compatible except when fixing defects. That makes version-aware testing a core security control, not a maintenance afterthought.
Spring Security 7 ships with the Spring Boot 4 release train and removes APIs deprecated since Spring Security 5.8. Recent coverage also reports a potentially breaking default change in which CSRF protection is enabled for API endpoints. A previously functioning stateless API can then return unexplained 403 responses when clients don't provide the expected token. The Spring Boot 4 migration coverage describes this change and the operational risk it creates.
A safe migration gate should compare the old and new outcomes for each representative request:
- Expected allow: The same caller can still perform the approved operation.
- Expected deny: Unauthorized and underprivileged callers remain blocked.
- Changed default: Any new denial or allowance has a documented reason.
- Business impact: Changed responses are mapped to affected clients and workflows.
- Release evidence: Reviewers can inspect the policy diff and test results.
A successful build proves compatibility with the compiler. It doesn't prove equivalence with the security policy.
For regulated services, retain the baseline, behavior comparison, exception decisions, and approval record with the release. That record turns an upgrade from an opaque dependency event into an auditable security-policy migration.
Evaluating Vulnerabilities Through Exploitability, Not Just Counts
A dependency tree can tell you that Spring Security is present. It can't tell you whether a vulnerable path is reachable in this application. Counting advisories without tracing execution paths creates noise, while a lower-count dependency with an exposed authentication flow may deserve faster action.
Spring Security vulnerabilities can affect very different areas, including BCrypt password-length handling, method-security authorization on parameterized types, Actuator-related authentication bypass conditions, HTTP security headers, X.509 client-certificate impersonation, and one-time-token session behavior. The public Spring Security vulnerability listings illustrate that variety.
Map the advisory to application behavior
For each advisory, identify the affected component and ask whether the application uses it. The investigation should cover:
- Authentication providers: Does the service use the provider or password-handling path described by the advisory?
- Endpoint type: Are Actuator, management, browser, or API endpoints exposed?
- Authorization mechanism: Does the code use method security, parameterized types, or the affected annotation path?
- Credential model: Does the service accept X.509 certificates, one-time tokens, passwords, or another mechanism?
- Deployment boundary: Can an attacker reach the relevant endpoint or submit the relevant input?
A library version appearing in a dependency tree is evidence for triage, not proof of exploitability. Conversely, an application may pull in a feature transitively and expose it through configuration or auto-configuration without the team realizing that the path is active.
Prove remediation in code and artifacts
Patch application should produce more than a changed version string. Add a regression test that exercises the vulnerable path, verifies the expected denial or safe behavior, and fails against the pre-remediation condition where feasible. Inspect the resolved dependency graph and deployed artifact, because a parent build, transitive dependency, or container layer can preserve the vulnerable version.
The verification record should connect four facts:
- The advisory affects a specific component or behavior.
- The application either reaches or does not reach that behavior.
- The selected fix or compensating control changes the reachable path.
- The built and deployed artifact contains the intended remediation.
This approach also improves prioritization. A remotely reachable authentication bypass deserves different treatment from a dormant feature that isn't enabled, even if both appear under the same library family. Risk decisions should reflect reachability, attacker control, exposure, and business impact rather than CVE count alone.
Integrating Dependency Management and CI/CD Policy Checks
Security policy works best when it travels with the code. A document stored in a design folder can't reliably prevent a later route addition, dependency update, or framework migration from changing effective behavior. The repository, pull request, and deployment should carry enough context to show which decisions apply.

Turn design decisions into merge controls
A practical workflow starts with a security inventory generated from tickets, architecture documents, repositories, and service metadata. From that inventory, define enforceable controls such as:
- Authentication control: Protected routes must use an approved authentication mechanism.
- Authorization control: Sensitive service methods must have explicit business authorization.
- CSRF control: Browser-backed state changes must preserve the required token behavior.
- Dependency control: Spring Boot and Spring Security versions must follow the approved support and upgrade policy.
- Testing control: New or changed security-sensitive routes require positive and negative authorization tests.
- Advisory control: A dependency update must include reachability analysis for relevant security advisories.
The pull request check should inspect both configuration and behavior. Static rules can flag broad authorization matchers, dangerous global CSRF changes, missing method-security annotations, or suspicious endpoint exposure. Runtime tests should then exercise the resulting filter chains and service methods, because syntax inspection can't prove what a composed configuration does in practice.
Keep the policy connected to implementation
Policy-as-code should report the exact rule that failed, the affected file or route, and the approved decision that provides context. Developers need an actionable result inside the pull request, not a separate security queue with no link to the implementation.
Tools such as SonarQube, SpotBugs with Find Security Bugs, and Semgrep can contribute code and dependency signals, but they don't replace threat modeling or authorization tests. Software composition analysis is useful for tracking resolved versions and advisory impact, while software composition analysis guidance can help teams structure that part of the workflow.
A living security context also supports AI-assisted coding. When an agent proposes a new endpoint or changes a filter chain, the system should surface the relevant threat model, required controls, and negative tests before the code reaches review. The merge gate should block unsafe changes, while allowing a documented exception when an authorized reviewer accepts the risk.
Verifying Security Posture with a Practical Checklist
A useful Spring Boot security review ends with evidence, not confidence. Run the checks against the deployed configuration and the code that produces it. A local test that bypasses filters or mocks away authorization can confirm business logic while missing the control that protects the production route.
Confirm identity and request boundaries
Start with the request lifecycle:
- Inventory versions: Record the exact Spring Boot and Spring Security lines, plus the framework and namespace baseline. Spring Security major releases can introduce breaking changes, so the version is part of the security context.
- Map filter chains: Identify which chain handles each route, how ordering works, and which authentication mechanism applies.
- Test anonymous access: Confirm that public routes are intentionally public and protected routes deny anonymous requests.
- Validate credentials: Exercise expired, malformed, incorrectly signed, and wrongly scoped tokens where relevant.
- Inspect claims: Confirm issuer, audience, expiration, subject, and authority mapping. Don't accept a valid signature as sufficient authorization.
- Review secrets: Ensure credentials, signing material, and provider configuration aren't embedded in source or exposed through logs.
Verify authorization and state changes
Then test the business policy at every layer. For each sensitive operation, record the required identity, permission, resource scope, and expected response for both allowed and denied callers.
Use negative tests to catch drift:
- Wrong role: An authenticated caller without the required authority receives denial.
- Wrong tenant: A permitted user can't read or change another tenant's resource.
- Wrong ownership: A user can't operate on an object outside the approved relationship.
- Direct method call: Service-layer authorization remains effective without the controller.
- Unsafe request: Browser-backed POST, PUT, PATCH, and DELETE requests handle CSRF according to the client design.
- Cross-origin request: The response and state change match the intended browser boundary.
- Management endpoint: Actuator and diagnostic routes aren't reachable beyond their approved audience.
For a broader release review, use this application security checklist as a companion to the endpoint-specific tests. Keep the checklist tied to pull requests so a new route, provider, dependency, or framework upgrade automatically triggers the relevant verification.
Recheck dependencies and release evidence
Finally, resolve the dependency graph and compare it with the approved policy. For every relevant advisory, document whether the affected path is reachable, what remediation was applied, and how a test proves the result. Review p95 latency, error behavior, cache failures, key rotation, and remote identity-provider outages when authentication is part of the request path.
A release is ready when the team can answer three questions without reconstructing history from memory: What does this service protect? What does each caller get to do? What evidence shows that the deployed artifact enforces those decisions?
DevArmor connects continuous threat modeling, security design reviews, policy-as-code checks, and implementation verification across the delivery lifecycle. Use DevArmor to keep Spring Boot security decisions tied to pull requests and deployments, then visit the platform to evaluate a workflow for your engineering and AppSec teams.
Table of Contents
Subscribe

