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

Spring Boot+Spring Security带XMLHttpRequest头的401无响应体问题

问题分析与解决方案

现象是否正常?

这个现象是正常的,它是Spring Security 1.5.x版本中BasicAuthenticationEntryPoint的默认行为:当检测到请求带有X-Requested-With: XMLHttpRequest头部时,会仅返回401状态码而不携带响应体。这个设计初衷是适配早期AJAX框架的认证处理逻辑,但显然不符合你需要统一返回JSON错误体的需求。

修复方案

要解决这个问题,我们需要自定义一个AuthenticationEntryPoint,替换掉默认的Basic认证入口点,让它在任何情况下都返回包含错误信息的JSON响应体。

步骤1:创建自定义认证入口点类

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JsonBasicAuthenticationEntryPoint implements AuthenticationEntryPoint {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        // 设置响应状态码和内容类型
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);

        // 构建符合需求的错误响应体
        Map<String, Object> errorResponse = new HashMap<>();
        errorResponse.put("timestamp", new Date());
        errorResponse.put("status", HttpServletResponse.SC_UNAUTHORIZED);
        errorResponse.put("error", "Unauthorized");
        errorResponse.put("message", authException.getMessage());
        errorResponse.put("path", request.getRequestURI());

        // 将响应体写入输出流
        objectMapper.writeValue(response.getOutputStream(), errorResponse);
    }
}

步骤2:修改SecurityConfiguration配置

在你的SecurityConfiguration中,替换默认的httpBasic()配置,使用我们自定义的入口点:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private DaoAuthenticationProvider authenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().fullyAuthenticated()
                .and()
                // 替换默认的Basic认证入口点为自定义实现
                .httpBasic()
                .authenticationEntryPoint(new JsonBasicAuthenticationEntryPoint())
                .and()
                .csrf().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider);
    }
}

原理说明

自定义的JsonBasicAuthenticationEntryPoint会在认证失败时,直接构建你需要的JSON响应体,完全忽略请求是否带有X-Requested-With头部,确保所有认证失败场景都返回统一格式的错误信息,彻底解决原问题中的差异行为。

验证测试

现在无论是发送普通的Basic认证错误请求,还是携带X-Requested-With: XMLHttpRequest的请求,都会收到如下格式的响应:

{ "timestamp": "2018-01-15T11:59:31.837+0000", "status": 401, "error": "Unauthorized", "message": "Bad credentials", "path": "/karbonator/api/v1/wallet" }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:15:17