调用不存在的端点时收到 403 而不是 404
Posted
技术标签:
【中文标题】调用不存在的端点时收到 403 而不是 404【英文标题】:Receiving 403 instead of 404 when calling non existing endpoint 【发布时间】:2021-12-31 09:21:41 【问题描述】:这是 Spring Security 配置的典型部分:
@Override
protected void configure(HttpSecurity http) throws Exception
http.csrf().and().cors().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers("/login", "/api/v1/auth/**").permitAll();
http.authorizeRequests().anyRequest().authenticated();
http.authorizeRequests().anyRequest().authenticated()
有问题。
添加后,当我调用不存在的端点时,例如:GET: /api/v1/not-existing,我收到 403 而不是预期的404 响应。
我想保护我所有的资源,但我想在调用不存在的资源时得到 404。
我该如何解决?
【问题讨论】:
【参考方案1】:我可以接受这种行为。如果用户未通过身份验证,为什么还要担心告诉他有关您系统的更多信息。就像一个用户没有权限查看你的硬盘,为什么要让他发现你的硬盘目录树结构。
如果真的要返回 404 ,需要在 ExceptionTranslationFilter
中自定义 AuthenticationEntryPoint
和 AccessDeniedHandler
。如果用户没有足够的权限访问端点(即AccessDeniedException
发生),它们都会被调用。前者用于匿名用户,后者用于非匿名用户(即认证成功但没有足够权限的用户)
它们的两个默认实现(即Http403ForbiddenEntryPoint
和AccessDeniedHandlerImpl
)现在都简单地返回 403 。您必须自定义它们,以便它们首先检查是否存在现有端点来服务当前的HttpServletRequest
,如果没有则返回 404。您可以通过循环遍历DispatcherServlet
中的HandlerMapping
并检查是否有任何HandlerMapping
可以处理当前的HttpServletRequest
。
首先创建一个执行此检查的对象:
public class HttpRequestEndpointChecker
private DispatcherServlet servlet;
public HttpRequestEndpointChecker(DispatcherServlet servlet)
this.servlet = servlet;
public boolean isEndpointExist(HttpServletRequest request)
for (HandlerMapping handlerMapping : servlet.getHandlerMappings())
try
HandlerExecutionChain foundHandler = handlerMapping.getHandler(request);
if (foundHandler != null)
return true;
catch (Exception e)
return false;
return false;
然后自定义AuthenticationEntryPoint
和AccessDeniedHandler
使用这个对象进行检查:
public class MyAccessDeniedHandler extends AccessDeniedHandlerImpl
private HttpRequestEndpointChecker endpointChecker;
public MyAccessDeniedHandler(HttpRequestEndpointChecker endpointChecker)
this.endpointChecker = endpointChecker;
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException
if (!endpointChecker.isEndpointExist(request))
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Resource not found");
else
super.handle(request, response, accessDeniedException);
public class MyAuthenticationEntryPoint extends Http403ForbiddenEntryPoint
private HttpRequestEndpointChecker endpointChecker;
public MyAuthenticationEntryPoint(HttpRequestEndpointChecker endpointChecker)
this.endpointChecker = endpointChecker;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException
if (!endpointChecker.isEndpointExist(request))
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Resource not found");
else
super.commence(request, response, authException);
并配置它们:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Autowired
private DispatcherServlet dispatcherServlet;
@Autowired
private HttpRequestEndpointChecker endpointChecker;
@Override
protected void configure(HttpSecurity http) throws Exception
..............
..............
http.exceptionHandling()
.authenticationEntryPoint(new MyAuthenticationEntryPoint(endpointChecker))
.accessDeniedHandler(new MyAccessDeniedHandler(endpointChecker));
@Bean
public HttpRequestEndpointChecker endpointChecker()
return new HttpRequestEndpointChecker(dispatcherServlet);
【讨论】:
我一直认为默认行为是 404 而不是 403,不是吗?... :o 而且我返回 403 是不寻常的。 取决于你如何看待它。在这种情况下,出于我在答案中提到的原因,我更愿意返回 403。我觉得很自然也很有意义【参考方案2】:在我看来,您唯一的选择如下:
@Override
protected void configure(HttpSecurity http) throws Exception
http.csrf().and().cors().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers("/login", "/api/v1/auth/**").permitAll();
http.authorizeRequests().antMatchers(all-your-endpoints).authenticated();
http.authorizeRequests().anyRequest().permitAll();
您需要将all-your-endpoints
替换为匹配所有端点的正则表达式或多个正则表达式。事实上,您甚至可以摆脱 http.authorizeRequests().antMatchers("/login", "/api/v1/auth/**").permitAll();
,除非您真的想明确说明它。
【讨论】:
以上是关于调用不存在的端点时收到 403 而不是 404的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot POST 请求返回 http 状态 405“方法不允许”而不是 HTTP 状态 404
Spring Boot:发生 RESTFul 错误(404、403、500)时获取 JSON 正文
对 .htaccess 的请求应返回 404 而不是 403