Adding authentication to a Spring Boot application is one of the first steps toward creating a secure REST API. While Spring Security provides a powerful framework, it can initially feel overwhelming because many different components work together.
This guide explains the entire authentication flow in simple terms, including common pitfalls such as receiving 403 Forbidden responses even after a successful login.
What Spring Security Actually Does
When a user logs in, Spring Security performs four major steps:
- Receives the username and password.
- Loads the user from the database.
- Verifies the password.
- Stores the authenticated user.
If using session authentication, Spring creates an HTTP session and sends a JSESSIONID cookie back to the client.
Subsequent requests simply send that cookie instead of sending the username and password again.
Client
|
POST /login
(username/password)
|
Spring Security
|
AuthenticationManager
|
UserDetailsService
|
MySQL Database
|
Authentication Success
|
Create HTTP Session
|
Return JSESSIONID Cookie
Creating the User Entity
A typical user table contains information similar to:
- Username
- Password
- Full name
- Role
- Profile picture identifier
- Internal user identifier
The password should always be stored using BCrypt.
Never store passwords in plain text.
Loading Users from MySQL
Spring Security never queries the database directly.
Instead, it asks a class implementing UserDetailsService.
Its job is simple:
- Find the user
- Throw an exception if the user doesn’t exist
- Return a Spring Security
UserDetails
Example flow:
Login Request
↓
UserDetailsService
↓
User Repository
↓
MySQL
↓
UserDetails
Roles and Authorities
Many beginners assume that simply authenticating a user is enough.
It isn’t.
Spring Security distinguishes between:
- Authentication
- Authorization
Authentication answers:
Who are you?
Authorization answers:
What are you allowed to access?
If an endpoint requires
ROLE_USER
then the authenticated user must actually have that authority.
For example:
ROLE_USER
ROLE_ADMIN
ROLE_MANAGER
If the authority doesn’t exist, authentication succeeds but authorization fails with:
403 Forbidden
Protecting REST Endpoints
Public endpoints might include:
- Login
- Registration
- Health checks
- Public resources
Everything else should require authentication.
Typical configuration:
/login
/register
/public/**
↓
Permit All
Everything else
↓
Authenticated
Session Authentication
Session authentication is still one of the simplest approaches for traditional web applications.
After login:
POST /login
↓
Authentication Successful
↓
Spring creates session
↓
JSESSIONID
↓
Browser/Postman stores cookie
↓
Future requests automatically send cookie
The backend simply looks up the session instead of authenticating the user again.
Understanding JSESSIONID
Receiving a JSESSIONID cookie only means:
A session exists.
It does not necessarily mean the user is authenticated.
Spring stores authentication separately inside the session.
If authentication never reaches the Security Context, the session exists but contains no authenticated user.
This often leads to confusion.
Why 403 Forbidden Happens After Login
One of the most common beginner problems is:
- Login returns HTTP 200
- JSESSIONID is received
- Every protected endpoint returns
403 Forbidden
This usually means one of the following:
1. User Has No Authority
Authentication succeeded.
Authorization failed.
2. Wrong Role Name
Spring typically expects:
ROLE_USER
not
USER
unless configured otherwise.
3. Authentication Was Never Saved
A custom authentication filter may authenticate the user but never store the Authentication object inside the Security Context.
Without this step, every subsequent request appears anonymous.
4. Stateless Sessions
Using
SessionCreationPolicy.STATELESS
disables HTTP sessions.
No authentication is remembered between requests.
This setting is intended for JWT authentication, not JSESSIONID authentication.
5. Authentication Filter Doesn’t Continue the Filter Chain
A custom filter should either:
- Continue processing
- Or correctly finish authentication
Improper filter implementation can prevent Spring Security from saving the authenticated user.
The Difference Between 401 and 403
Many developers confuse these responses.
401 Unauthorized
Means:
You are not authenticated.
Examples:
- Missing session
- Missing JWT
- Invalid credentials
403 Forbidden
Means:
You are authenticated, but you don’t have permission.
This distinction is extremely important during debugging.
Debugging Spring Security
One of the easiest ways to diagnose security problems is enabling debug logging.
Spring Security prints messages explaining:
- Who authenticated
- Which filter processed the request
- Which authorities were loaded
- Why access was denied
These logs often identify the exact configuration issue.
Preparing for Angular
Many developers initially use sessions while testing with Postman before moving to Angular.
There are two common approaches.
Session Authentication
Advantages
- Very easy
- Automatic cookie handling
- Minimal code
Disadvantages
- Server stores sessions
- Requires cookie support
JWT Authentication
Advantages
- Stateless
- Better for SPAs
- Better for mobile apps
- Easier horizontal scaling
Disadvantages
- Slightly more complex
- Requires token management
Most Angular applications eventually migrate to JWT authentication.
Recommended Development Workflow
A practical progression looks like this:
- Create User entity
- Create UserRepository
- Implement UserDetailsService
- Configure AuthenticationManager
- Create login endpoint
- Verify BCrypt passwords
- Verify authorities
- Test with Postman
- Protect endpoints
- Integrate Angular
Building the authentication system incrementally makes debugging much easier.
Common Beginner Mistakes
Avoid these frequent issues:
- Storing plain-text passwords
- Forgetting BCrypt encoding
- Missing
ROLE_prefix - Using stateless sessions with JSESSIONID
- Not saving Authentication into the Security Context
- Assuming a session automatically means authentication
- Mixing JWT and session authentication
- Forgetting to whitelist the login endpoint
- Returning custom login responses without preserving authentication
- Ignoring Spring Security debug logs
Best Practices
For a maintainable Spring Security implementation:
- Always hash passwords with BCrypt.
- Separate authentication from authorization.
- Store user roles in the database.
- Keep public endpoints to a minimum.
- Enable debug logging during development.
- Test authentication thoroughly with Postman before integrating a frontend.
- Decide early whether the application will use sessions or JWT tokens.
- Avoid mixing multiple authentication mechanisms unless absolutely necessary.
Conclusion
Spring Security provides a robust foundation for securing REST APIs, but it requires understanding how authentication, authorization, sessions, and roles interact.
A successful login is only one part of the process. Protected endpoints also require the authenticated user’s information to be correctly stored and recognized on subsequent requests. Many issues—such as receiving a 403 Forbidden response despite having a valid JSESSIONID—are usually caused by missing authorities, incorrect session configuration, or an incomplete authentication flow.
By understanding each component individually and testing incrementally, developers can build a secure, scalable backend that is ready for future integration with frameworks such as Angular while following modern Spring Security best practices.
References
- Spring Security Reference Documentation
- Spring Boot Reference Guide
- Spring Framework Documentation
- Jakarta Servlet Specification
- OWASP Authentication Cheat Sheet


