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
相关产品推荐
相关产品推荐

