JAVA:Filer过滤器
介绍
Java中的Filter也被称为过滤器,它是Servlet技术的一部分,用于在web服务器上拦截请求和响应,以检查或转换其内容。
Filter的urlPatterns可以过滤特定地址http的请求,也可以利用Filter对访问请求的数据IP做限制。
文档为本人学习java过滤器过程中做的学习记录。
主要用途:
- 认证过滤:检查用户请求并确定是否合法。
- 登记日志:记录请求和响应的日志。
- 改变请求和响应:可以修改请求头和响应头。
- 数据压缩:在发送大数据前,可以压缩数据。
- 加密:对请求和响应进行加密和解密。
使用说明
Spring项目中,创建类继承Filter,实现init、doFilter、destroy方法;
- init:只在项目启动的时候运行,用来做数据的初始化
- doFilter:数据过滤,每次http请求都会过滤;filterChain.doFilter(request, servletResponse)前面的程序程序是在执行目标程序前执行,后面的程序是在目标程序后执行,若doFilter程序没有执行filterChain.doFilter,请求将会被拦截。
- destroy:项目结束时运行
多个过滤器执行顺序
注意:SpringBoot项目中,多个过滤器时,@Order + @WebFilter + @ServletComponentScan 是无法实现按顺序执行过滤器的,推荐使用FilterRegistrationBean实现过滤。
- 原因详细分析见:https://blog.csdn.net/Zong_0915/article/details/126747302
案例(使用FilterRegistrationBean限制过滤器执行循序+限制IP+修改请求返回值)
案例:有两个过滤器:MyFilter01和MyFilter02,要求按先执行过滤器MyFilter01,再执行过滤器MyFilter02顺序。
MyFilter01实现对IP的白名单限制(限制127.0.0.1仅可以访问一次),以及请求返回值的更写。
MyFilter02仅用于体现执行顺序。
- 案例参考地址:
https://blog.csdn.net/zhanwuguo8346/article/details/120498756
创建过滤器:MyFilter01用于限制访问IP,修改请求返回值;
@Slf4j public class MyFilter01 implements Filter { private Map<String, Integer> whiteIPMap; private List<String> alwaysIPList; public MyFilter01(Map<String, Integer> whiteIPMap, List<String> alwaysIPList) { this.alwaysIPList = alwaysIPList; this.whiteIPMap = whiteIPMap; } private final ObjectMapper objectMapper = new ObjectMapper(); private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("/swagger-resources", "/v3/swagger-login", "/v2/api-docs"))); private static Map<String, Integer> whiteIpMapDay = new HashMap<>(); @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("(MyFilter01)项目开始时候执行:------->>>init"); System.out.println("(MyFilter01)whiteIPMap:" + whiteIPMap); System.out.println("(MyFilter01)alwaysIPList:" + alwaysIPList); whiteIpMapDay.putAll(whiteIPMap); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("(MyFilter01)项目每次http请求时候执行:------->>>doFilter.start"); HttpServletRequest request = (HttpServletRequest) servletRequest; String ipAddr = getIpAddr(request); log.info("PermissionFilter 过滤器,请求ip为:{}", ipAddr); String path = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("[/]+$", ""); boolean contains = ALLOWED_PATHS.contains(path); servletResponse.setCharacterEncoding("utf-8"); servletResponse.setContentType("text/html;charset=utf-8"); if (alwaysIPList.contains(ipAddr) && contains) { filterChain.doFilter(request, servletResponse); } else { if (whiteIpMapDay.containsKey(ipAddr)) { Integer integer = whiteIpMapDay.get(ipAddr); if (integer > 0) { if (contains) { filterChain.doFilter(request, servletResponse); } else { whiteIpMapDay.put(ipAddr, (whiteIpMapDay.get(ipAddr) - 1)); filterChain.doFilter(request, servletResponse); } } else { BaseResponse<Void> baseResponse = new BaseResponse<>(); baseResponse.setCode(-1); baseResponse.setMessage("请求被拒绝,本IP:" + ipAddr + "每天只允许调用:" + whiteIPMap.get(ipAddr) + "次!"); String response = objectMapper.writeValueAsString(baseResponse); setServletResponseMessage(servletResponse, response); } } else { BaseResponse<Void> baseResponse = new BaseResponse<>(); baseResponse.setMessage("不支持本IP请求:" + ipAddr); baseResponse.setCode(-1); String response = objectMapper.writeValueAsString(baseResponse); setServletResponseMessage(servletResponse, response); } } System.out.println("(MyFilter01)项目每次http请求时候执行:------->>>doFilter.end"); } @Override public void destroy() { System.out.println("(MyFilter01)项目结束时候执行:------->>>destroy"); } /** * 处理返回是getWriter()还是getOutputStream(); * getWriter():out对象用于处理字符流数据。 * getOutputStream():os用于输出字符流数据或者二进制的字节流数据都可以。 */ private void setServletResponseMessage(ServletResponse servletResponse, String response) { try { servletResponse.getWriter().write(response); } catch (IOException e) { throw new RuntimeException(e); } catch (IllegalStateException e) { servletResponse.setContentType("text/plain;charset=UTF-8"); // 设置内容类型和编码 try { ServletOutputStream outputStream = servletResponse.getOutputStream(); outputStream.write(response.getBytes(StandardCharsets.UTF_8)); // 写入字节数组 } catch (IOException ex) { throw new RuntimeException(ex); } } } public static String getIpAddr(HttpServletRequest request) { String ipAddress = request.getHeader("x-forwarded-for"); if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("Proxy-Client-IP"); } if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("WL-Proxy-Client-IP"); } if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getRemoteAddr(); if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) { //根据网卡取本机配置的IP InetAddress inet = null; try { inet = InetAddress.getLocalHost(); } catch (UnknownHostException e) { e.printStackTrace(); } // ipAddress= inet.getHostAddress(); ipAddress = "127.0.0.1"; } } //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 if (ipAddress.length() > 15) { //"***.***.***.***".length() = 15 if (ipAddress.indexOf(",") > 0) { ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); } } return ipAddress; } }
过滤器:MyFilter02仅作日志记录。体现执行顺序
public class MyFilter02 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("(MyFilter02)项目开始时候执行:------->>>init"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("(MyFilter02)项目每次http请求时候执行:------->>>doFilter.start"); filterChain.doFilter(servletRequest, servletResponse); System.out.println("(MyFilter02)项目每次http请求时候执行:------->>>doFilter.end"); } @Override public void destroy() { System.out.println("(MyFilter02)项目结束时候执行:------->>>destroy"); } }
application.yml中设置白名单访问次数,一直有权限访问的白名单。
127.0.0.1限制仅可以访问一次。
permission: white-ip: '{"127.0.0.1": 1, "192.168.16.30": 2}' always-ip: 127.0.0.1,127.0.0.1
创建配置类:FilterConfig使用@Value读取配置文件值,注册FilterRegistrationBean到Spring容器中,并设置两个过滤器的执行顺序和过滤路径
@Configuration public class FilterConfig { @Value("#{${permission.white-ip}}") private Map<String, Integer> whiteIPMap; @Value("#{'${permission.always-ip}'.split(',')}") private List<String> alwaysIPList; @Bean public FilterRegistrationBean<MyFilter01> myFilterRegistrationBean01() { FilterRegistrationBean<MyFilter01> bean = new FilterRegistrationBean<>(); bean.setFilter(new MyFilter01(whiteIPMap, alwaysIPList)); bean.addUrlPatterns("/api/*"); bean.setOrder(1); return bean; } @Bean public FilterRegistrationBean<MyFilter02> myFilterRegistrationBean02() { FilterRegistrationBean<MyFilter02> bean = new FilterRegistrationBean<>(); bean.setFilter(new MyFilter02()); bean.addUrlPatterns("/*"); bean.setOrder(2); return bean; } }
程序中用到的BaseResponse类:
@Data public class BaseResponse<T> implements Serializable { private int code; private T data; private String message; private String description; public BaseResponse() { } public BaseResponse(int code, T data, String message, String description) { this.code = code; this.data = data; this.message = message; this.description = description; } public BaseResponse(int code, T data, String message) { this(code, data, message, ""); } public BaseResponse(int code, T data) { this(code, data, "", ""); } public BaseResponse(ErrorCodeEnum errorCode) { this(errorCode.getCode(), null, errorCode.getMessage(), errorCode.getDescription()); } }
测试结果:
第一次执行接口ApiPost(127.0.0.1)接口返回:数据正常创建
第二次执行接口ApiPost(127.0.0.1)接口返回:提示IP只能每天访问一次(这里没有提供更新:MyFilter01.whiteIpMapDay来实现每天可反问一次,可以使用@Schedule每天定时对静态变态做充值)。
执行日志:可以看出过滤器按照顺序执行
参考网址
https://blog.csdn.net/zhanwuguo8346/article/details/120498756
https://blog.csdn.net/Zong_0915/article/details/126747302