You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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___

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 00:41:38