前言
本篇博文主要是想表达以下几个方面
- 什么是过滤器?
- 什么是拦截器?
- 过滤器与拦截器的区别在于什么地方?
- 过滤器与拦截器各自应用的场景是什么?
- 用过滤器与拦截器实现实际开发中的功能需求
什么是过滤器-Filter
前人言:取你所想
- 依赖于Servlet容器,实现基于回调函数,对所有的请求进行过滤
- Filter随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。
- 启动时加载过滤器的实例,并调用**init()**方法来初始化实例
- 每次的请求只能调用**doFilter()**进行处理
- 当服务停止时,调用destory销毁实例
缺点
- 只能是在容器初始化时调用一次
什么是拦截器-Interceptor
前人言:用你所用
- 依赖于web框架,依赖于Java的反射机制,是AOP的思想体现
- 基于web框架,可以调用IOC容器中的各种依赖,可以进行一些业务操作
- 实现拦截器的俩种方式(spring)
- 实现HandlerInterceptor接口
- 实现WebRequestInterceptor接口 或者 实现 WebRequestInterceptor 的类
- HandlerInterceptor接口定义方法preHandle, postHandle, 和afterCompletion
- preHandle:请求方法前置拦截,该方法会在Controller处理之前进行调用,Spring中可以有多个Interceptor,这些拦截器会按照设定的Order顺序调用,当有一个拦截器在preHandle中返回false的时候,请求就会终止
- postHandle:preHandle返回结果为true时,在Controller方法执行之后,视图渲染之前被调用
- afterCompletion:在preHandle返回ture,并且整个请求结束之后,执行该方法。
过滤器与拦截器的区别
相同
- 都是aop思想的体现
- 都是对请求的方法进行拦截,对方法进行增强
不同
-
实现原理不同
- Filter基于回调函数
- Interceptor基于Java的反射机制(动态代理实现)
-
使用范围不同
- Filter在Servlet规范中定义的,依赖于Tomcat等容器,只能在web的程序中使用
- Interceptor 是spring的组件,由spring管理,可以单独使用
-
触发时机不一样
扫描二维码关注公众号,回复:
13923892 查看本文章
拦截器的功能实现
功能需求: 实现用户的登录验证
拦截器的配置类(InterceptorConfig)
- 一般主要是用来配置拦截的路径/放行的路径
- 注入Bean,在发生拦截时生效.举例: 下面的LoginInterceptor
/**
* 拦截配置
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Bean
LoginInterceptor loginInterceptor() {
return new LoginInterceptor();
}
/**
* 添加拦截路径
* 放行拦截路径
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截全部路径,这个跨域需要放在最上面
registry.addInterceptor(corsInterceptor()).addPathPatterns("/**");
// 拦截路径
registry.addInterceptor(loginInterceptor()).addPathPatterns("/api/v1/pri/*/*/**")
//放行路径
.excludePathPatterns("/api/v1/pri/user/login", "/api/v1/pri/user/register");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
实现拦截器
-
实现HandlerInterceptor接口,重写里面preHandle的方法
-
这里实现一个用户权限校验的过程,前端向后端发送请求的时候,拦截请求,获取里面的token,将"破译"后的token 放入request域中返回
-
sendJsonMessage类,当校验token为null时直接response响应给前端
/**
* 登录拦截器
* 进入到Restcontroller之前的方法
* @author ouj
*/
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
//从请求中获取token
String accessToken = request.getHeader("token");
if (accessToken == null) {
accessToken = request.getParameter("token");
}
// 判断token是否为null
if (StringUtils.isNotBlank(accessToken)) {
Claims claims = JWTUtils.checkJWT(accessToken);
if (claims == null) {
sendJsonMessage(response, "登录过期,请重新登录");
}
Integer id = (Integer) claims.get("id");
String name = (String) claims.get("name");
//在request域中把id的属性放进"User_id"中
request.setAttribute("user_id", id);
request.setAttribute("name", name);
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
// 登录失败
return false;
}
/**
* 响应json数据给前端
* @param response
* @param obj
*/
public static void sendJsonMessage(HttpServletResponse response, Object obj) {
try {
/**
* 将数据转成Json字符串,然后以数据流的形式writer写出去
* ObjectMapper 是Jackson库中主要用于读取和写入Json数据的类,能够很方便地将Java对象转为Json格式的数据
* 用于后端Servlet向AJAX传递Json数据,动态地将数据展示在页面上。为了能够使用这个类,需要先下载Jackson库。
*/
ObjectMapper objectMapper = new ObjectMapper();
response.setContentType("application/json; charset=utf-8");
//返回一个输出流
PrintWriter writer = response.getWriter();
// writeValue(参数,obj):直接将传入的对象序列化为json,并且返回给客户端
// writeValueAsString(obj):将传入的对象序列化为json,返回给调用者
writer.print(objectMapper.writeValueAsString(obj));
writer.close();
response.flushBuffer();
} catch (Exception e) {
e.printStackTrace();
}
}
@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);
}
}
过滤器的功能实现
实现功能需求:用户校验
/**
* 过滤器
*/
@WebFilter(urlPatterns = "/api/v1/pri/*",filterName = "loginfilter")
public class LoginFilter implements Filter {
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* 容器加载的时候
* @param filterConfig
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("开启过滤器");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤开始");
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse rep = (HttpServletResponse) servletResponse;
//从headers中得到token
String token =req.getHeader("token");
// 如果header里面没有token时,从请求的参数里面拿token
if (StringUtils.isEmpty(token)){
token = req.getParameter("token");
}
//如果还为空,直接返回json字符串或跳转index页面
if (!StringUtils.isEmpty(token)){
// 判断token是否合法
//在登录时在sessionMap里存了token,通过token取出user,若为空则未登录,若不为空则显示正确
User user = UserServiceImpl.sessionMap.get(token);
if(user !=null){
filterChain.doFilter(servletRequest,servletResponse);
}else{
JsonData jsonData = JsonData.buildError("登录失败,token无效",-2);
//json转字符
String jsonStr = objectMapper.writeValueAsString(jsonData);
renderJson(rep,jsonStr);
}
}else {
JsonData jsonData = JsonData.buildError("登录失败,token无效",-3);
String jsonStr = objectMapper.writeValueAsString(jsonData);
renderJson(rep,jsonStr);
}
}
private void renderJson(HttpServletResponse response,String json){
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
try(PrintWriter writer=response.getWriter()){
writer.print(json);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 容器销毁的时候
*/
@Override
public void destroy() {
System.out.println("destory LoginFilter");
}
}
应用场景
- 过滤器
- 字节码编码转换
- 敏感词过滤 (列入防止sql注入)
- 权限验证
- 压缩响应信息
- 拦截器
- 权限验证
- 日志记录
- 性能监控
- 通用行为
- 读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现5)