I am trying to add custom Filter
to only specific URL, however the filter get applied to every request, regardless of URL and method, does anybody know the proper way to fix this using latest from Spring Security, i.e. not using WebSecurityConfigurerAdapter
, because it is going to be deprecated. It according with:
There are many similar questions here, but they either do not work for me, or they use the "old" approach such as:
I have number of endpoints exposed that all follow the pattern: /api/**
however I need to provide some authentication for a specific endpoint: /api/some/url
and a particular method (GET
in this case), how do I do this properly?
NOTE: the endpoint URLs are all under /api/*
(should they be called nested?)
My security configuration looks like this:
@EnableWebSecurity
public class SecurityConfig {
private MyFilter myFilter;
public SecurityConfig(MyFilter pif) {
myFilter = pif;
}
/**
* Handling AuthZ & AuthN for most APIs. No AuthZ & AuthN.
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain defaultSecurity(HttpSecurity http) throws Exception {
http.requestMatchers((requests) ->
requests.antMatchers("/"))
.authorizeHttpRequests((authorize) -> authorize.anyRequest()
.permitAll());
return http.build();
}
/**
* Handling AuthZ & AuthN for GET /api/some/url.
*/
@Bean
public SecurityFilterChain keyApiSecurity(HttpSecurity http) throws Exception {
http.requestMatchers((requests) -> requests
.antMatchers(HttpMethod.GET, "/api/some/url").and())
.addFilterBefore(myFilter,
BasicAuthenticationFilter.class)
.authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll());
return http.build();
}
}
When you expose some GenericFilter
implementation as a bean in spring-boot, it automatically puts it in a common filter chain for any request, because it doesn't know if it's a security filter or not - it could be a logging filter or anything else.
So this filter bean will be executed regardless of spring-security.
Your defaultSecurity
filter chain doesn't have this custom filter, so MyFilter
will be executed after spring security filter chain due to the order.
At the same time, keyApiSecurity
filter chain sets this custom filter before BasicAuthenticationFilter
, so it will be executed there, and will not be executed the second time, because basic doFilter()
implementation of OncePerRequestFilter
s method checks whether the request was already filtered by the filter.
So, if you want your filter to work only as a security filter, you should not expose it as a bean, and you should set it in a security filter chain like this:
.addFilterBefore(new MyFilter(), BasicAuthenticationFilter.class)
Also you should think about setting the lowest priority for a "default" security filter chain, because if it's selected first - other security filter chains will be totally ignored. So I think some specific filter security chains should have higher priority.
EDIT:
If you can't set your security filter with the new
operator because you rely on bean injection in this Filter
implementation, you can override shouldNotFilter(HttpServletRequest request)
method of OncePerRequestFilter
, for example like this:
@Component
public class MyFilter extends OncePerRequestFilter {
private final RequestMatcher uriMatcher =
new AntPathRequestMatcher("/api/some/url", HttpMethod.GET.name());
// some bean injection...
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// filter logic here...
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
RequestMatcher matcher = new NegatedRequestMatcher(uriMatcher);
return matcher.matches(request);
}
}
Then it will filter only matched requests, and putting it into the security filter chain will set its order.