Filter和Interceptor的使用 Filter和Interceptor的不同点
Filter是Servlet的,它主要用于对用户的请求进行预处理,也可以对ServletResponse进行后置处理.
Interceptor拦截器,在AOP中用于某个方法或字段被访问前,进行拦截,然后在之前或者之后做一些拦截操作,比如日志。
从前两点就可以看出来,Filter和Interceptor的作用时机是不同的,Filter都没有机会进到方法那里,所以如果我们想做一些权限的校验我们可以采用过滤器,如果我们想对方法做一些加强,比如方法前后做一些日志,那我们就可以使用Interceptor拦截器了
Filter实践 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import com.ruoyi.common.core.redis.RedisCache;import com.ruoyi.common.utils.StringUtils;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import java.io.IOException;import java.util.Arrays;import java.util.List;import java.util.Optional;import java.util.stream.Collectors;@Slf4j public class OpenApiFilter implements Filter { @Autowired private RedisCache redisCache; @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { log.warn("开始 进行 open api filter" ); HttpServletRequest request = (HttpServletRequest) servletRequest; String requestURI = request.getRequestURI(); String xClientId = request.getHeader("x-Client-id" ); if (StringUtils.isNotEmpty(xClientId)) { String openApiStr = Optional.ofNullable(redisCache.getCacheObject(xClientId)) .map(item -> { return item.toString(); }) .orElse(null ); if (StringUtils.isNotNull(openApiStr)) { String[] split = openApiStr.split(";" ); List<String> openApiList = Arrays.stream(split).collect(Collectors.toList()); if (openApiList.contains(requestURI)) { filterChain.doFilter(servletRequest, servletResponse); }else { log.warn("IP:{}不存在该open api {}的访问权限" , request.getRemoteAddr(), requestURI); } }else { log.warn("IP:{}不存在该open api {}的访问权限, 请检查x-Client-id的正确性" , request.getRemoteAddr(), requestURI); } }else { log.warn("IP:{}不存在该open api {}的访问权限" , request.getRemoteAddr(), requestURI); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import com.ruoyi.openapi.core.filter.OpenApiFilter;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.filter.DelegatingFilterProxy;import java.util.Arrays;@Configuration public class OpenApiFilterConfig { @Bean(name = "openApiFilter") public OpenApiFilter getOpenApiFilter () { return new OpenApiFilter (); } @Bean public FilterRegistrationBean registerMyFilter () { FilterRegistrationBean registrationBean = new FilterRegistrationBean <>(); registrationBean.setOrder(1 ); registrationBean.setFilter(new DelegatingFilterProxy ("openApiFilter" )); registrationBean.setUrlPatterns(Arrays.asList("/aiot/openapi/*" )); registrationBean.setEnabled(false ); return registrationBean; } }
通过测试发现,正常的接口也被过滤了,,,这是为什么?我也不知道,心想着我在setFilter的时候既然用的是DelegatingFilterProxy那我为什么不直接使用DelegatingFilterProxyRegistrationBean
呢, 于是改为下面的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import com.ruoyi.openapi.core.filter.OpenApiFilter;import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import javax.servlet.DispatcherType;@Configuration public class OpenApiFilterConfig { @Bean(name = "openApiFilter") public OpenApiFilter getOpenApiFilter () { return new OpenApiFilter (); } @Bean public DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean () { DelegatingFilterProxyRegistrationBean filterProxy = new DelegatingFilterProxyRegistrationBean ("openApiFilter" ); filterProxy.addUrlPatterns("/aiot/openapi/*" ); filterProxy.addInitParameter("targetFilterLifecycle" ,"true" ); filterProxy.addInitParameter("exclusions" , "*.js,*.gif,*.jpg,*.png,*.css,*.ico" ); filterProxy.setDispatcherTypes(DispatcherType.REQUEST); return filterProxy; } }
这次测试通过,只过滤了我们想要过滤的接口。至于上面那种方式不行还有待探索。参考连接参考
FilterRegistrationBean和DelegatingFilterProxyRegistrationBean区别:
FilterRegistrationBean通过onStartup方法直接注册filter。
DelegatingFilterProxyRegistrationBean是将DelegatingFilterProxy注册到Servlet3.0+的容器中,同时实现了ApplicationContextAware接口,实例ApplicationContext通过通过传入自定义filter的名称查找对应的bean,并生成相应bean的代理对象。
DelegatingFilterProxy DelegatingFilterProxy就是一个对于servlet filter的代理,用这个类的好处就是可以通过spring容器来管理servlet filter的生命周期。
还有就是如果filter中需要一些spring容器的实例,可以通过spring直接注入,另外读取一些配置文件这些便利的操作也可以通过spring来配置实现
Spring web在设计的时候考虑到某些功能的实现是通过Filter来拦截进行实现的,如果直接的简单的实现几个Filter好像也不是不可以(平时我们就是这么用的),但是Spring框架最核心的是IOC容器,和Spring框架最好的实现就是将要实现的Filter功能注册到IOC容器的一个Bean,这样就可以和Spring IOC容器进行完美的融合,所以Spring Web设计了DelegatingFilterProxy。
本质上来说DelegatingFilterProxy就是一个Filter,其间接实现了Filter接口,但是在doFilter中其实调用的从Spring 容器中获取到的代理Filter的实现类delegate。
DelegatingFilterProxy原理
DelegatingFilterProxy根据targetBeanName从Spring 容器中获取被注入到Spring 容器的Filter实现类,在DelegatingFilterProxy配置时一般需要配置属性targetBeanName
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Override protected void initFilterBean () throws ServletException { synchronized (this .delegateMonitor) { if (this .delegate == null ) { if (this .targetBeanName == null ) { this .targetBeanName = getFilterName(); } WebApplicationContext wac = findWebApplicationContext(); if (wac != null ) { this .delegate = initDelegate(wac); } } } } protected Filter initDelegate (WebApplicationContext wac) throws ServletException { Filter delegate = wac.getBean(getTargetBeanName(), Filter.class); if (isTargetFilterLifecycle()) { delegate.init(getFilterConfig()); } return delegate; }
在DelegatingFilterProxy的实现方法doFilter中,其实最终调用的是委派的类delegate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Override protected void initFilterBean () throws ServletException { synchronized (this .delegateMonitor) { if (this .delegate == null ) { if (this .targetBeanName == null ) { this .targetBeanName = getFilterName(); } WebApplicationContext wac = findWebApplicationContext(); if (wac != null ) { this .delegate = initDelegate(wac); } } } } protected Filter initDelegate (WebApplicationContext wac) throws ServletException { Filter delegate = wac.getBean(getTargetBeanName(), Filter.class); if (isTargetFilterLifecycle()) { delegate.init(getFilterConfig()); } return delegate; }
Interceptor的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import com.ruoyi.aiot.webconfig.annotation.DeviceAuth;import com.ruoyi.common.utils.StringUtils;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.lang.reflect.Method;public class DeviceAuthTokenInterceptor implements HandlerInterceptor { @Value("${emq.value}") private String emqToken; @Value("${emq.header}") private String header; @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true ; } HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); DeviceAuth annotation = method.getAnnotation(DeviceAuth.class); if (StringUtils.isNull(annotation)){ return true ; } String token = request.getHeader(header); return emqToken.equals(token); } }
注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import com.ruoyi.aiot.webconfig.Interceptor.DeviceAuthTokenInterceptor;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration public class WebDeviceAuthConfigurer implements WebMvcConfigurer { @Bean public DeviceAuthTokenInterceptor getDeviceAuthTokenInterceptor () { return new DeviceAuthTokenInterceptor (); } @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(getDeviceAuthTokenInterceptor()).addPathPatterns("/aiot/device/state" ); } }