Spring Security登录异常:仅验证一次,重启/关闭浏览器仍保持登录
问题描述
我正在学习Spring Security,之前在另一个项目里就碰到过这个问题,所以搭了个简单的REST API来复现情况。访问受保护端点时,系统只验证一次请求有效性;哪怕重启应用、关闭浏览器再打开,还是处于登录状态,不用再输密码。我还用Postman测试过(同时验证了Chrome和Firefox浏览器),问题一样:首次登录要密码,但登录后用Basic Auth只输用户名就能访问。
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配置代码
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(); } }
解决方案
这个问题核心是HTTP Basic认证的客户端缓存机制——浏览器、Postman这类工具会默认缓存Basic Auth的凭证,后续请求自动带上认证信息;哪怕服务端重启,只要客户端缓存没清,就会直接复用凭证通过验证。所谓“只输用户名就能访问”,是因为工具自动补全了缓存的密码。
1. 配置服务端禁止客户端缓存凭证
在Spring Security中添加响应头配置,告诉浏览器不要缓存认证相关数据:
@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()) .headers(headers -> headers .cacheControl(cache -> cache.disable()) ) .build(); }
2. 设置无状态会话(可选)
如果希望服务端不保留会话状态,每次请求都重新验证,可以开启stateless模式:
import org.springframework.security.config.http.SessionCreationPolicy; // ... 其他代码不变 @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()) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) .build(); }
注意:开启无状态后,服务端不会保存会话,但客户端的凭证缓存依然存在,需要配合第一步的响应头配置才能彻底解决重复登录问题。
3. 手动清除客户端缓存
- 浏览器:按
Ctrl+Shift+Delete清除缓存与Cookie,或在开发者工具的「Application」面板中删除对应域名的Cookie。 - Postman:关闭请求「Authorization」选项卡中的「Persist auth」开关,或在设置→隐私中清除自动保存的凭证。
补充说明
重启应用后依然能访问,是因为认证凭证存在客户端缓存里,和服务端状态无关。只要客户端缓存的凭证没被清除,请求时就会自动带上Authorization: Basic ...头,服务端收到后会重新验证凭证有效性,只要用户名密码正确就会放行。
内容的提问来源于stack exchange,提问作者leo___
相关产品推荐
相关产品推荐

