Spring - Execute code before controller's method is invoked - java

Is there any annotation similar to #PreAuthorize or #PreFilter that I can use to run code before a method in the Controller is invoked?
I need to add info to the request context (specific to the method being called) to be then retrieved by the ExceptionHandler.
For example
#RestController
public MyController{
#UnkwonwAnnotation("prepareContext(request.getAgentId())"){
public ResponseEntity method1(RequestA requestA) {
...
}
#UnkwonwAnnotation("prepareContext(request.getUserName())"){
public ResponseEntity method1(RequestB requestB) {
...
}
}
I could actually just use #PreAuthorize but doesn't feel right

You Can add interceptor for this
Sample Interceptor
public class CustomInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response) {
//Add Login here
return true;
}
}
Configuration
#Configuration
public class MyConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/**");
}
}
Hope this helps

Spring Aspect is also a good option to execute code before controller.
#Component
#Aspect
public class TestAspect {
#Before("execution(* com.test.myMethod(..)))")
public void doSomethingBefore(JoinPoint jp) throws Exception {
//code
}
}
Here myMethod() will execute before controller.

Maybe a good option is implement a custom filter that runs every time that a request is received.
You need extend "OncePerRequestFilter" and overwrite the method "doFilterInternal"
public class CustomFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
//Add attributes to request
request.getSession().setAttribute("attrName", new String("myValue"));
// Run the method requested by petition
filterChain.doFilter(request, response);
//Do something after method runs if you need.
}
}
After you have to register the filter in Spring with FilterRegistrationBean. If you have Spring security yo need add your filter after security filter.

Expanding on Sai Prateek answer, I'v created a custom annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface OperationContext {
String clientId();
String userId();
String operation();
}
and a component to handle it:
#Aspect
#Component
public class OperationContextAspect {
#Before(value = "#annotation(operationContext)", argNames = "operationContext")
public void preHandle(OperationContext operationContext) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
requestAttributes.setAttribute("operation", operationContext.operation, RequestAttributes.SCOPE_REQUEST);
requestAttributes.setAttribute("clientId", operationContext.clientId(), RequestAttributes.SCOPE_REQUEST);
requestAttributes.setAttribute("userId", operationContext.userId(), RequestAttributes.SCOPE_REQUEST);
}
}
I then annotate the controller methods providing the required parameters:
#RestController
public class MyController {
#OperationContext(clientId = '#request.getClientId', userId = '#request.getUserId', operation = "OPERATION_A")
public ResponseEntity aMethod(MyRequest request) {
...
}
}

Related

Spring Boot Request Scoped Bean

I am using spring boot for creating microservices. I need to implement request scope beans as I get some information in header and need this to be available across all the classes for that particular request. Below is what I did, but I get null pointer error.
#Component
#RequestScope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public Class RequestHeaderInfo {
private String appInfo;
...
}
#Component
public class RequestFilter implements Filter {
#Autowired
private RequestHeaderInfo requestHeaderInfo;
public void doFilter(ServletRequest req,....) {
HTTPServletRequest request = (HTTPServletRequest) req;
requestHeaderInfo.setAppInfo(request.getHeader("appInfo")) //throws null pointer error here
....
}
}
#Contoller
public class RestController {
#Autowired
private RequestHeaderInfo requestHeaderInfo;
}
I want this request header info object to be available throughout the particular request. In my filter class it throws null pointer error. Am I on the right track implementing request scoped bean?
In our project (Spring boot multi-module and not microservices) we are using an Interceptor class to filter the request.
#Component
public class RequestInterceptor implements org.springframework.web.servlet.HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String appInfo = request.getHeader("appInfo");
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
Plus our Bean with the request scope is configured like this
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
#Component
public class RequestBean {
}
Hope this helps.

Spring boot #WebFilter by specific urlPatterns does not work [duplicate]

Is there any annotation for a Filter class (for web applications) in Spring Boot? Perhaps #Filter?
I want to add a custom filter in my project.
The Spring Boot Reference Guide mentioned about
FilterRegistrationBean, but I am not sure how to use it.
If you want to setup a third-party filter you can use FilterRegistrationBean.
For example, the equivalent of web.xml:
<filter>
<filter-name>SomeFilter</filter-name>
<filter-class>com.somecompany.SomeFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SomeFilter</filter-name>
<url-pattern>/url/*</url-pattern>
<init-param>
<param-name>paramName</param-name>
<param-value>paramValue</param-value>
</init-param>
</filter-mapping>
These will be the two beans in your #Configuration file:
#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;
}
public Filter someFilter() {
return new SomeFilter();
}
The above was tested with Spring Boot 1.2.3.
Here is an example of one method of including a custom filter in a Spring Boot MVC application. Be sure to include the package in a component scan:
package com.dearheart.gtsc.filters;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
#Component
public class XClacksOverhead implements Filter {
public static final String X_CLACKS_OVERHEAD = "X-Clacks-Overhead";
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader(X_CLACKS_OVERHEAD, "GNU Terry Pratchett");
chain.doFilter(req, res);
}
#Override
public void destroy() {}
#Override
public void init(FilterConfig arg0) throws ServletException {}
}
There are three ways to add your filter,
Annotate your filter with one of the Spring stereotypes such as #Component
Register a #Bean with Filter type in Spring #Configuration
Register a #Bean with FilterRegistrationBean type in Spring #Configuration
Either #1 or #2 will do if you want your filter applies to all requests without customization, use #3 otherwise. You don't need to specify component scan for #1 to work as long as you place your filter class in the same or sub-package of your SpringApplication class. For #3, use along with #2 is only necessary when you want Spring to manage your filter class such as have it auto wired dependencies. It works just fine for me to new my filter which doesn't need any dependency autowiring/injection.
Although combining #2 and #3 works fine, I was surprised it doesn't end up with two filters applying twice. My guess is that Spring combines the two beans as one when it calls the same method to create both of them. In case you want to use #3 alone with authowiring, you can AutowireCapableBeanFactory. The following is an example,
private #Autowired AutowireCapableBeanFactory beanFactory;
#Bean
public FilterRegistrationBean myFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
Filter myFilter = new MyFilter();
beanFactory.autowireBean(myFilter);
registration.setFilter(myFilter);
registration.addUrlPatterns("/myfilterpath/*");
return registration;
}
There isn't a special annotation to denote a servlet filter. You just declare a #Bean of type Filter (or FilterRegistrationBean). An example (adding a custom header to all responses) is in Boot's own EndpointWebMvcAutoConfiguration;
If you only declare a Filter it will be applied to all requests. If you also add a FilterRegistrationBean you can additionally specify individual servlets and url patterns to apply.
Note:
As of Spring Boot 1.4, FilterRegistrationBean is not deprecated and simply moved packages from org.springframework.boot.context.embedded.FilterRegistrationBean to org.springframework.boot.web.servlet.FilterRegistrationBean
UPDATE: 2022-05-29:
There are two simple ways to do this in Spring Boot 1.5.8.RELEASE and there isn't any need for XML.
First way:
If you do not have any specific URL pattern, you can use #Component like this (full code and details are here https://github.com/surasint/surasint-examples/tree/master/spring-boot-jdbi/3_spring-boot-filter , look at README.txt to start):
#Component
public class ExampleFilter implements Filter {
...
}
Second way:
If you want to use URL patterns, you can use #WebFilter like this (full code and details are here https://github.com/surasint/surasint-examples/tree/master/spring-boot-jdbi/4_spring-boot-filter-urlpattern , look at README.txt to start):
#WebFilter(urlPatterns = "/api/count")
public class ExampleFilter implements Filter {
...
}
But you also need to add #ServletComponentScan annotation in your #SpringBootApplication class:
#ServletComponentScan
#SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
...
}
Note that #Component is Spring's annotation, but #WebFilter is not. #WebFilter is Servlet 3 annotation.
Both ways, you just need a basic Spring Boot dependency in pom.xml (there isn't any need for an explicit Tomcat embedded jasper)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
</parent>
<groupId>com.surasint.example</groupId>
<artifactId>spring-boot-04</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
WARNING: The first way, if the Controller in Spring Boot returns to a JSP file, the request will pass the filter twice.
While, in the second way, the request will pass the filter only once.
I prefer the second way, because it is more similar to default behavior in the Servlet specification.
Here is an example of my custom Filter class:
package com.dawson.controller.filter;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
public class DawsonApiFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
if (req.getHeader("x-dawson-nonce") == null || req.getHeader("x-dawson-signature") == null) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setContentType("application/json");
httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Required headers not specified in the request");
return;
}
chain.doFilter(request, response);
}
}
And I added it to the Spring Boot configuration by adding it to Configuration class as follows:
package com.dawson.configuration;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import com.dawson.controller.filter.DawsonApiFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
#SpringBootApplication
public class ApplicationConfiguration {
#Bean
public FilterRegistrationBean dawsonApiFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new DawsonApiFilter());
// In case you want the filter to apply to specific URL patterns only
registration.addUrlPatterns("/dawson/*");
return registration;
}
}
From the Spring documentation,
Embedded servlet containers - Add a Servlet, Filter or Listener to an application
To add a Servlet, Filter, or Servlet *Listener provide a #Bean
definition for it.
For example:
#Bean
public Filter compressFilter() {
CompressingFilter compressFilter = new CompressingFilter();
return compressFilter;
}
Add this #Bean configuration to your #Configuration class and the filter will be registered on startup.
Also you can add Servlets, Filters, and Listeners using classpath scanning,
#WebServlet, #WebFilter, and #WebListener annotated classes can be
automatically registered with an embedded servlet container by
annotating a #Configuration class with #ServletComponentScan and
specifying the package(s) containing the components that you want to
register. By default, #ServletComponentScan will scan from the package
of the annotated class.
We have roughly four different options to register a filter using Spring.
Firstly, we can create a Spring bean implementing Filter or extending HttpFilter:
#Component
public class MyFilter extends HttpFilter {
#Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// Implementation details...
chain.doFilter(request, response);
}
}
Secondly, we can create a Spring bean extending GenericFilterBean:
#Component
public class MyFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
//Implementation details...
chain.doFilter(currentRequest, servletResponse);
}
}
Alternatively we can use the FilterRegistrationBean class:
#Configuration
public class FilterConfiguration {
private final MyFilter myFilter;
#Autowired
public FilterConfiguration(MyFilter myFilter) {
this.myFilter = myFilter;
}
#Bean
public FilterRegistrationBean<MyFilter> myFilterRegistration() {
FilterRegistrationBean<DateLoggingFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(myFilter);
filterRegistrationBean.setUrlPatterns(Collections.singletonList("/*"));
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST);
filterRegistrationBean.setOrder(Ordered.LOWEST_PRECEDENCE - 1);
return filterRegistrationBean;
}
}
And lastly we can use the #WebFilter annotation with #ServletComponentScan:
#WebFilter(urlPatterns = "/*", dispatcherTypes = {DispatcherType.REQUEST})
public class MyFilter extends HttpFilter {
#Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// Implementation details...
chain.doFilter(request, response);
}
}
If you use Spring Boot + Spring Security, you can do that in the security configuration.
In the below example, I'm adding a custom filter before the UsernamePasswordAuthenticationFilter (see all the default Spring Security filters and their order).
#EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired FilterDependency filterDependency;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(
new MyFilter(filterDependency),
UsernamePasswordAuthenticationFilter.class);
}
}
And the filter class
class MyFilter extends OncePerRequestFilter {
private final FilterDependency filterDependency;
public MyFilter(FilterDependency filterDependency) {
this.filterDependency = filterDependency;
}
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
// Filter
filterChain.doFilter(request, response);
}
}
Using the #WebFilter annotation, it can be done as follows:
#WebFilter(urlPatterns = {"/*" })
public class AuthenticationFilter implements Filter{
private static Logger logger = Logger.getLogger(AuthenticationFilter.class);
#Override
public void destroy() {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest arg0, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
logger.info("checking client id in filter");
HttpServletRequest request = (HttpServletRequest) arg0;
String clientId = request.getHeader("clientId");
if (StringUtils.isNotEmpty(clientId)) {
chain.doFilter(request, response);
} else {
logger.error("client id missing.");
}
}
#Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
This filter will also help you to allow cross origin access
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "20000");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN");
if("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
public void destroy() {}
#Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
Step 1: Create a filter component by implementing the Filter interface.
#Component
public class PerformanceFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
...
...
}
}
Step 2: Set this filter to the URI patterns using FilterRegistrationBean.
#Configuration
public class FilterConfig {
#Bean
public FilterRegistrationBean<PerformanceFilter> perfFilter() {
FilterRegistrationBean<PerformanceFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new PerformanceFilter());
registration.addUrlPatterns("/*");
return registration;
}
}
You can refer to this link for a complete application.
It's more an advice than an answer, but if you are using a Spring MVC in your web application the good idea is to use Spring HandlerInterceptor instead of Filter.
It can do the same job, but also
Can work with ModelAndView
Its methods can be called before and after request processing, or after request completion.
It can be easily tested
1. Implement HandlerInterceptor interface and add a #Component annotation to your class
#Component
public class SecurityInterceptor implements HandlerInterceptor {
private static Logger log = LoggerFactory.getLogger(SecurityInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
request.getSession(true);
if(isLoggedIn(request))
return true;
response.getWriter().write("{\"loggedIn\":false}");
return false;
}
private boolean isLoggedIn(HttpServletRequest request) {
try {
UserSession userSession = (UserSession) request.getSession(true).getAttribute("userSession");
return userSession != null && userSession.isLoggedIn();
} catch(IllegalStateException ex) {
return false;
}
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, #Nullable ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, #Nullable Exception ex) throws Exception {
}
}
2. Configure your Interceptor
#Configuration
public class WebConfig implements WebMvcConfigurer {
private HandlerInterceptor securityInterceptor;
#Autowired
public void setSecurityInterceptor(HandlerInterceptor securityInterceptor) {
this.securityInterceptor = securityInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(securityInterceptor).addPathPatterns("/**").excludePathPatterns("/login", "/logout");
}
}
You need two main things:
Add #ServletComponentScan to your Main Class
You may add a package named filter inside it. You create a Filter class that has the following:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class RequestFilter implements Filter {
// whatever field you have
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
// Whatever implementation you want
try {
chain.doFilter(req, res);
} catch(Exception e) {
e.printStackTrace();
}
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
}
You can use #WebFilter javax.servlet.annotation.WebFilter on a class that implements javax.servlet.Filter:
#WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {
}
Then use #ServletComponentScan to register.
I saw a lot of answers here, but I didn't try any of them. I've just created the filter as in the following code.
import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
#WebFilter(urlPatterns = "/Admin")
#Configuration
public class AdminFilter implements Filter{
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("happened");
}
#Override
public void destroy() {
}
}
And I left the remaining Spring Boot application as it was.
Use:
#WebFilter(urlPatterns="/*")
public class XSSFilter implements Filter {
private static final org.apache.log4j.Logger LOGGER = LogManager.getLogger(XSSFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
LOGGER.info("Initiating XSSFilter... ");
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpRequestWrapper requestWrapper = new HttpRequestWrapper(req);
chain.doFilter(requestWrapper, response);
}
#Override
public void destroy() {
LOGGER.info("Destroying XSSFilter... ");
}
}
You need to implement Filter, and it needs to be annotated with #WebFilter(urlPatterns="/*").
And in the Application or Configuration classes, you need to add #ServletComponentScan. By this, your filter will get registered.
You can also make a filter by using #WebFilter and implementing Filter. It will do.
#Configuration
public class AppInConfig
{
#Bean
#Order(1)
public FilterRegistrationBean aiFilterRegistration()
{
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new TrackingFilter());
registration.addUrlPatterns("/**");
registration.setOrder(1);
return registration;
}
#Bean(name = "TrackingFilter")
public Filter TrackingFilter()
{
return new TrackingFilter();
}
}
I saw the answer by Vasily Komarov. Here is a similar approach, but using abstract HandlerInterceptorAdapter class instead of using HandlerInterceptor.
Here is an example...
#Component
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
}
}
#Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
#Autowired
private CustomInterceptor customInterceptor ;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor );
}
}
Filters, as the name suggest, used to perform filtering on either the request to a resource or on the response from a resource, or both. Spring Boot provides a few options to register custom filters in the Spring Boot application. Let’s look at the different options.
1. Define Spring Boot filter and invocation order
Implement the Filter interface to create a new filter in Spring Boot.
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
LOGGER.info("########## Initiating Custom filter ##########");
}
#Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
LOGGER.info("Logging Request {} : {}", request.getMethod(), request.getRequestURI());
// Call next filter in the filter chain
filterChain.doFilter(request, response);
LOGGER.info("Logging Response :{}", response.getContentType());
}
#Override
public void destroy() {
// TODO: 7/4/2018
}
}
Let’s quickly look at some important points in the above code
The filter registered by the #Component annotation.
To fire filters in the right order, we needed to use the #Order annotation.
#Component
#Order(1)
public class CustomFirstFilter implements Filter {
}
#Component
#Order(2)
public class CustomSecondFilter implements Filter {
}
In the above code, CustomFirstFilter will run before the CustomSecondFilter.
The lower the number, the higher the precedence
2. URL Pattern
If the convention-based mapping is not flexible enough, we can use FilterRegistrationBean for the complete control of the application. Here, don’t use #Component annotation for the filter class but register the filter using a FilterRegistrationBean.
public class CustomURLFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomURLFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
LOGGER.info("########## Initiating CustomURLFilter filter ##########");
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
LOGGER.info("This Filter is only called when request is mapped for /customer resource");
// Call the next filter in the filter chain
filterChain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
Register the custom Filter using FilterRegistrationBean.
#Configuration
public class AppConfig {
#Bean
public FilterRegistrationBean < CustomURLFilter > filterRegistrationBean() {
FilterRegistrationBean < CustomURLFilter > registrationBean = new FilterRegistrationBean();
CustomURLFilter customURLFilter = new CustomURLFilter();
registrationBean.setFilter(customURLFilter);
registrationBean.addUrlPatterns("/greeting/*");
registrationBean.setOrder(2); // Set precedence
return registrationBean;
}
}
First, add #ServletComponentScan to your SpringBootApplication class.
#ServletComponentScan
public class Application {
Second, create a filter file extending Filter or third-party filter class and add #WebFilter to this file like this:
#Order(1) //optional
#WebFilter(filterName = "XXXFilter", urlPatterns = "/*",
dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD},
initParams = {#WebInitParam(name = "confPath", value = "classpath:/xxx.xml")})
public class XXXFilter extends Filter{
As you all know, Spring Boot is a wonderful way of developing a web application or stand-alone application with minimum configuration and opinionated setup.
This is how I have achieved a web filter development in a Spring Boot application
My SpringBootApp specifications:
Spring Boot version: 2.0.4.RELEASE
Java version: 8.0
Servlet specification: Servlet 3.0 (Mandatory and Important)
I declared my web filter in the following manner, adhering to the Servlet specification 3.0
This is the programmatic way of defining a filter as a replacement to web.xml-based definitions.
The "#Webfilter" annotation will be processed by the container during deployment. The Filter class, in which it is found, will be created as per the configuration and applied to the URL patterns, javax.servlet.Servlets and javax.servlet.DispatcherTypes.
To avoid Web.xml completely and to achieve "Deployable" WebApp:
To deploy a Spring Boot application as "Traditional WAR", the application class should extend SpringBootServletInitializer.
NOTE:
SpringBootServletInitializer is a "programmatic implementation" of web.xml with reference to the Servlet 3.0+ specifications, which requires an implementation of WebApplicationInitializer.
Thus, SpringBootApplication doesn't require "web.xml" as its application class (after extending SpringBootServletInitializer). It scans for
#WebFilter,
#WebListener and
#WebServlet.
Annotation #ServletComponentScan
This annotation enables scanning base packages for the web components annotated with #WebFilter, #WebListener and #WebServlet.
Due to the fact that embedded containers do not support #WebServlet, #WebFilter and #WebListener annotations, Spring Boot, relying greatly on embedded containers, introduced this new annotation #ServletComponentScan to support some dependent JAR files that use these three annotations.
Scanning is only performed when using an embedded Servlet container.
The following is my Spring Boot application class definition:
Custom Servlet Initializer:
Here: I have defined a custom class: "ServletInitializer" which extends Class: SpringBootServletInitializer.
As explained earlier, SpringBootServletInitializer is responsible for scanning annotations:
#WebFilter,
#WebListener and
#WebServlet.
And hence the Spring Boot application class should
Either extend the class: SpringBootServletInitializer or
extend the custom class which extends the class: SpringBootServletInitializer
For Spring Boot in any configuration class I've done:
#Bean
public OncePerRequestFilter myFilter() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
AuthUser authUser = SecurityUtil.safeGet(); // applied after secutiry filters
...
filterChain.doFilter(request, response);
}
};
}
That's all, no any registration needed. See also What is OncePerRequestFilter?
Annotate your filter with one of the Spring stereotypes such as #Component. Register a #Bean with Filter type in Spring #Configuration. Register a #Bean with FilterRegistrationBean type in Spring #Configuration.
For filtering based on any criteria and for any list in spring boot, query language is more flexible and allows us to filter down to exactly the resources we need.
For instance, if we have simple entity like User and define it like this.
#Entity
public class User {
#Id
#GeneratedValue(strategy =
GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
private int age;
//Setter and getter as usual
Then we create a UserSpecification that implements the Specification interface, and we're going to pass in our own constraint to construct the actual query. In this part methods related to criteria and builder as defined are very useful and flexible.
public class UserSpecification implements Specification<User> {
private SearchCriteria criteria;
#Override
public Predicate toPredicate
(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
if (criteria.getOperation().equalsIgnoreCase(">")) {
return builder.greaterThanOrEqualTo(
root.<String> get(criteria.getKey()), criteria.getValue().toString());
}
else if (criteria.getOperation().equalsIgnoreCase("<")) {
return builder.lessThanOrEqualTo(
root.<String> get(criteria.getKey()), criteria.getValue().toString());
}
else if (criteria.getOperation().equalsIgnoreCase(":")) {
if (root.get(criteria.getKey()).getJavaType() == String.class) {
return builder.like(
root.<String>get(criteria.getKey()), "%" + criteria.getValue() + "%");
} else {
return builder.equal(root.get(criteria.getKey()), criteria.getValue());
}
}
return null;
}
}
And beside we create a Specification based on some simple constraints that represented in the following SearchCriteria class, any criteria can be create like this:
public class SearchCriteria {
private String key;
private String operation;
private Object value;
}
At the end define UserRepository and extend JpaSpecificationExecutor,
public interface UserRepository
extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {}
Filters are mostly used in logger files. It varies according to the logger you using in the project.
Let me explain for log4j2:
<Filters>
<!-- It prevents an error -->
<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
<!-- It prevents debug -->
<ThresholdFilter level="debug" onMatch="DENY" onMismatch="NEUTRAL" />
<!-- It allows all levels except debug/trace -->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
Filters are used to restrict the data and I used the threshold filter further to restrict the levels of data in the flow. I mentioned the levels that can be restricted over there.
For your further reference, see the level order of log4j2 - Log4J Levels: ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF

How to register a JavaEE filter in your Spring Boot application?

I have the following Spring Boot controller and a JavaEE filter classes in my Spring Boot based REST application. Please note that I do not have a web.xml configured here.
http://localhost:8080/api/products -> Returns 200
The problem is that the interceptor/filter never gets called.
ProductController.java
#RestController
#RequestMapping("/api")
public class ProductController {
#Inject
private ProductService productService;
//URI: http://localhost:8080/api/products
#RequestMapping(value = "/products", method = RequestMethod.GET)
public ResponseEntity<Iterable<Product>> getAllProducts() {
Iterable<Product> products = productService.getAllProducts();
return new ResponseEntity<>(products, HttpStatus.OK);
}
//URI: http://localhost:8080/api/products/50
#RequestMapping(value = "/products/{productId}", method = RequestMethod.GET)
public ResponseEntity<?> getProduct(#PathVariable Long productId) {
Product product = productService.getProduct(productId);
return new ResponseEntity<>(product, HttpStatus.OK);
}
}
SecurityFilter.java
#WebFilter(urlPatterns = {"/api/products/*"})
public class SecurityFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//in the init method
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//THIS METHOD NEVER GETS CALLED AND HENCE THIS NEVER GETS PRINTED ON CONSOLE.
//WHY ?????
System.out.println("**********Into the doFilter() method...........");
final HttpServletRequest httpRequest = (HttpServletRequest) request;
final HttpServletResponse httpResponse = (HttpServletResponse) response;
//move ahead
chain.doFilter(httpRequest, httpResponse);
}
#Override
public void destroy() {
//nothing to implement
}
}
BasicRestApplication.java
#SpringBootApplication
public class BasicRestApplication {
public static void main(String[] args) {
SpringApplication.run(BasicRestApplication.class, args);
}
}
I have gone through this link How to add a filter class in Spring Boot? but it does not give a clear idea on what needs to be added where to register the filter with Spring Boot.
Found the solution from here Add a Servlet Filter in a Spring Boot application
Created a new configuration class and registered the filter class.
#Configuration
public class ApplicationConfig {
#Bean
public Filter securityFilter() {
return new SecurityFilter();
}
}

Spring interceptions not working

I know that this have been asked a lot of times, but none of them could make my code work. I might be doing something wrong but I can't find out what.
I'm using Spring Boot with AngularJS, what I'm trying to do is to preHandle all the requests.
This is my code:
Controller:
#RestController
#RequestMapping(value = { "/user" })
public class UserController {
#RequestMapping(value = "/get", method = RequestMethod.GET)
public String getLanguage() {
return "user";
}
}
Interceptor:
#Component
public class RequestHandler extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException {
System.out.println("intercepted");
return false;
}
}
WebConfig:
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
HandlerInterceptor requestHandler;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestHandler);
}
}
And I added this to the applicationContext.xml:
<mvc:interceptors>
<bean class="server.RequestHandler" />
</mvc:interceptors>
I've been all the weekend trying to make this work and I couldn't, any help will be really appreciated!
Thank you!
You could try defining the Bean manually without declaring your Interceptor as a #Component like this:
RequestHandler.java
public class RequestHandler extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws ServletException {
System.out.println("intercepted");
return true;
}
}
WebConfig.java
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public RequestHandler requestHandler() {
return new RequestHandler();
}
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(requestHandler());
}
}
And by the way: If the class is declared as an interceptor why no renaming it to something which contains the term Interceptor in it such as RequestInterceptor?
It should work straightforward. Please let me know if that did the trick for you!
Update: I've implemented this in a prototype. See this minimal, complete, and verifiable example. Hope it helps!
https://github.com/dbubenheim/stackoverflow-41794738.git

Audit/Log all incoming web requests to #RequestMapping annotated methods

Source
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-handlermapping
I have Controller classes annotated with #Controller, they have methods annotated with #RequestMapping. My task is to audit all the web requests received by the Controller classes to #RequestMapping methods, I am using datatables on UI to send and receive response from controllers. the Audit framework is already in place.
The project is configured in Java Config.
I am not sure how to proceed on getting this done.
// Configure Interceptor
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
public #Bean
RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
handlerMapping.setAlwaysUseFullPath(true);
handlerMapping.setUseSuffixPatternMatch(false);
return handlerMapping;
}
}
//Add Handler
#Component
public class MyInterceptor extends HandlerInterceptorAdapter {
#Inject RequestMappingHandlerMapping requestMappingHandlerMapping;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// do stuff - ( Does my Audit Code go here? eg: Logger.info("xyz request"))
return true;
}
}
I was thinking something like this would work.
Any Suggestions on this, and
if its easier Using Listener or some other Interceptor, it would be helpful
Using interceptors you have full access to the HandlerMethod which provides convenient access to method parameters, the method return value, method annotations, etc.
The following example intercepts and logs mapped requests.
class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptorAdapter() {
Logger logger = LoggerFactory.getLogger(WebMvcConfig.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
logger.info("{} - {} - method '{}' on controller '{}'",
request.getMethod(), request.getRequestURI(), method.getName(),
handlerMethod.getBean().getClass()
);
}
return true;
}
});
}
}
It returns true to continue with the execution chain (and forward the request to other interceptors or the controller-method itself).
An example log-output looks like:
GET - /greeting - method 'greeting' on controller 'class hello.GreetingController'

Categories