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

Spring Security基础认证问题:密码仅首次验证有效

问题分析与解决建议

问题根源

这是因为Spring Security默认在Basic认证成功后会创建HTTP会话,后续请求会复用会话中已通过认证的用户信息,不再重新校验请求头里的Basic凭证。所以只要会话未过期/失效,哪怕后续请求携带错误的用户名密码,也会直接通过认证返回200。

解决方法

1. 配置无状态认证(推荐用于API场景)

如果你的服务是无状态的REST API,直接禁用会话创建,让每次请求都必须重新验证Basic凭证:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .anyRequest().authenticated()
            .and()
        .httpBasic()
            .and()
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 禁用会话
    return http.build();
}

2. 强制每次请求重新校验凭证(保留会话场景)

如果必须保留会话,但要求每次请求都校验Basic凭证,可以通过自定义认证入口点结合会话销毁实现:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .anyRequest().authenticated()
            .and()
        .httpBasic()
            .authenticationEntryPoint(new BasicAuthenticationEntryPoint() {
                @Override
                public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx) throws IOException, ServletException {
                    response.addHeader("WWW-Authenticate", "Basic realm=\"" + getRealmName() + "\"");
                    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authEx.getMessage());
                    // 销毁当前会话,强制后续请求重新认证
                    request.getSession().invalidate();
                }

                @Override
                public void afterPropertiesSet() throws Exception {
                    setRealmName("YourRealm");
                    super.afterPropertiesSet();
                }
            })
            .and()
        .sessionManagement()
            .maximumSessions(1); // 限制单用户同时会话数
    return http.build();
}

3. 检查UserDetailsService实现

确保你的UserService中返回的UserDetails对象状态属性设置正确,避免因账户状态异常导致的认证逻辑异常:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    UserEntity user = userRepository.findByUsername(username)
        .orElseThrow(() -> new UsernameNotFoundException("User not found"));
    
    return User.withUsername(user.getUsername())
        .password(user.getPassword())
        .roles(user.getRoles().toArray(new String[0]))
        .accountExpired(false)
        .accountLocked(false)
        .credentialsExpired(false)
        .enabled(true)
        .build();
}

验证步骤

修改配置后重启服务,按以下流程测试:

  • 用错误密码请求接口,返回401
  • 用正确密码请求接口,返回200
  • 再次用错误密码请求接口,此时会返回401,符合预期

内容的提问来源于stack exchange,提问作者italktothewind

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 11:05:19