可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I wonder, if there is 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.
回答1:
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
回答2:
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 {}
}
回答3:
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
回答4:
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;
}
回答5:
UPDATE: 2017-12-16:
There are 2 simple ways to do this in Spring boot 1.5.8.RELEASE, no need for xml.
First way:
If you do not have any spacific url pattern, you can use @Component like this:
(Full code and details are here https://www.surasint.com/spring-boot-filter/)
@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://www.surasint.com/spring-boot-filter-urlpattern/)
@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. @WebFiler is Servlet 3 annotation.
Both ways, you just need basic spring boot dependency in pom.xml (no need for 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 Servlet specification (https://docs.oracle.com/cd/E19879-01/819-3669/6n5sg7b0b/index.html)
You can see more test log here https://www.surasint.com/spring-boot-webfilter-instead-of-component/
回答6:
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;
}
}
回答7:
From Spring docs,
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.
Eg:
@Bean
public Filter compressFilter() {
CompressingFilter compressFilter = new CompressingFilter();
return compressFilter;
}
Add this @Bean
config to your @Configuration
class and 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.
回答8:
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);
}
}
回答9:
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
}
}
回答10:
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
回答11:
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 leaved the remaining Spring Boot application as it was.
回答12:
It\'s more an advice than 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\");
}
}
回答13:
you can also make a filter by using @WebFilter and implements 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();
}
}
回答14:
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{
回答15:
@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 need to be annotated with @WebFilter(urlPatterns=\"/*\")
And in Application or Configuration class you need to add @ServletComponentScan
By this it your filter will get registered.
回答16:
I saw answer by @Vasily Komarov. 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 );
}
}
回答17:
Filters are mostly used in logger files it varies according to the logger you using in the project
Lemme explain for log4j2:
<Filters>
<!-- It prevents 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 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 refrence see the level order of log4j2 - Log4J Levels: ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF