Filter & Interceptor
# 过滤器和拦截器区别
# 过滤器 Filters
过滤器是webserver的一部分,不属于SpringFramework。可以使用过滤器来控制请求或者阻断请求。
Spring Security就是基于过滤器实现了认证和授权。通过配置一个名为 DelegatingFilterProxy
的拦截器, Spring Security可以拦截所有出入流量,因为基于拦截器,所以可以工作在SpringMVC 外。
# 拦截器 HandlerInterceptors
拦截器是 Spring MVC
的一部分,工作在 DispatcherServlet
和 Controllers
之间。我们可以在 到达Controller之前、View 渲染前后对请求进行拦截处理。
# Filter的使用
通过实现接口 javax.servlet.Filter
定义过滤器。
@Component
public class LogFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(LogFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
logger.info("Hello from: " + request.getLocalAddr());
chain.doFilter(request, response);
}
}
2
3
4
5
6
7
8
9
10
11
12
通过注解 @Component
将过滤器加入到 Spring 容器中,容器会自动处理。
# HandlerInterceptor 的使用
在 SpringBoot 中使用拦截器分为两个步骤,首先定义一个拦截器,然后通过配置类配置拦截器。
通过实现接口HandlerInterceptor
定义拦截器,重写三个方法:
preHandle()
在执行 Controller 方法之前调用postHandle()
在执行 Controller的渲染 View 代码方法之前执行afterCompletion()
在执行 Controller 的渲染 View 代码方法之后执行
@Component
public class LogInterceptor implements HandlerInterceptor {
private static Logger log = LoggerFactory.getLogger(LogInterceptor.class);
/**
* 执行控制器方法前执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("Hello from:" + request.getLocalAddr());
return true;
}
/**
* 执行完控制器方法,但在渲染ModelAndView前执行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 控制器方法执行完执行
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCopletion");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
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
通过实现 WebMvcConfigure
的类,再将这个类放入到容器中实现对 SpringMVC的配置。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LogInterceptor logInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor);
}
}
2
3
4
5
6
7
8
9
10
11
# 验证
添加一个控制器方法,访问这个方法,查看日志打印情况。
@RestController
public class TestController {
@GetMapping("/test")
public String handleTest() {
return "ResponseContent";
}
}
2
3
4
5
6
7
8
通过 curl http://localhost:9001/test
打印日志:
17:18:15.452 [http-nio-9001-exec-1] INFO i.g.human0722.demo.filter.LogFilter - Hello from:0:0:0:0:0:0:0:1
17:18:15.461 [http-nio-9001-exec-1] INFO i.g.h.d.interceptor.LogInterceptor - Hello from:0:0:0:0:0:0:0:1
Inside Controller
17:18:15.478 [http-nio-9001-exec-1] INFO i.g.h.d.interceptor.LogInterceptor - postHandle
17:18:15.478 [http-nio-9001-exec-1] INFO i.g.h.d.interceptor.LogInterceptor - afterCopletion
2
3
4
5
可以看到执行顺序是: 自定义的拦截器、过滤器的 preHandle方法、postHandle方法、afterCompletion方法。
如果控制器返回的是 ModelAndView 相关类型,拦截器会被执行2次。如果只想让执行1次,要在第一次执行preHandle()后对请求设置修改,标记为已经处理过,第二次判断这个标志。
# 总结
过滤器适合粒度较大的控制,比如认证直接阻断请求。
过滤器适合粒度更新的控制,比如针对某些方法鉴权。
# 应用
# 过滤器过滤请求
OncePreRequestFilter 是 Filter 的一个子类, 内部代码保证了一次请求该过滤器只会被执行一次。且自动将
ServletRequest
转换成了HttpServletRequest
。
@Component
public class LogFilter extends OncePerRequestFilter {
private Logger log = LoggerFactory.getLogger(LogFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
log.info("Hello from:" + request.getRemoteAddr());
String token = request.getHeader("token");
if (!StringUtils.hasLength(token)) {
response.sendError(HttpStatus.BAD_REQUEST.value(), "Bad Request");
// 如果没有 return, 会正常进入后续流程,不是直接返回。
return;
}
filterChain.doFilter(request, response);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 拦截器配合注解实现鉴权
1、 先自定义一个权限注解 @Auth
,用来表示访问该方法/类需要的权限标识。
// Auth.class
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Documented
@Target({METHOD, TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Auth {
String[] permissions() default { };
String logical() default Logical.AND;
}
// Logical.class
public class Logical {
public static final String OR = "logical_or";
public static final String AND = "logical_and";
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2、定义拦截器
@Component
public class LogInterceptor implements HandlerInterceptor {
private static Logger log = LoggerFactory.getLogger(LogInterceptor.class);
/**
* 执行控制器方法前执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean flag = true;
String token = request.getHeader("token");
log.info("Token from LogInterceptor:" + request.getHeader("token"));
String[] split = token.split(",");
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Boolean clazzHasPermission = true;
Auth clazzAuth = handlerMethod.getMethod().getDeclaringClass().getAnnotation(Auth.class);
if (clazzAuth != null) {
String[] clazzPermissions = clazzAuth.permissions();
List<String> requiredPermissions = new ArrayList<>(Arrays.asList(clazzPermissions));
String clazzLogical = clazzAuth.logical();
log.info("Clazz require permission: {}, Mode: {}, Has permissions: {}", requiredPermissions, clazzLogical, split);
for (String permission: split) {
requiredPermissions.remove(permission);
}
clazzHasPermission = (Logical.OR.equals(clazzLogical) && requiredPermissions.size() < split.length) || (Logical.AND.equals(clazzLogical) && requiredPermissions.size() == 0);
}
Boolean methodHasPermission = true;
if (clazzHasPermission) {
Auth methodAuth = handlerMethod.getMethod().getAnnotation(Auth.class);
if (methodAuth != null) {
String[] methodPermissions = methodAuth.permissions();
String methodLogical = methodAuth.logical();
List<String> requiredPermissions = new ArrayList<>(Arrays.asList(methodPermissions));
log.info("Method require permissions: {}, Mode: {}, Has permissions: {}", requiredPermissions, methodLogical, split);
for (String permission : split) {
requiredPermissions.remove(permission);
}
methodHasPermission = (Logical.OR.equals(methodLogical) && requiredPermissions.size() < split.length) || (Logical.AND.equals(methodLogical) && requiredPermissions.size() == 0);
}
}
flag = clazzHasPermission && methodHasPermission;
}
if (!flag) {
response.reset();
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
return flag;
}
/**
* 执行完控制器方法,但在渲染ModelAndView前执行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 控制器方法执行完执行
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
3、配置拦截器
import io.github.human0722.demo.interceptor.LogInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@RequiredArgsConstructor
public class InterceptorConfig implements WebMvcConfigurer {
final private LogInterceptor logInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17