欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Filter

程序员文章站 2022-07-15 10:44:26
...

Filter的作用

Filter可以在请求到达目标资源之前先对请求进行拦截过滤, 也可以在响应到达客户端之前先对响应进行拦截过滤.

Filter

Filter的生命周期

Filter的生命周期和Servlet的生命周期类似, 其主要生命周期阶段有四个: Filter对象的创建、Filter对象的初始化、Filter执行doFilter()方法、Filter对象的销毁. Filter的整个生命周期过程的执行, 均由Web服务器负责管理.

Filter

Filter运行原理

Web服务器为Filter创建了一个Map和一个数组, Map的key为请求URI, value为Filter实例对象的引用; 数组里存放着所有与请求相匹配的Filter. 当客户端请求到达Web容器(服务器端)时, 会先对请求进行解析, 使用解析出的URI作为比较对象, 从Map中查找是否存在相匹配的key, 若存在则读取其Value, 即Filter对象的引用, 将该引用存入到数组中. 然后继续向后查找, 直到将该Map查找完毕, 这样在数组中就会存在按照查找顺序排好序的Filter引用. 数组初始化完毕后, 开始按照数组元素顺序进行执行, 数组中的Filter全部执行完毕后, 再跳转到请求的目标资源.

Filter特征

(1)Filter是单例多线程的.

(2)Filter是在服务器启动时被创建和初始化的, 且创建和初始化只执行一次.

(3)Filter每过滤一次就执行一次doFilter(), 在服务器被停止时执行destroy().

(4)由于Filter是单例多线程的, 为了保证其线程安全性, 一般不为Filter添加可修改的成员变量.

(5)过滤器的优先级由注册顺序来定.

Filter

装饰者模式

装饰者(Decorator)模式又叫包裹(Wrapper)模式. 装饰者模式以对客户端透明的方式扩展对象的功能, 是继承关系的一个替代方案. 装饰模式常常被称作包裹模式, 就是因为每一个具体装饰类都会将下一个具体装饰类或者具体构建类包裹起来.

Filter

Filter

实例

目标类返回一个前后带空格的小写字符串, 要求把空格去掉, 并将字符串转换成大写.

public interface ISomeService {
    public String doSome();
}
/**
目标类
*/
public class SomeServiceImpl implements ISomeService{
    /**
     *  返回一个前后带空格的小写字符串
     */
    @Override
    public String doSome() {
        return " abcde ";
    }
}

/**
 * 装饰者基类
 */
public class SomeServiceWrapper implements ISomeService {
    private ISomeService target;

    public SomeServiceWrapper() {
    }

    public SomeServiceWrapper(ISomeService target) {
        this.target = target;
    }

    @Override
    public String doSome() {
        return target.doSome();
    }
}
/**
 * 具体装饰者类---去除前后空格
 */
public class TrimDecorator extends SomeServiceWrapper {
    public TrimDecorator() {
    }

    public TrimDecorator(ISomeService target) {
        super(target);
    }

    @Override
    public String doSome() {
        return super.doSome().trim();
    }
}
/**
 * 具体装饰者类---将字符串转为大写
 */
public class ToUpperCaseDecorator extends SomeServiceWrapper {
    public ToUpperCaseDecorator() {
    }

    public ToUpperCaseDecorator(ISomeService target) {
        super(target);
    }

    @Override
    public String doSome() {
        return super.doSome().toUpperCase();
    }
}

public class Test {
    public static void main(String[] args) {
        //创建目标对象
        ISomeService target = new SomeServiceImpl();

        //对目标对象进行增强
        ISomeService service1 = new TrimDecorator(target);
        //将第一次增强过的结果作为第二次增强的参数, 形成"装饰者链"
        ISomeService service2 = new ToUpperCaseDecorator(service1);

        System.out.println(service2.doSome());
    }
}
ABCDE

Filter应用

请求中文乱码问题解决方案

(1)为什么会产生中文乱码?

产生中文乱码的原因是浏览器的编码格式和服务器的编码格式不一致.

(2)GET请求方式产生乱码的原因

当用户提交了一个包含中文参数的请求时, 浏览器首先会将这些中文使用默认的编码格式转化为一个字节序列, 发送到Web服务器. 中文浏览器默认的编码格式一般是UTF-8或GBK. Web服务器接收到这个字节序列后, 会按照其默认的编码格式ISO-8859-1对其进行解码(Tomcat8之后对GET请求解码默认是UTF-8, 也就是说GET请求不再是乱码), 此时就产生了乱码. 最后, 服务器会将解码后的参数存放到parameterMap中, 此时parameterMap中的参数已经是乱码了.

(3)POST请求方式产生乱码的原因

GET请求方式在浏览器端不能设置编码格式, 只能使用浏览器默认的编码格式, 但POST请求方式在浏览器端和服务器端都是使用ContentType中的字符集进行编解码, 这个字符集我们可以通过request.setCharacterEncoding(charset)来设置。如果没有设置ContentType的字符集, 就会使用默认的编码格式, 产生乱码的原因和GET请求方式一样.

(4)使用Filter全局过滤中文乱码

/**
 * 全局中文过滤器
 */
public class GlobalFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void destroy() {
    }

    /**
     * 所有传到web服务器的数据, 不管是GET请求还是POST请求, 都用new String(username.getBytes(“ISO-8859-1”),“UTF-8”)进行处理.
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request = new MyRequest((HttpServletRequest) request);
        chain.doFilter(request, response);
    }
}

/**
 * HttpServletRequestWrapper为装饰者基类, MyRequest具体的装饰者类
 */
public class MyRequest extends HttpServletRequestWrapper {
    // 标记getParameterMap()方法是否调用过(不管调用多少次getParameter(String name), getParameterMap()只能被调用一次, 否则会出现乱码)
    private boolean flag = false;

    public MyRequest(HttpServletRequest request) {
        super(request);
    }

    /**
     *  需要用到哪个方法就重写该方法, 没被重写的方法还是乱码
     */
    @Override
    public String getParameter(String name) {
//        return super.getParameter(name);
        return getParameterMap().get(name)[0];
    }

    /**
     *  乱码处理
     */
    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> parameterMap = super.getParameterMap();
        if(!flag){
            for(Map.Entry<String, String[]> entry : parameterMap.entrySet()){
                String[] values = entry.getValue();
                for (int i = 0; i < values.length; i++) {
                    try {
                        values[i] = new String(values[i].getBytes("iso-8859-1"), "UTF-8");
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
            flag = true;
        }
        return parameterMap;
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

  <!--filter要放在servlet之前 -->
  <filter>
    <filter-name>GlobalFilter</filter-name>
    <filter-class>org.filters.GlobalFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>GlobalFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>SomeServlet</servlet-name>
    <servlet-class>org.filters.SomeServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>SomeServlet</servlet-name>
    <url-pattern>/someServlet</url-pattern>
  </servlet-mapping>

</web-app>

访问权限过滤器

只有登录的用户才能访问的资源. 如果是页面, 可以把页面放在/user目录下; 如果是Servlet, 可以在加上/user前缀. 在过滤器里获取ServletPath, 如果是以/user开头的请求就判断是否登录, 否则直接放行.

public class PermissionFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void destroy() {
    }

    /**
     * 权限过滤, 判断是否登录
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        String servletPath = req.getServletPath();
        if(servletPath.startsWith("/user")){
            //false, 表示获取session, 没有不创建新session, 直接返回null
            HttpSession session = req.getSession(false);
            if(session != null){
                String username = (String) session.getAttribute("username");
                if(username != null){
                    chain.doFilter(request, response);
                }else{
                    req.getRequestDispatcher("/login.jsp").forward(request, response);
                    return;
                }
            }
        }else{
            chain.doFilter(request,response);
        }
    }
}

<filter>
<filter-name>PermissionFilter</filter-name>
<filter-class>org.demo2.PermissionFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>PermissionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
相关标签: Filter