拦截器和过滤器有什么区别

发布时间 2023-09-26 21:21:43作者: qd372502
拦截器和过滤器有什么区别?拦截器范围较小只拦截SpringMVC请求,过滤器是Servlet提供的,拦截一切web请求,web请求还能不是SpringMVC吗
拦截器(Interceptors)和过滤器(Filters)是在Java Web应用程序中用于处理请求和响应的两种不同的机制,它们有一些关键区别:
 
1. **作用范围**:
   - 拦截器:拦截器是Spring框架提供的一种机制,主要用于拦截Spring MVC的请求。它们通常用于在控制器处理请求之前或之后执行一些自定义的逻辑,如身份验证、日志记录、性能监控等。
   - 过滤器:过滤器是Servlet规范提供的,它们可以拦截所有Web请求,不仅限于Spring MVC。过滤器在Web应用程序中的范围更广泛,可以在请求进入Servlet容器时或离开Servlet容器时执行一些操作,例如修改请求参数、处理字符编码、gzip压缩等。
 
2. **调用时机**:
   - 拦截器:拦截器在Spring MVC中的Controller调用之前或之后执行。它们通常与Spring MVC的生命周期集成,可以访问Spring的上下文和Bean。
   - 过滤器:过滤器在请求进入Servlet容器之前或离开Servlet容器之后执行。它们与Servlet容器的生命周期相关联,与Spring框架无关。
 
3. **配置方式**:
   - 拦截器:拦截器的配置是通过Spring MVC的配置文件(如XML配置文件或Java配置类)来完成的。通常,你需要定义一个实现了Spring的HandlerInterceptor接口的拦截器类,并将其配置为Spring MVC的一部分。
   - 过滤器:过滤器的配置是通过Web应用程序的web.xml文件来完成的。你需要在web.xml文件中配置过滤器的类和拦截的URL模式。
 
4. **用途**:
   - 拦截器:主要用于处理Spring MVC请求的一些额外逻辑,如权限验证、日志记录等。
   - 过滤器:通常用于处理与Servlet容器相关的任务,例如字符编码、请求参数处理等,也可以用于一些全局的任务,如安全性、压缩等。
 
总的来说,拦截器和过滤器都是在Web应用程序中处理请求和响应的重要组件,但它们的作用范围、调用时机、配置方式和用途有所不同。拦截器更专注于Spring MVC请求处理的定制,而过滤器更通用,可以应用于所有Web请求,无论是否涉及Spring MVC。

拦截器和过滤器是Java Web开发中常用的两个概念,它们的主要作用都是在请求处理的过程中加入一些额外的功能。不过它们之间还是有一些区别的哦。
 
首先,拦截器是基于面向切面编程(AOP)的思想实现的,它是通过代理模式来实现的。拦截器可以在请求的多个处理环节中插入一些自定义逻辑。它主要用于对业务逻辑的增强,比如日志记录、权限验证等。拦截器是通过在配置文件中进行配置,并在代码中进行调用。
 
而过滤器是基于Servlet规范实现的,它是在请求到达Servlet之前或者离开Servlet之后对请求和响应进行处理的。它主要用于对请求的过滤和处理,比如字符编码转换、请求参数处理等。过滤器是通过在web.xml文件中进行配置,并在请求处理链中按照顺序执行。
 
所以,拦截器和过滤器的一个主要区别就在于实现方式和作用范围。拦截器基于AOP实现,作用范围更广,可以在业务逻辑的不同环节插入自定义逻辑。而过滤器基于Servlet规范,作用范围相对较窄,主要用于对请求和响应进行过滤和处理。

使用拦截器拦截Controller请求,校验令牌
/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {
 
@Autowired
private JwtProperties jwtProperties;
 
/**
* 校验jwt
* 在Handler之前执行。就是Controller中标记了@XxxMapping注解的方式
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断当前拦截到的是Controller的方法还是其他资源
if (!(handler instanceof HandlerMethod)) {
//当前拦截到的不是动态方法,直接放行
return true;
}
 
//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getAdminTokenName());
 
//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
//当前登录的员工ID
Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
log.info("当前员工id:{}", empId);
//把登录ID设置到ThreadLocal上
BaseContext.setCurrentId(empId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码 没有token的人通过直接访问方法
response.setStatus(401);
return false;
}
}
 
/**
* 渲染视图结束后执行,执行释放资源的操作
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//从ThreadLocal 上删除登录ID
BaseContext.removeCurrentId();
}
}
 
 
这段代码看起来是一个Spring MVC的拦截器的 `preHandle` 方法,它在请求到达Controller方法之前执行一些操作,主要是用于身份验证和权限控制。以下是每句代码的详细解释:
 
1. `@Override`:这是一个Java注解,表示该方法是一个覆盖(重写)父类或接口的方法。
 
2. `public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception`:这是 `preHandle` 方法的定义,它接收三个参数:
   - `HttpServletRequest request`:表示HTTP请求对象,可以用于获取请求信息。
   - `HttpServletResponse response`:表示HTTP响应对象,可以用于设置响应信息。
   - `Object handler`:表示被拦截的处理器,通常是Controller的方法。
 
3. `if (!(handler instanceof HandlerMethod))`:这是一个条件判断,它检查被拦截的处理器是否是HandlerMethod的实例。HandlerMethod是Spring MVC中用于表示Controller方法的类。如果不是HandlerMethod的实例,说明当前拦截到的不是动态方法,而是其他资源,直接返回`true`,即放行请求。
 
4. `String token = request.getHeader(jwtProperties.getAdminTokenName())`:这行代码从HTTP请求头中获取令牌(token)。令牌通常用于身份验证和授权,它是一种在客户端和服务器之间传递身份信息的方式。
 
5. `try { ... } catch (Exception ex) { ... }`:这是一个异常处理块,它捕获可能发生的异常,并处理它们。
 
6. `log.info("jwt校验:{}", token)`:这行代码使用日志记录工具(可能是SLF4J或Log4j等)记录日志,显示正在进行JWT令牌校验,并输出令牌的内容。
 
7. `Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token)`:这行代码使用`JwtUtil`工具类来解析JWT令牌。`JwtUtil.parseJWT`方法接收两个参数,一个是秘钥(用于解密令牌),另一个是令牌本身。解析后的结果被存储在`claims`对象中,其中包含了令牌中的声明信息。
 
8. `Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString())`:这行代码从解析后的JWT声明中获取员工ID(假设在令牌中有一个名为`EMP_ID`的声明),并将其转换为长整型(`Long`)。
 
9. `log.info("当前员工id:{}", empId)`:这行代码再次使用日志记录工具,输出当前员工的ID。
 
10. `BaseContext.setCurrentId(empId)`:这行代码将当前员工的ID设置到一个`BaseContext`中,可能是一个自定义的上下文工具类,用于在请求处理中共享数据。
 
11. `return true`:如果JWT令牌验证通过,这行代码表示请求通过拦截器,可以继续访问Controller方法。
 
12. `response.setStatus(401)`:如果JWT令牌验证不通过,这行代码设置HTTP响应状态码为401(未授权)。这是HTTP的标准状态码,表示身份验证失败。
 
13. `return false`:如果JWT令牌验证不通过,这行代码表示请求不通过拦截器,不允许继续访问Controller方法。
 
总的来说,这段代码实现了一个拦截器,用于在请求到达Controller方法之前进行JWT令牌的验证,如果验证通过,允许继续访问Controller方法,否则返回401状态码表示未授权。

使用过滤器过滤WEB端所有非法请求,主要还是校验token合法性。
/**
* d:
* Description: TODO
* ClassName: com.heima.app.gateway.filters.AuthorizeFilter
* Author: regular
* Since: 2023/9/19 17:39
* Version: 1.0
*/
@Component
@Slf4j
public class AuthorizeFilter implements GlobalFilter, Ordered {
/**
* 鉴权
* @Description: AuthorizeFilter
* @Author: regular
* @Version: 1.0
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//0.如果是登录请求,直接放行
String url = request.getURI().getPath();
if(StringUtils.isNotBlank(url) && url.contains("/login")){
return chain.filter(exchange);
}
//1.从header中获取token
String token = request.getHeaders().getFirst("token");
boolean flag= true;
//2.判断token是否存在,如果不存在,响应401
if(StringUtils.isBlank(token)){
flag= false;
}
//3.判断token是否过期
try {
Claims claimsBody = AppJwtUtil.getClaimsBody(token);
if (Objects.isNull(claimsBody)) {
//解析token失败,响应401
flag= false;
}
int result = AppJwtUtil.verifyToken(claimsBody);
if (result == 1 || result == 2) {
//5.是,返回401
flag= false;
}
} catch (Exception e) {
e.printStackTrace();
flag= false;
}
if(!flag){
//解析token失败,响应401
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}else{
//否,放行
return chain.filter(exchange);
}
 
}
 
@Override
public int getOrder() {
return 0;
}
}