Spring using #Value annotation in Filter - java

I am currently working on a Spring project and I am making a new filter that checks if a valid JWT has been sent in the request.
I am running into an issue where I can't get a value from my application.yml file using the #Value annotation like so.
#Component
#Order(2)
public class JwtConfiguration implements Filter {
#Value("${jwt.secret}")
private String jwtSecret;
I know this works fine because I have the same thing in my unit test.
I have read somewhere that the filter is not in the application context so it will not have access to configuration and I will not be able to autowire dependencies.
Does anyone know a good technique for getting values from my application.yml to my filter?
I am also not using any XML configuration and would prefer a solution that doesn't use them.
I am using Spring Boot version 1.3.3.

This can be achieved by implementing ServletContextInitializer. See below sample code.
#Configuration
public class WebConfigurer implements ServletContextInitializer {
#Value("${jwt.secret}")
private String jwtSecret;
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
initFilter(servletContext, disps);
}
private void initFilter(ServletContext servletContext,
EnumSet<DispatcherType> disps) {
FilterRegistration.Dynamic myFilter =
servletContext.addFilter("myFilter",
new MyFilterClass(jwtSecret));
// You can pass null as first parameter to below API calls
myFilter.addMappingForUrlPatterns(disps, true, "/content/*");
myFilter.addMappingForUrlPatterns(disps, true, "/app/*");
myFilter.setAsyncSupported(true);
}
}
Edit/Update:
I suppose there is another way to add filters using Java Config
You can use FilterRegistrationBean to register the filters. Here you can set the order using setOrder method. But think it will create as many ServletContextInitializer as there are filters because FilterRegistrationBean is a ServletContextInitializer
See

org.springframework.web.filter.DelegatingFilterProxy

I will describe the way to do it in Spring boot using DelegatingFilterProxy:
1) add to your application.java this bean:
#Bean
public FilterRegistrationBean securityFilterChainRegistration() {
DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy();
delegatingFilterProxy.setTargetBeanName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
FilterRegistrationBean registrationBean = new FilterRegistrationBean(delegatingFilterProxy);
registrationBean.setName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
2) create your filterbean:
#Component(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public class TokenFilter extends GenericFilterBean {
#Value("${path.to.client.id}") String clientId;
#Autowired
RequestDumperFilter requestDumperFilter;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// do stuff.
}}
This is basically it. Now you can play around with your favorite way of protecting resources. For example, I added #EnableGlobalMethodSecurity(prePostEnabled = true) annotation to my application class, and protected resources with things like #PreAuthorize("hasRole('ROLE_USER')").

Related

Add a request filter to Spring which supports Rest resource annotations?

My Spring web app is configured via Java annotations (see the configuration class at the bottom).
Is it possible to add a request filter in front of one controller (or possibly many via some sort of configuration) that supports the same (or at least a subset of the) annotations available inside #RestController methods, such as #PathVariable, #RequestParam, etc.?
So far I found the documentation about the HandlerInterceptor interface within the DispatcherServlet Interception docs and I configured one following what's in Interceptors configuration:
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(ctx.getBean(CustomInterceptor.class));
}
But the interceptor implements the HandlerInterceptor interface thus the preHandle() method has a given signature:
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
I can of course read parameters, header values, etc from the arguments, but I was wondering whether some other way was already supported.
That the filter shows up in some sort of documentation (I'm using Swagger and SpringFox) would be a plus.
Main config class
#EnableWebMvc
#Configuration
#EnableSwagger2
#ComponentScan(basePackages= { /* ... */ })
#PropertySource("classpath:config.properties")
public class WebappConfig implements WebMvcConfigurer {
#Autowired
private ApplicationContext ctx;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// extra Jackson configuration
}
// see below for this method
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(ctx.getBean(CustomInterceptor.class));
}
}

SpringBoot with #Value property in Filter is null

I'm using SpringBoot's #Value annotation to populate an object property from the default application.properties file, but seeing some strange behaviour in a Filter override.
There are two breakpoints in my debugger:
#Component
public class BasicFilter implements Filter {
#Value("${aad.client_id}")
private String clientId;
#Bean
public FilterRegistrationBean registerFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new BasicFilter()); // <-- 1.
registration.addUrlPatterns("/secure/*");
return registration;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest) { // <- 2.
HttpServletRequest httpRequest = (HttpServletRequest) request;
...
At 1.: this.client_id is set to 'foo' (from application.properties
A2 2.: this.client_id is null
Any explanation for the different/missing values?
You're injecting a new BasicFilter() into the FilterRegistrationBean. In this case, you're creating a new object, but it won't be a Spring managed bean, so its clientId will be empty. So basically you have 2 instances now:
One created by Spring due to the #Component annotation
One injected into the FilterRegistrationBean using the new keyword (not managed by Spring)
You should likely move the registerFilter() method to a separate configuration class (annotated with #Configuration) and autowire/inject the BasicFilter bean into that method, eg:
#Configuration
public class FilterConfig {
#Bean
public FilterRegistrationBean registerFilter(BasicFilter filter) { // Inject it
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter); // Use it
registration.addUrlPatterns("/secure/*");
return registration;
}
}
Altenatively, if you want to keep the #Bean method in the filter itself, you should replace it by this, since the current object is managed by Spring, while the new instance won't be:
#Bean
public FilterRegistrationBean registerFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(this);
registration.addUrlPatterns("/secure/*");
return registration;
}
Related: Spring #Value annotation always evaluating as null?

How to apply spring boot filter based on URL pattern?

I have created a spring boot filter - implements GenericFilterBean with #Component annotation.
#Component
public class MyAuthenticationFilter extends GenericFilterBean {
...
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
...
}
}
The filter is automatically identified by the Spring Boot Framework and works fine for all of the REST API. I want this filter to apply only on a certain URL path, such as /api/secure/* but I can't find the right way.
I tried #WebFilter but it didn't work.
I'm not using XML configuration or servlet initializer - just the annotations.
What would be the correct way to get it working?
There is another option if you are able to extend OncePerRequestFilter. For example:
public class SomeFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// your filter logic ....
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getServletPath();
return !path.startsWith("/api/secure/");
}
}
You can add a filter like this:
#Bean
public FilterRegistrationBean someFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(someFilter());
registration.addUrlPatterns("/url/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("someFilter");
registration.setOrder(1);
return registration;
}
#Bean(name = "someFilter")
public Filter someFilter() {
return new SomeFilter();
}
#user1283002 I think it's possible to do using #WebFilter. I just stumbled upon this article. As per the article (haven't tried myself):
#WebFilter(urlPatterns = "/api/count")
public class ExampleFilter implements Filter{
// ..........
}
// and let Spring know to scan to find such #WebFilter annotation in your config
// class by using the #ServletComponentScan annotation like
#ServletComponentScan
#SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication.run(MyApplication.class, args);
}
// ..........
}
EDIT:
After further reading the docs for the #ServletComponentScan I came across an interesting disclaimer
Scanning is only performed when using an embedded webserver
Which means that when deploying our application in a web container (eg: Apache Tomcat) this class won't get scanned by the Spring framework and therefore any spring config on it (if any) won't be applied.
If there is no Spring config to be made you are good to go without any further changes, if not just add the #Component scan to the filter and make sure it's package is in the path of your #ComponentScan annotation.

Filter invoke twice when register as Spring bean

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 {
}

Spring mvc filter for some controllers

I need to process all request coming to some Spring controllers to get some requester informartion or to throw exceptions like a security filter.
I would like if is there something buildin in Spring like a filter for controllers (I need it not for all controller, but only for someone).
I don't want to apply this filter by url request but with a class/method extension or annotation.
This is my actual solution:
#Controller
public class MyFilteredController extends FilterController {
#RequestMapping("/filtered")
public void test1(HttpServletRequest req){
InfoBean infobean=filter(req);
//... controller job
}
}
A controller that extends a class with a filter method.
public abstract FilterController{
protected InfoBean filter(HttpServletRequest req){
//... filter job
return infobean;
}
}
I don't want to apply this filter by url request but with a
class/method extension or annotation
You can register a HandlerInterceptor for this purpose. For example, you can apply a filter to all handler methods that annotated with SomeAnnotation with following code:
public class CustomHandlerIntercepter extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
SomeAnnotation annotation = handlerMethod.getMethodAnnotation(SomeAnnotation.class);
if (annotation != null) {
// do stuff
}
}
return true;
}
}
Also, you should register your interceptor in WebConfig:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomHandlerIntercepter());
}
}
You can read more about interceptors in spring reference documentation.
Take a look at SpringSandwich http://springsandwich.com/
It lets you set filters (Spring Interceptors, actually) directly as controller annotations. Unlike normal servlet filters, you'll also have full access to your Spring context.
Disclosure: I'm the author of this framework :)
You can use mvc intercepters here .
see :
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-handlermapping-interceptor
and if you want to intercept particular url you can do it as specified here
http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-config-interceptors
Everything in using the SpringDispatcherServlet is URL based, I don't think you can do it by controller.
You will need to use a Filter, looks at the API here https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/filter/package-summary.html you will probable want to use a OncePerRequestFilter.
public class MyFilter extends OncePerRequestFilter{
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// TODO Auto-generated method stub
}
}
you will then need to add the filter in the web.xml
<filter>
<filter-name>requestFilter</filter-name>
<filter-class>com.greg.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>errorHandlerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Now the hacky bit, if you want to get Spring beans in here you can create a Bridge class with statics in it.
public class Bridge {
private static PaymentService paymentService;
public PaymentService getPaymentService() {
return paymentService;
}
public void setPaymentService(PaymentService paymentService) {
Bridge.paymentService = paymentService;
}
}
If you want to inject some spring beans into this class
<bean id="paymentService" class="net.isban.example.service.PaymentService" />
<bean class="net.isban.example.util.Bridge">
<property name="paymentService" ref="paymentService" />
</bean>
Then in your filter (not spring class).
PaymentService paymentService = new Bridge().getPaymentService();
Happy for someone to show me a less hacky way of doing this.

Categories

Resources