SpringBoot Spring Security can't have a given filter applied to only one path [duplicate] - spring-boot

I want to use #Autowire with a Filter. So I define my filter in the SecurityConfig as below:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(getA(), BasicAuthenticationFilter.class);
http.csrf().disable();
}
#Bean
public A getA(){
return new A();
}
This filter A extends Spring's GenericFilterBean.
I get below output when I invoke the controller, which shows the filter hits twice.
filter A before
filter A before
mycontroller invoke
filter A after
filter A after
My observation is, this extra invocation invoke with Spring container because if filter is not register as bean, it only get hits once. What is the reason and how can I fix it?

As you have observed, Spring Boot will automatically register any bean that is a Filter with the servlet container. One option is to not expose your filter as a bean and only register it with Spring Security.
If you want to be able to autowire dependencies into your Filter then it needs to be a bean. That means you need to tell Spring Boot not to register it as a filter. As described in the documentation, you do that using a FilterRegistrationBean:
#Bean
public FilterRegistrationBean registration(MyFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}

If you are on Spring 6.0.2 or plus version and if using OncePerRequestFilter,
Overriding shouldNotFilter method as follows will work.
#Override
protected boolean shouldNotFilter(HttpServletRequest request) {
return request.getServletPath().contains("/api/path/which/needs/to/exclude");
}

Removing #Component from the filter class helped me.
public class AuthTokenFilter extends OncePerRequestFilter {
}

Related

How to add a custom AuthenticationDetailsSource to BearerTokenAuthenticationFilter?

The BearerTokenAuthenticationFilter uses an AuthenticationDetailsSource to build the details of an authentication request:
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
I am implicitly using the OAuth2ResourceServerConfigurer, provided by spring-security-config-5.7.2, which sadly doesn't consider a developer-defined AuthenticationDetailsSource:
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
filter.setBearerTokenResolver(bearerTokenResolver);
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter = postProcess(filter);
http.addFilter(filter);
I confirm that the BearerTokenAuthenticationFilter has the setter I need:
setAuthenticationDetailsSource()
But I am unable to find a proper and simple way of using the setter (or any other way) to use a custom AuthenticationDetailsSource for that specific filter. I am trying to avoid creating a new filter or a new configuration.
What I have tried:
Went to github to see if there are any new versions - there are none unfortunately.
Tried to autowire the spring security filter chain and directly set the AuthenticationDetailsSource for the filter, but with no success so far.
Is there someone who managed to easily set the AuthenticationDetailsSource for a BearerTokenAuthenticationFilter?
Later edit
I have posted this question as a github issue for the Spring Security team:
https://github.com/spring-projects/spring-security/issues/11655
According to jzheaux#GitHub and as pointed in the accepted answer, I successfully used an ObjectPostProcessor:
http
.oauth2ResourceServer((oauth2) -> oauth2
.jwt(withDefaults())
.withObjectPostProcessor(new ObjectPostProcessor<BearerTokenAuthenticationFilter>() {
#Override
public BearerTokenAuthenticationFilter postProcess(BearerTokenAuthenticationFilter object) {
object.setAuthenticationDetailsSource(myAuthenticationDetailsSource);
return object;
}
});
To set your own AuthenticationDetailsSource, create ObjectPostProcessor class, where you can use setAuthenticationDetailsSource:
public class MyObjectPostProcessor implements ObjectPostProcessor<BearerTokenAuthenticationFilter> {
#Override
public <O extends BearerTokenAuthenticationFilter> O postProcess(O filter) {
filter.setAuthenticationDetailsSource(new MyAuthenticationDetailsSource());
return filter;
}
}
Then you can set MyObjectPostProcessor when creating SecurityFilterChain configuration:
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.oauth2ResourceServer()
.withObjectPostProcessor(new MyObjectPostProcessor());
return http.build();
}
}

Why Servlet Filter can be Spring Bean?

What i know about Filter and Interceptor is that Filters as J2EE Specifications are part of the webserver and not the Spring framework. So some older articles explain that it is impossible to register filters as Spring Bean while Interceptor is possible.
But the results I got when I tested today is that Filters can be Spring Bean and also inject Spring Bean on Filters are possible too like Interceptors.
(I tested on SpringBoot Framework)
#Component
public class CustomFilterTest implements Filter {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws ServletException, IOException {
chain.doFilter(request, response);
}
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
#Override
public void destroy() {
Filter.super.destroy();
}
}
#RestController
#RequiredArgsConstructor
public class ProductController {
private final CustomFilterTest customFilterTest;
#GetMapping("/test")
public ResponseEntity<Void> temp() {
System.out.println(customFilterTest);
return ResponseEntity.noContent().build();
}
}
Can anyone please explain to me?
We have to make a distinction between a regular Spring application and a Spring Boot application here. As with both, you can register a servlet filter as a bean, but the mechanism is a bit different.
Spring Framework
In plain Spring use the DelegatingFilterProxy to achieve this. The task of the DelegatingFilterProxy is to look for a bean with the same name as the filter in the root application context (the ApplicationContext registered through the ContextLoaderListener). This bean has to be your managed servlet filter.
#Configuration
#EnableWebMvc
public class WebConfiguration {
#Bean
public void YourFilter myFilter() { ... }
}
Then for the web application you would register a DelegatingFilterProxy with the name myFilter to make this work.
public class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
public void onStartup(ServletContext servletContext)
throws ServletException {
super.onStartup(servletContext);
servletContext.addFilter("myFilter", DelegatingFilterProxy.class);
}
Spring Boot
In Spring Boot it is a bit different as Spring Boot is also in control of your servlet container, like Tomcat. It basically means that Tomcat is also a managed bean in your ApplicationContext and Spring Boot can inject dependencies into it. So when Spring Boot detects a bean for the servlet filter it will automatically add it to the filter chain (without the need of a DelegatingFilterProxy).
Which means only an #Bean for your filter is needed.
#Configuration
public class WebConfiguration {
#Bean
public YourFilter myFilter() { ... }
}
Additionally you can configure things like URLs etc. by adding an additional FilterRegistrationBean for this filter.
Conclusion
For plain Spring the DelegatingFilterProxy has been around since Spring 1.2 which was released in 2005. This means if you are reading really, really, really old articles (before 2005) this was true, however with the addition of the DelegatingFilterProxy, this isn't anymore. With the release of Spring Boot, this became even a lesser issue, is it more or less is the only way to register a filter (as a managed bean).

Is #Order really needed in Spring Boot 2.0.4 OAuth server?

I have this little OAuth server class and I am using Spring Boot 2.0.4 and the spring-security-oauth2-autoconfigure 2.0.0.RELEASE dependency :
#RestController
#SpringBootApplication
#EnableAuthorizationServer
#Order(200) // really needed ?
public class MyOAuthServerApplication extends WebSecurityConfigurerAdapter {
#RequestMapping({ "/me" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
// #formatter:on
}
}
#Bean
#Override
public UserDetailsService userDetailsService() {
UserDetails mary =
User.withUsername("mary")
.password("{bcrypt}$2a$10$B3NUb0x.MYnSfx7WJItrvO/ymEQwLCKQNehmCuA8keL1uTyHizI0i")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(mary);
}
public static void main(String[] args) {
SpringApplication.run(MyOAuthServerApplication.class, args);
}
}
This seems to work well with and without the #Order(200) annotation.
So is this annotation really needed ?
The Order annotation is used to define the injection precedence.
Read more her: https://www.baeldung.com/spring-order
In your case it's because of the EnableResourceServer annotation. And you must keep the annotation.
From the doc:
The #EnableResourceServer annotation creates a security filter with
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER-1) by default, so by
moving the main application security to
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) we ensure that the
rule for "/me" takes precedence.
Please find the tutorial here: https://spring.io/guides/tutorials/spring-boot-oauth2/
You need it if you have another WebSecurityConfigurerAdapter configuration.
For example if you allow users to login via login form with a different UserDetailsService and so on. Then this should be tried before your oauth authentification and thus needs a lower order, for example #Order(199).
Another example would be different configuration for your API access.
If you don't have any other configuration, then you don't need to set the order.
Setting the order to 200 also seems to be an arbitrary value, that should simply be higher then the others and thus executed last.

Spring boot how to custom HttpMessageConverter

Back-end, Spring boot project(v1.3.0.RELEASE), supply Rest JSON Api to fron-end, just now encountered an error:
Infinite recursion (StackOverflowError)
I decide to change to a custom FastJsonHttpMessageConverter, and code is below
#Bean
public HttpMessageConverter httpMessageConverter() {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
return fastJsonHttpMessageConverter;
}
but it does not work, in real it uses a default HttpMessageConverter. Although does not have above error, the output is not as I expected. e.g.
suppliers: [
{
$ref: "$.value"
}
]
Now change above code
#Bean
public HttpMessageConverter mappingJackson2HttpMessageConverter() {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
return fastJsonHttpMessageConverter;
}
This time it works, I want to know why the method name have to be mappingJackson2HttpMessageConverter? If use another method name how to configure it?
After seeing this offical document, I know how to customize converters.
#Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = new FastJsonHttpMessageConverter();
return new HttpMessageConverters(additional);
}
A Revise to my main post, actually below code does not work.
#Bean
public HttpMessageConverter mappingJackson2HttpMessageConverter() {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
return fastJsonHttpMessageConverter;
}
Spring boot never enter this method if you set breakpoint inside it.
And below code also works.
#SpringBootApplication
public class FooApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(FooApplication.class, args);
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(new FastJsonHttpMessageConverter());
}
}
Spring boot says (https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-responsebody-rendering):
If a bean you add is of a type that would have been included by default anyway (like MappingJackson2HttpMessageConverter for JSON conversions) then it will replace the default value.
The bean you are adding is not of the same type, so the above does not happen. Your converter goes somewhere in the list of converters (probably the end), and the first suitable converter (the old one) does the job.
Beans produced by the Java configuration have the name of the method, so when you create a second bean named mappingJackson2HttpMessageConverter, it overrides the one created by spring boot's JacksonHttpMessageConvertersConfiguration and takes it place.
Instead of adding a converter bean, you might prefer to override the whole list of converters:
As in normal MVC usage, any WebMvcConfigurerAdapter beans that you provide can also contribute converters by overriding the configureMessageConverters method,

Invoke a filter before spring security filter chain in boot

I have configured my filter as below but it doesn't get invoked before Spring Security Filter chain. I have set the order as zero
I'm using Spring Boot 1.3 which support setting order on filter
#Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new UrlRewriteFilter());
registrationBean.addUrlPatterns("*");
registrationBean.addInitParameter("confReloadCheckInterval", "5");
registrationBean.addInitParameter("logLevel", "DEBUG");
registrationBean.addInitParameter("confPath", "urlrewrite.xml");
registrationBean.setOrder(0);
return registrationBean;
}
application.properties
security.filter-order=5
//>spring 2.1.3
spring.security.filter-order=5
These properties change from time to time and can be found here
https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#actuator-properties
I haven't done this myself but looking at the code it appears you simply need to set a property for the order of the security filter. For example in application.properties
This should order your filter before the security filter. I don't know what the implications are of changing this order as far as security goes it feels a bit risky to me. There is a discussion amongst the Spring developers about this here. It ends up in them implementing what my answer was above.
Discussion
https://github.com/spring-projects/spring-boot/issues/1640
Test showing what this property does.(search for testCustomFilterOrder())
https://github.com/spring-projects/spring-boot/blob/1.2.x/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java
The best way I have found to do this in SpringBoot 2.0 is in your Spring Security Configuration using addFilterBefore. I chose to do it before the Username Password filter as my filter was an alternative login filter
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new UrlRewriteFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/*/**").permitAll()
.antMatchers("/api/login").permitAll()
.antMatchers("/api/**").access("hasRole('ROLE_ADMIN')");
}
}
If you don't feel like choosing an arbitrary order for the security filter chain in the application.properties file, you can set the order using the SecurityProperties.DEFAULT_FILTER_ORDER value. This is the value used by the security filter chain since it doesn't explicitly set an order value.
#Bean
public FilterRegistrationBean<MyFilter> myFilterRegistration() {
FilterRegistrationBean<MyFilter> regBean= new FilterRegistrationBean<>();
regBean.setFilter(new MyFilter());
regBean.addUrlPatterns("/*");
// Invoke MyFilter before the security filter chain
regBean.setOrder(SecurityProperties.DEFAULT_FILTER_ORDER - 1);
return regBean;
}

Resources