This is a typical part of Spring Security configuration:
@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();
}
I have a problem with http.authorizeRequests().anyRequest().authenticated()
.
After adding it, when I call non-existing endpoints, for example: GET: /api/v1/not-existing, I receive 403 instead of expected 404 response.
I want to protect all my resources but I want to get 404 when calling not existing resources.
How can I fix it?
CodePudding user response:
It seems to me that your only option is the following:
@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();
}
You need to replace all-your-endpoints
with a regex or multiple regex that match all your endpoints. In fact, you can even get rid of http.authorizeRequests().antMatchers("/login", "/api/v1/auth/**").permitAll();
unless you really want to be explicit about it.
CodePudding user response:
I am okay with this behaviour . If an user is not authenticated , why bother to worry about telling him more information about your system. Just like if an user does not have permission to view your harddisk , why need to let him can discover your harddisk directory tree structure .
If you really want to return 404 , you need to customize AuthenticationEntryPoint
and AccessDeniedHandler
in the ExceptionTranslationFilter
. Both of them will be invoked if an user does not have enough permission to visit an endpoint (i.e. AccessDeniedException
happen). The former is for the anonymous user and the latter is for the non-anonymous user (i.e. user that is authenticated successfully but without enough permission)
Both of their default implementation (i.e Http403ForbiddenEntryPoint
and AccessDeniedHandlerImpl
) simply return 403 now . You have to customize them such that they will first check if there are existing endpoints to serve the current HttpServletRequest
and return 404 if no. You can do it by looping through the HandlerMapping
inside the DispatcherServlet
and check if any of the HandlerMapping
can handle the current HttpServletRequest
.
First create an object that do this check :
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;
}
}
Then customize AuthenticationEntryPoint
and AccessDeniedHandler
to use this object for checking :
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);
}
}
}
And configure them :
@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);
}
}