资讯专栏INFORMATION COLUMN

过滤器应用【编码、敏感词、压缩、转义过滤器】

BlackHole1 / 681人阅读

摘要:前言在上篇博文中,我们已经讲解了过滤器的基本概念,使用以及简单的应用了。这篇博文主要讲解过滤器的高级应用。。编码过滤器目的解决全站的乱码问题开发过滤器将和强转成协议的第一次测试中向浏览器回应中文数据,没有出现乱码。

前言
在上篇博文中,我们已经讲解了过滤器的基本概念,使用以及简单的Servlet应用了。这篇博文主要讲解过滤器的高级应用。。
编码过滤器

目的:解决全站的乱码问题

开发过滤器
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        //将request和response强转成http协议的
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        HttpServletResponse httpServletResponse = (HttpServletResponse) resp;

        httpServletRequest.setCharacterEncoding("UTF-8");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("text/html;charset=UTF-8");
        
        chain.doFilter(httpServletRequest, httpServletResponse);
    }

第一次测试

Servlet1中向浏览器回应中文数据,没有出现乱码。

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.getWriter().write("看完博客点赞!");

    }

分析

上面的过滤器是不完善的,因为浏览器用get方式提交给服务器的中文数据,单单靠上面的过滤器是无法完成的!

那么我们需要怎么做呢??我们之前解决get方式的乱码问题是这样的:使用request获取传递过来的数据,经过ISO 8859-1反编码获取得到不是乱码的数据(传到Servlet上的数据已经被ISO 8859-1编码过了,反编码就可以获取原来的数据),再用UTF-8编码,得到中文数据!

参考我之前的博文:https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=100000001&idx=6&sn=b0c346d7f17a097525ad106291aad28c&chksm=6bd740005ca0c916f72872122dff3bad8c49f9f690ea51b442b542dbbb654382bb663a01473b&mpshare=1&scene=1&srcid=0206hvswcHK67to1b4tr46AK#rd

在Servlet获取浏览器以GET方式提交过来的中文是乱码的根本原因是:getParameter()方法是以ISO 8859-1的编码来获取浏览器传递过来的数据的,得到的是乱码

既然知道了根本原因,那也好办了:过滤器传递的request对象,使用getParameter()方法的时候,获取得到的是正常的中文数据

也就是说,sun公司为我们提供的request对象是不够用的,因为sun公司提供的request对象使用getParameter()获取get方式提交过来的数据是乱码,于是我们要增强request对象(使得getParameter()获取得到的是中文)!

增强request对象

增强request对象,我们要使用包装设计模式!

包装设计模式的五个步骤:

1、实现与被增强对象相同的接口

2、定义一个变量记住被增强对象

3、定义一个构造器,接收被增强对象

4、覆盖需要增强的方法

5、对于不想增强的方法,直接调用被增强对象(目标对象)的方法

sun公司也知道我们可能对request对象的方法不满意,于是提供了HttpServletRequestWrapper类给我们实现(如果实现HttpServletRequest接口的话,要实现太多的方法了!)


    class MyRequest extends HttpServletRequestWrapper {
    
        private HttpServletRequest request;
    
        public MyRequest(HttpServletRequest request) {
            super(request);
            this.request = request;
        }
    
        @Override
        public String getParameter(String name) {
            String value = this.request.getParameter(name);
    
            if (value == null) {
                return null;
            }
    
            //如果不是get方法的,直接返回就行了
            if (!this.request.getMethod().equalsIgnoreCase("get")) {
                return value;
            }
    
            try {
    
                //进来了就说明是get方法,把乱码的数据
                value = new String(value.getBytes("ISO8859-1"), this.request.getCharacterEncoding());
                return value ;
    
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
    
                throw new RuntimeException("不支持该编码");
            }
    
        }
    }

将被增强的request对象传递给目标资源,那么目标资源使用request调用getParameter()方法的时候,获取得到的就是中文数据,而不是乱码了!

        //将request和response强转成http协议的
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        HttpServletResponse httpServletResponse = (HttpServletResponse) resp;

        httpServletRequest.setCharacterEncoding("UTF-8");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("text/html;charset=UTF-8");

        MyRequest myRequest = new MyRequest(httpServletRequest);

        //传递给目标资源的request是被增强后的。
        chain.doFilter(myRequest, httpServletResponse);
第二次测试

使用get方式传递中文数据给服务器

敏感词的过滤器

如果用户输入了敏感词(傻b、尼玛、操蛋等等不文明语言时),我们要将这些不文明用于屏蔽掉,替换成符号!

要实现这样的功能也很简单,用户输入的敏感词肯定是在getParameter()获取的,我们在getParameter()得到这些数据的时候,判断有没有敏感词汇,如果有就替换掉就好了!简单来说:也是要增强request对象

增强request对象
    class MyDirtyRequest extends HttpServletRequestWrapper {
    
        HttpServletRequest request;
    
        //定义一堆敏感词汇
        private List list = Arrays.asList("傻b", "尼玛", "操蛋");
    
        public MyDirtyRequest(HttpServletRequest request) {
            super(request);
            this.request = request;
        }
    
        @Override
        public String getParameter(String name) {
    
            String value = this.request.getParameter(name);
    
            if (value == null) {
                return null;
            }
    
            //遍历list集合,看看获取得到的数据有没有敏感词汇
            for (String s : list) {
    
                if (s.equals(value)) {
                    value = "*****";
                }
            }
    
            return value ;
        }
    }
开发过滤器
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        //将request和response强转成http协议的
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        HttpServletResponse httpServletResponse = (HttpServletResponse) resp;

        MyDirtyRequest dirtyRequest = new MyDirtyRequest(httpServletRequest);

        //传送给目标资源的是被增强后的request对象
        chain.doFilter(dirtyRequest, httpServletResponse);
    }
测试

压缩资源过滤器

按照过滤器的执行顺序:执行完目标资源,过滤器后面的代码还会执行。所以,我们在过滤器中可以获取执行完目标资源后的response对象!

我们知道sun公司提供的response对象调用write()方法,是直接把数据返回给浏览器的。我们要想实现压缩的功能,write()方法就不能直接把数据写到浏览器上!

这和上面是类似的,过滤器传递给目标资源的response对象就需要被我们增强,使得目标资源调用writer()方法的时候不把数据直接写到浏览器上

增强response对象

response对象可能会使用PrintWriter或者ServletOutputStream对象来调用writer()方法的,所以我们增强response对象的时候,需要把getOutputSteam和getWriter()重写

    
    class MyResponse extends HttpServletResponseWrapper{
    
        HttpServletResponse response;
        public MyResponse(HttpServletResponse response) {
            super(response);
            this.response = response;
        }
    
    
        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            return super.getOutputStream();
        }
    
        @Override
        public PrintWriter getWriter() throws IOException {
            return super.getWriter();
        }
    }

接下来,ServletOutputSteam要调用writer()方法,使得它不会把数据写到浏览器上。这又要我们增强一遍了!

增强ServletOutputSteam
    /*增强ServletOutputSteam,让writer方法不把数据直接返回给浏览器*/
    class MyServletOutputStream extends ServletOutputStream{
    
        private ByteArrayOutputStream byteArrayOutputStream;
    
        public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
            this.byteArrayOutputStream = byteArrayOutputStream;
        }
    
        //当调用write()方法的时候,其实是把数据写byteArrayOutputSteam上
        @Override
        public void write(int b) throws IOException {
            this.byteArrayOutputStream.write(b);
    
        }
    }

增强PrintWriter

PrintWriter对象就好办了,它本来就是一个包装类,看它的构造方法,我们直接可以把ByteArrayOutputSteam传递给PrintWriter上。

    @Override
    public PrintWriter getWriter() throws IOException {
        printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));

        return printWriter;
    }
获取缓存数据

我们把数据都写在了ByteArrayOutputSteam上了,应该提供方法给外界过去缓存中的数据!

    public byte[] getBuffer() {

        try {

            //防止数据在缓存中,要刷新一下!
            if (printWriter != null) {
                printWriter.close();
            }
            if (byteArrayOutputStream != null) {
                byteArrayOutputStream.flush();
                return byteArrayOutputStream.toByteArray();
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
增强response的完整代码
class MyResponse extends HttpServletResponseWrapper{

    private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

    private PrintWriter printWriter ;

    private HttpServletResponse response;
    public MyResponse(HttpServletResponse response) {
        super(response);
        this.response = response;
    }


    @Override
    public ServletOutputStream getOutputStream() throws IOException {

        //这个的ServletOutputSteam对象调用write()方法的时候,把数据是写在byteArrayOutputSteam上的
        return new MyServletOutputStream(byteArrayOutputStream);
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));

        return printWriter;
    }

    public byte[] getBuffer() {

        try {

            //防止数据在缓存中,要刷新一下!
            if (printWriter != null) {
                printWriter.close();
            }
            if (byteArrayOutputStream != null) {
                byteArrayOutputStream.flush();
                return byteArrayOutputStream.toByteArray();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
过滤器
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {


        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        MyResponse myResponse = new MyResponse(response);

        //把被增强的response对象传递进去,目标资源调用write()方法的时候就不会直接把数据写在浏览器上了
        chain.doFilter(request, myResponse);

        //得到目标资源想要返回给浏览器的数据
        byte[] bytes = myResponse.getBuffer();

        //输出原来的大小
        System.out.println("压缩前:"+bytes.length);

        
        //使用GZIP来压缩资源,再返回给浏览器
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
        gzipOutputStream.write(bytes);
        gzipOutputStream.flush();
        
        //得到压缩后的数据
        byte[] gzip = byteArrayOutputStream.toByteArray();
        
        System.out.println("压缩后:" + gzip.length);
        
        //还要设置头,告诉浏览器,这是压缩数据!
        response.setHeader("content-encoding", "gzip");
        response.setContentLength(gzip.length);
        response.getOutputStream().write(gzip);
     
    }
测试

在Servlet上输出一大段文字:

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.getWriter().write("fdshfidsuhfidusfhuidsfhuidshdsuifhsd" +
                "uifhsduifffffdshfidsuhfidusfhuidsfhuidshdsuif" +
                "hsduifhsduifffffdshfidsuhfidusfhuidsfhuidshd" +
                "suifhsduifhsduifffffdshfidsuhfidusfhuidsfhuidsh" +
                "dsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuids" +
                "hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuid" +
                "shdsuifhsduifhsduiffdshfidsuhfidusfhuidsfhuids" +
                "hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhui" +
                "dshdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfh" +
                "uidshdsuifhsduifhsduifffffdshfidsuhfidusfhuids" +
                "fhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhuid" +
                "sfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhui" +
                "dsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfh" +
                "uidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusf" +
                "huidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidus" +
                "fhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhfid" +
                "usfhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhf" +
                "idusfhuidsfhuidshdsuifhsduifhsd" +
                "uifffffdshfidsuhfidusfhuidsfhuidshdsuifhsduifhsduifffffff");

    }

效果:

HTML转义过滤器

只要把getParameter()获取得到的数据转义一遍,就可以完成功能了。

增强request

class MyHtmlRequest extends HttpServletRequestWrapper{

    private HttpServletRequest request;

    public MyHtmlRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }


    @Override
    public String getParameter(String name) {

        String value = this.request.getParameter(name);
        return this.Filter(value);
        
    }

    public String Filter(String message) {
        if (message == null)
            return (null);

        char content[] = new char[message.length()];
        message.getChars(0, message.length(), content, 0);
        StringBuffer result = new StringBuffer(content.length + 50);
        for (int i = 0; i < content.length; i++) {
            switch (content[i]) {
                case "<":
                    result.append("<");
                    break;
                case ">":
                    result.append(">");
                    break;
                case "&":
                    result.append("&");
                    break;
                case """:
                    result.append(""");
                    break;
                default:
                    result.append(content[i]);
            }
        }
        return (result.toString());
        
    }
    
}
过滤器



    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {



        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        MyHtmlRequest myHtmlRequest = new MyHtmlRequest(request);

        //传入的是被增强的request!
        chain.doFilter(myHtmlRequest, response);

    }
测试

jsp代码:

    

Servlet代码:

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String value = request.getParameter("username");
        response.getWriter().write(value);

    }

缓存数据到内存中

在前面我们已经做过了,让浏览器不缓存数据【验证码的图片是不应该缓存的】。

现在我们要做的是:缓存数据到内存中【如果某个资源重复使用,不轻易变化,应该缓存到内存中】

这个和压缩数据的Filter非常类似的,因为让数据不直接输出给浏览器,把数据用一个容器(ByteArrayOutputSteam)存起来。如果已经有缓存了,就取缓存的。没有缓存就执行目标资源!

增强response对象
class MyResponse extends HttpServletResponseWrapper {

    private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

    private PrintWriter printWriter ;

    private HttpServletResponse response;
    public MyResponse(HttpServletResponse response) {
        super(response);
        this.response = response;
    }


    @Override
    public ServletOutputStream getOutputStream() throws IOException {

        //这个的ServletOutputSteam对象调用write()方法的时候,把数据是写在byteArrayOutputSteam上的
        return new MyServletOutputStream(byteArrayOutputStream);
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));

        return printWriter;
    }

    public byte[] getBuffer() {

        try {

            //防止数据在缓存中,要刷新一下!
            if (printWriter != null) {
                printWriter.close();
            }
            if (byteArrayOutputStream != null) {
                byteArrayOutputStream.flush();
                return byteArrayOutputStream.toByteArray();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}


//增强ServletOutputSteam,让writer方法不把数据直接返回给浏览器

class MyServletOutputStream extends ServletOutputStream {

    private ByteArrayOutputStream byteArrayOutputStream;

    public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
        this.byteArrayOutputStream = byteArrayOutputStream;
    }

    //当调用write()方法的时候,其实是把数据写byteArrayOutputSteam上
    @Override
    public void write(int b) throws IOException {
        this.byteArrayOutputStream.write(b);

    }
}
过滤器

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        //定义一个Map集合,key为页面的地址,value为内存的缓存
        Map map = new HashMap<>();

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        //得到客户端想要请求的资源
        String uri = request.getRequestURI();
        byte[] bytes = map.get(uri);

        //如果有缓存,直接返回给浏览器就行了,就不用执行目标资源了
        if (bytes != null) {
            response.getOutputStream().write(bytes);
            return ;
        }

        //如果没有缓存,就让目标执行
        MyResponse myResponse = new MyResponse(response);
        chain.doFilter(request, myResponse);

        //得到目标资源想要发送给浏览器的数据
        byte[] b = myResponse.getBuffer();

        //把数据存到集合中
        map.put(uri, b);

        //把数据返回给浏览器
        response.getOutputStream().write(b);


    }
测试

尽管是刷新,获取得到的也是从缓存拿到的数据!

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章的同学,可以关注微信公众号:Java3y

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70938.html

相关文章

  • javascript 正则的一些使用场景——敏感过滤

    摘要:正则不是必须的,任何东西我们都可以不用正则用其他方式实现。正则中的转义寻找第一个出现的数字正则中的转义代表数字索引数字出现在索引的位置只能找到字母出现的索引位置。所以我们就需要写一个过滤标签的方法。点代表任意字符,在正则里劲量不要用。 写一个最简单的正则 // var re=new RegExp(a);//js风格 var re=/a/;//perl风格,古老语言现在没人用了 // v...

    kun_jian 评论0 收藏0
  • web安全三,XSS攻击

    摘要:我把这个领域的东西写成了一个系列,以后还会继续完善下去安全一同源策略与跨域安全二攻击安全三攻击 上文说完了CSRF攻击,本文继续研究它的兄弟XSS攻击。 什么是XSS攻击 XSS攻击全名(Cross-Site-Script)跨域脚本攻击,为了跟CSS(Cascading-Style-Sheet)区分开来,所以缩写是XSS。 XSS攻击的原理 上一节说道的CSRF攻击是利用的是伪请求,这...

    jlanglang 评论0 收藏0
  • web安全三,XSS攻击

    摘要:我把这个领域的东西写成了一个系列,以后还会继续完善下去安全一同源策略与跨域安全二攻击安全三攻击 上文说完了CSRF攻击,本文继续研究它的兄弟XSS攻击。 什么是XSS攻击 XSS攻击全名(Cross-Site-Script)跨域脚本攻击,为了跟CSS(Cascading-Style-Sheet)区分开来,所以缩写是XSS。 XSS攻击的原理 上一节说道的CSRF攻击是利用的是伪请求,这...

    JiaXinYi 评论0 收藏0
  • Java3y文章目录导航

    摘要:前言由于写的文章已经是有点多了,为了自己和大家的检索方便,于是我就做了这么一个博客导航。 前言 由于写的文章已经是有点多了,为了自己和大家的检索方便,于是我就做了这么一个博客导航。 由于更新比较频繁,因此隔一段时间才会更新目录导航哦~想要获取最新原创的技术文章欢迎关注我的公众号:Java3y Java3y文章目录导航 Java基础 泛型就这么简单 注解就这么简单 Druid数据库连接池...

    KevinYan 评论0 收藏0
  • Web安全开发规范手册V1.0

    摘要:一背景团队最近频繁遭受网络攻击,引起了技术负责人的重视,笔者在团队中相对来说更懂安全,因此花了点时间编辑了一份安全开发自检清单,觉得应该也有不少读者有需要,所以将其分享出来。 一、背景 团队最近频繁遭受网络攻击,引起了技术负责人的重视,笔者在团队中相对来说更懂安全,因此花了点时间编辑了一份安全开发自检清单,觉得应该也有不少读者有需要,所以将其分享出来。 二、编码安全 2.1 输入验证 ...

    Yuqi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<