-
Notifications
You must be signed in to change notification settings - Fork 0
Spring Security
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Setting up Spring Security in a separate module is a great way to modularize your application and separate concerns. Here's a full guide on how to do that, including dependencies, configuration, and properties setup.
🔧 Project Structure Example
my-project/
│
├── security-module/ <-- Spring Security config here
│ ├── build.gradle / pom.xml
│ └── src/main/java/...
│
├── main-application/
│ ├── build.gradle / pom.xml
│ └── src/main/java/...
│
└── settings.gradle / parent pom.xml1️⃣ security-module Setup Maven:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>2️⃣ Create Security Configuration
🔐 What is httpBasic?
httpBasic is a type of authentication where the client sends the username and password in every HTTP request, encoded in the Authorization header.
Example:
Authorization: Basic base64(username:password)Spring Security handles decoding and checking these credentials behind the scenes.
✅ How to Implement HTTP Basic Authentication - Enable httpBasic in your security config
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic() // <--- Enables Basic Auth
.and()
.csrf().disable(); // Disable CSRF for testing APIs easily (not recommended for prod)
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager(
User.withUsername("admin")
.password("{noop}admin123") // {noop} disables password encoding
.roles("ADMIN")
.build()
);
}
}🔒 Notes on Password Encoding
- {noop} means no encoding, used for testing.
- In real apps, use BCrypt:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}Then update the user password like:
.password(passwordEncoder().encode("admin123"))🔐 Spring Security REST APIs - HTTP Basic/FormLogin Authentication
- Accessing http://localhost:8080/myworld/slf4j/test/log → ✅ should work with no authentication.
- Accessing http://localhost:8080/myworld/sample/text → 🔐 should prompt for username/password (admin:admin123).
| ✔️ HttpBasicSecurityConfig | ✔️ FormLoginSecurityConfig |
|---|---|
@Configuration
@EnableWebSecurity
public class HttpBasicSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests((authorizeRequests) ->
authorizeRequests
.antMatchers("/login", "/public/").permitAll() // ⬅️ Allow login without auth, exclude context-path=/myworld
.antMatchers("/slf4j/").permitAll() // ⬅️ Allow direct access
//.antMatchers("/myworld/slf4j/**").permitAll() // exclude context-path to Allow direct access
.anyRequest().authenticated() // Everything else requires authentication
);
http.httpBasic();
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
} |
@Configuration
@EnableWebSecurity
public class FormLoginSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests( (authorizeRequests) -> authorizeRequests
.antMatchers("/login", "/logout", "/public/").permitAll()
.antMatchers("/slf4j/").permitAll() // Allow direct access exclude context-path=/myworld
.anyRequest().authenticated() // Everything else requires authentication
);
http.formLogin() // ✅ this alone enables default login page
//🔥 Important: DO NOT set .loginPage(...) unless you serve an HTML custom login page yourself.
.loginProcessingUrl("/login") // exclude context-path=/myworld
.successHandler((request, response, authentication) -> {
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/json");
response.getWriter().write("{\"message\": \"Login successful\"}");
})
.failureHandler((request, response, exception) -> {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.getWriter().write("{\"error\": \"Login failed\"}");
});
http.logout()
.logoutUrl("/logout") // exclude context-path=/myworld
.logoutSuccessHandler((request, response, authentication) -> {
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/json");
response.getWriter().write("{\"message\": \"Logout successful\"}");
});
http.csrf().disable(); // For REST APIs, CSRF can be disabled
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = ...;
return new InMemoryUserDetailsManager(user);
}
}
} |
| Standard user agents (such as Internet Explorer and Netscape) | |
|
|
✉️ Spring Console Loglogging.level.org.springframework.security=DEBUG
SecurityContextImpl UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=admin, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=1841D51C998DA0D704E926B8C31C3EB6] Granted Authorities=[ROLE_ADMIN]] | |
o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [ org.springframework.security.web.authentication .www.BasicAuthenticationFilter extends OncePerRequestFilter ] |
o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [ org.springframework.security.web.authentication .UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter, .ui.DefaultLoginPageGeneratingFilter extends GenericFilterBean, .ui.DefaultLogoutPageGeneratingFilter extends OncePerRequestFilter ] |
| 🔁 Your Request/Response Breakdown Headers | |
Request Headers
Authorization: "Basic YWRtaW46YWRtaW4xMjM="
Cookie: JSESSIONID={{someID}}
Response Headers
Set-Cookie: JSESSIONID=446F0C4728FA4C230BFDE6EDE7DD1F9E; Path=/myworld; HttpOnly |
Request Headers
Content-Type: multipart/form-data; boundary=--------------------------952892955974462193381526
Response Headers
Set-Cookie: JSESSIONID=7A3AC066FE7BF6F5A026282D793C1B93; Path=/myworld; HttpOnly
Location: http://localhost:8080/myworld/ |
HTTP Basic Authentication model RFC 1945, Section 11.1 RFC 2617, Section 2
The "basic" authentication scheme is based on the model that the client must authenticate itself with a user-ID and a password for each realm.
In the request Headers, the Authorization header passes the API a Base64 encoded string representing your username and password values, appended to the text Basic as follows:
Basic <Base64 encoded username and password>
✉️ Spring log: org.springframework.security.web.authentication.www.BasicAuthenticationFilter extends OncePerRequestFilter,
Processes a HTTP request's BASIC authorization headers, putting the result into the SecurityContextHolder.
In summary, this filter is responsible for processing any request that has a HTTPrequest header of Authorization with an authentication scheme of Basic and a Base64-encoded username:password token. Forexample, to authenticate user "Aladdin" with password "open sesame" the followingheader would be presented:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
This filter can be used to provide BASIC authentication services to both remotingprotocol clients (such as Hessian and SOAP) as well as standard user agents (such asInternet Explorer and Netscape).
If authentication is successful, the resulting Authentication object will beplaced into the SecurityContextHolder.
If authentication fails and ignoreFailure is false (thedefault), an AuthenticationEntryPoint implementation is called (unless the ignoreFailure property is set to true). Usually this should be BasicAuthenticationEntryPoint, which will prompt the user to authenticate againvia BASIC authentication.
Basic authentication is an attractive protocol because it is simple and widelydeployed. However, it still transmits a password in clear text and as such isundesirable in many situations.
Note that if a RememberMeServices is set, this filter will automatically sendback remember-me details to the client. Therefore, subsequent requests will not need topresent a BASIC authentication header as they will be authenticated using theremember-me mechanism.
✅ How Spring Security Auth Flow Works (Default)
| Step | Server check - HTTP status |
|---|---|
| Request with session cookie | Spring checks session validity Valid session from same client ✅ 200 OK Session is expired or restarted ❌ 401, even with JSESSIONID |
| Request without session, with Basic header | Spring authenticates user and starts new session ✅ Authenticated |
| Request without session and without Basic header | Spring returns ❌ 401 Unauthorized |
🔁 Your Request/Response Breakdown Headers on GET http://localhost:8080/myworld/sample/json/string
Even though HTTP Basic Auth is stateless, Spring Security by default uses HTTP sessions behind the scenes unless configured as stateless.
🔽 First Request:
👎 No Header, New Client: (HTTP status : # → HTTP/1.1 401 Unauthorized)
Request Headers with no Authorization
👍 With Basic Header: (HTTP status : # → HTTP/1.1 200 OK)
Request Headers
Authorization: "Basic YWRtaW46YWRtaW4xMjM=" → This is a Base64 encoded string of username:password. Decoded: admin:admin123
User-Agent: PostmanRuntime/7.37.3 → You can clear Cookie's from postman Manually - Cookies tab (top-right near the request URL bar).
# Cookie: JSESSIONID=399E414B4CF6FFFC9194DB0EEE → You're reusing a previously issued session ID — probably from a previous successful login.
Even though HTTP Basic Auth is stateless, Spring Security by default uses HTTP sessions behind the scenes unless configured as stateless.
* Spring Security sees your credentials (via Basic Auth) ✅
* It validates them and then starts a new session 🆕
* It returns a new JSESSIONID cookie to the client. → Server creates a session and returns Set-Cookie: JSESSIONID=...
Response Headers
Set-Cookie: JSESSIONID=446F0C4728FA4C230BFDE6EDE7DD1F9E; Path=/myworld; HttpOnly
🔽 From same client second/next Request on-words, client may send only the cookie (session reuse), or include both cookie + Basic header
Request Headers
Cookie: JSESSIONID=446F0C4728FA4C230BFDE6EDE7DD1F9E
HTTP Basic Auth is stateless
* Server uses session to skip re-authentication unless expired
Response Headers
🔐 Why Different Clients Get 401 Unauthorized (Even if Session Exists)
- JSESSIONID is not valid for the new client:
- HTTP sessions are server-side and usually tied to the client (via cookies).
- When you send the same session ID from a different client, Spring Security doesn't associate that session with a valid login.
- Session doesn’t exist or has expired:
- If you're using a short timeout (e.g., 1 min), it may have expired already.
- Or, if the server restarted, all sessions were wiped.
- Spring Security checks session first, then checks for credentials:
- If no valid session is found, and no Authorization header is present,
- Spring responds with:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Realm"🔐 What is a "Realm" in Basic Authentication? A realm is just a string label used by the server to identify the protected area or scope that requires authentication.
💬 Example: If a server responds with this header:
WWW-Authenticate: Basic realm="Admin Area"
Spring Boot is a project that is built on the top of the Spring Framework. It provides an easier and faster way to set up, configure, and run both simple and web-based applications. It is a Spring module that provides the RAD (Rapid Application Development) feature to the Spring Framework.
