JAVA:Filer过滤器+案例:请求IP访问限制和请求返回值修改

news2024/9/22 1:55:24

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1934612.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

上位机图像处理和嵌入式模块部署(香橙派AI Pro开发板试用)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 和工控机相比较,linux嵌入式开发板使用上面方便很多、也容易很多。很多的第三方库都可以通过yum、apt-get这样的方法直接下载到,不需要自己通过源代码重新进行编译、安装。因为自…

PHP萌宠之家微信小程序系统源码

&#x1f43e;萌宠之家微信小程序&#x1f43e; —— 铲屎官们的温馨小窝✨ &#x1f3e0;【一键开启萌宠乐园】&#x1f3e0; 亲们&#xff0c;是不是每次刷手机都忍不住想看看那些软萌可爱的毛孩子&#xff1f;现在&#xff0c;有了“萌宠之家”微信小程序&#xff0c;你的…

进程与线程(二)线程相关

目录 一. 基本概念二. 线程与进程的比较三. 线程的属性四. 线程的状态与切换五. 线程的组织与控制线程控制块&#xff08;TCB&#xff09;线程创建线程终止 六. 线程的实现方式用户级线程&#xff08;User-Level-Thread&#xff0c;ULT&#xff09;内核级线程&#xff08;Kerne…

刷题了:977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

学习记录&#xff0c;主要参考&#xff1a;代码随想录 977.有序数组的平方 题目链接&#xff1a;https://leetcode.cn/problems/remove-element/ 文章讲解&#xff1a;https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html 视频讲解&#xff1a;http…

【go】Excelize处理excel表 带合并单元格、自动换行与固定列宽的文件导出

文章目录 1 简介2 相关需求与实现2.1 导出带单元格合并的excel文件2.2 导出增加自动换行和固定列宽的excel文件 1 简介 之前整理过使用Excelize导出原始excel文件与增加数据校验的excel导出。【go】Excelize处理excel表 带数据校验的文件导出 本文整理使用Excelize导出带单元…

签名优化:请求数据类型不是`application/json`,将只对随机数进行签名计算,例如文件上传接口。

文章目录 I 签名进行请求数据类型类型判断1.1 常见的ContentType1.2 签名切面处理1.3 文件上传案例1.4 处理接口信息背景: 文件上传接口的请求数据类型通常为multipart/form-data,方便携带文本域和使用接口文档进行调试。 如果携带JSON数据,不方便调试接口。 前端数据也要特…

如何学习Hadoop:糙快猛的大数据之路(利用GPT 学习)

目录 引言Hadoop是什么&#xff1f;学习Hadoop的"糙快猛"之道1. 不要追求完美&#xff0c;先动手再说2. 从简单的MapReduce开始3. 利用大模型加速学习4. 循序渐进&#xff0c;建立知识体系 构建您的Hadoop技能树1. 夯实基础&#xff1a;Linux和Java2. 深入理解HDFS3.…

Chromium CI/CD 之Jenkins实用指南2024 - 常见的构建错误(六)

1. 引言 在前一篇《Chromium CI/CD 之 Jenkins - 发送任务到Ubuntu&#xff08;五&#xff09;》中&#xff0c;我们详细讲解了如何将Jenkins任务发送到Ubuntu节点执行&#xff0c;并成功验证了文件的传输和回传。这些操作帮助您充分利用远程节点资源&#xff0c;提升了构建和…

收银系统源码-商城下单,门店接单

随着新零售时代的不断进步&#xff0c;线下线上一体化的收银系统&#xff0c;被很多门店越来越重视。用户在线上商城下单后&#xff0c;门店如何接单呢&#xff0c;如何处理订单呢&#xff1f; 1.收银系统开发语言 核心开发语言: PHP、HTML5、Dart后台接口: PHP7.3后合管理网…

【BUG】已解决:note: This is an issue with the package mentioned above,not pip.

已解决&#xff1a;note: This is an issue with the package mentioned above&#xff0c;not pip. 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷…

操作系统发展简史(Unix/Linux 篇 + DOS/Windows 篇)+ Mac 与 Microsoft 之风云争霸

操作系统发展简史&#xff08;Unix/Linux 篇&#xff09; 说到操作系统&#xff0c;大家都不会陌生。我们天天都在接触操作系统 —— 用台式机或笔记本电脑&#xff0c;使用的是 windows 和 macOS 系统&#xff1b;用手机、平板电脑&#xff0c;则是 android&#xff08;安卓&…

1. 个人谈心 ——【如何学习编程及合理安排休息时间】

&#x1f4d6; 声明 ! ! ! 此文章仅仅属于个人思想&#xff0c;如有不满或者意见不相同&#xff0c;可以在评论区讨论留言&#xff0c;非常感谢支持&#xff01;&#xff01;&#xff01; &#x1f495;个人主页&#xff1a;三亿老奶奶心中的梦 &#x1f4d8;收录专栏&#xff…

AI(Adobe lliustrator)教程+软件包

简介&#xff1a; 软件主要应用于印刷出版、海报书籍排版、专业插画、多媒体图像处理和互联网页面的制作等&#xff0c;也可以为线稿提供较高的精度和控制&#xff0c;适合生产任何小型设计到大型的复杂项目。 通常用于创建LOGO(商标或徽标)&#xff0c;图标&#xff0c;插图…

Spring-Boot基础--yaml

目录 Spring-Boot配置文件 注意&#xff1a; YAML简介 YAML基础语法 YAML:数据格式 YAML文件读取配置内容 逐个注入 批量注入 ConfigurationProperties 和value的区别 Spring-Boot配置文件 Spring-Boot中不用编写.xml文件&#xff0c;但是spring-Boot中还是存在.prope…

深入理解CSS基础【代码审计实战指南】

文章目录 为什么需要cssCSS语法CSS的组成css注释&#xff1a; 快速入门示例&#xff1a;常用样式字体颜色和边框颜色介绍颜色示例&#xff1a;边框边框的宽度与高度 字体样式背景样式文本居中 字体颜色和边框颜色介绍颜色示例&#xff1a;边框边框的宽度与高度 字体样式背景样式…

了解PHY,MAC芯片

物理层芯片叫做PHY、数据链路层芯片叫做MAC。 1、MCU内置MAC芯片外部PHY芯片 1.1、PHY PHY在发送数据的时候&#xff0c;收到MAC过来的数据&#xff08;对PHY来说&#xff0c;没有帧的概念&#xff0c;对它来说&#xff0c;都是数据而不管什么地址&#xff0c;数据还是CRC&am…

拖拽上传(预览图片)

需求 点击上传图片&#xff0c;或直接拖拽图片到红色方框里面也可上传图片&#xff0c;上传后预览图片 效果 实现 <!DOCTYPE html> <html lang"zh-cn"><head><meta charset"UTF-8"><meta name"viewport" content&…

GIT--git clone fatal [文件过大或网络不稳定] [大型仓库]

GIT--git clone fatal 1 介绍1.1 原因分类1.2 文件过大或网络不稳定 2 分析3 操作3.1 指定克隆深度【浅克隆】3.2 分批次下载3.3 增大Git的HTTP POST缓冲区大小3.4 配置git的最低速度和最低速度时间(单位&#xff1a;秒)3.5 压缩3.6 过滤下载 git filter branch3.7 仅克隆一个分…

Go语言并发编程-Context上下文

Context上下文 Context概述 Go 1.7 标准库引入 context&#xff0c;译作“上下文”&#xff0c;准确说它是 goroutine 的上下文&#xff0c;包含 goroutine 的运行状态、环境、现场等信息。 context 主要用来在 goroutine 之间传递上下文信息&#xff0c;包括&#xff1a;取…

AI发展下的伦理挑战,应当如何应对?

人工智能&#xff08;AI&#xff09;的快速发展带来了许多伦理挑战&#xff0c;如何应对这些挑战是一个复杂而多方面的问题。以下是一些应对策略和建议&#xff1a; 坚持伦理先行原则&#xff1a; 制定科技伦理规范和标准&#xff0c;将伦理规范嵌入人工智能开发、运行等各个阶…