Issue
I'm learning spring security, i've already met this problem in another project, i've copied this simple rest api just to see if it was going to happen again. When i reach a protected endpoint, it check the validity of the request, but only once. In fact i can stop thr application, close the browser, and reopen anything, but still is logged in and does not ask password anymore. I've also tried postman to see if it was a browser problem, (chekced with chrome and firefox), but it's still the same, the first time it ask me a password, and then once logged, in postman using basic auth, i can even login using only the username withouth saying any password.
Controller:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@GetMapping
public String home() {
return "Hello, World!";
}
@GetMapping("/user")
public String user() {
return "Hello, User!";
}
@GetMapping("/admin")
public String admin() {
return "Hello, Admin!";
}
}
security config:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeRequests(auth -> {
auth.antMatchers("/").permitAll();
auth.antMatchers("/user").hasRole("USER");
auth.antMatchers("/admin").hasRole("ADMIN");
})
.httpBasic(withDefaults())
.build();
}
}
Solution
Spring boot by default uses an embedded tomcat to run your application
In case you have already built your jar and you just shut down normally the app and restart it again, then it is normal that you still see the authentication existing.
From tomcat doc
Whenever Apache Tomcat is shut down normally and restarted, or when an application reload is triggered, the standard Manager implementation will attempt to serialize all currently active sessions to a disk file located via the pathname attribute. All such saved sessions will then be deserialized and activated (assuming they have not expired in the mean time) when the application reload is completed.
What the above means is that the security authentication which would exist in a HttpSession in ram memory would be transfered to hard disk during shutdown and reloaded again in ram memory during restart if it has not expired yet.
The above will answer why the application remembers you even after you restart.
As to you closing the browser I think you are closing the browser's tab and so the httpBasic header remains when you reopen a new tab and send a request to the restarted application.
To avoid this situation that tomcat stores session and reloads them during restart, for tomcat8
as described in documentation you can add in your src/main/resources
a META-INF/context.xml
file. Inside context.xml
you should add the line <Manager pathname="" />
.
Answered By - Panagiotis Bougioukos
Answer Checked By - David Marino (JavaFixing Volunteer)