调用不存在的端点时收到 403 而不是 404
Receiving 403 instead of 404 when calling non existing endpoint
这是Spring安全配置的典型部分:
@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。
我该如何解决?
在我看来,您唯一的选择是:
@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();
除非你真的想明确说明它。
我可以接受这种行为。如果用户未通过身份验证,何必费心告诉他有关您系统的更多信息。就像一个用户没有权限查看你的硬盘,为什么要让他发现你的硬盘目录树结构。
如果你真的想要return 404,你需要在ExceptionTranslationFilter
中自定义AuthenticationEntryPoint
和AccessDeniedHandler
。如果用户没有足够的权限访问端点(即 AccessDeniedException
发生),它们都将被调用。前者是针对匿名用户,后者是针对非匿名用户(即认证成功但没有足够权限的用户)
它们的默认实现(即 Http403ForbiddenEntryPoint
和 AccessDeniedHandlerImpl
)现在只是 return 403。您必须自定义它们,以便它们首先检查是否存在现有端点来服务当前 HttpServletRequest
和 return 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);
}
}
这是Spring安全配置的典型部分:
@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。
我该如何解决?
在我看来,您唯一的选择是:
@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();
除非你真的想明确说明它。
我可以接受这种行为。如果用户未通过身份验证,何必费心告诉他有关您系统的更多信息。就像一个用户没有权限查看你的硬盘,为什么要让他发现你的硬盘目录树结构。
如果你真的想要return 404,你需要在ExceptionTranslationFilter
中自定义AuthenticationEntryPoint
和AccessDeniedHandler
。如果用户没有足够的权限访问端点(即 AccessDeniedException
发生),它们都将被调用。前者是针对匿名用户,后者是针对非匿名用户(即认证成功但没有足够权限的用户)
它们的默认实现(即 Http403ForbiddenEntryPoint
和 AccessDeniedHandlerImpl
)现在只是 return 403。您必须自定义它们,以便它们首先检查是否存在现有端点来服务当前 HttpServletRequest
和 return 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);
}
}