Servlet中Filter过滤器工作原理和应用详解

1. Filter 过滤器原理

1.1 Filter 基本信息

Filter,过滤器接口
对客户端向服务器发送的请求进行过滤,用于在请求之前处理资源的组件。
Filter和Listener都属于Servlet中的高级部分,Filter是最为实用的技术

1.2 Filter 过滤器链

请求时,从客户端到服务端顺序处理;
响应时,从服务端到客户端顺序处理。

遵从原则:先过滤,后放行。

1.3 Filter 工作原理

在这里插入图片描述
执行流程:

  1. 浏览器发起请求
  2. 服务器会根据这个请求,创建 request 对象及 response 对象
  3. 过滤器会持有 request 对象及 response 对象
  4. 只有当过滤器放行之后,request 对象及 response 对象才会传给 Servlet

1.4 Filter 生命周期

  • 随着服务器的初始化而初始化
  • 随着服务器的关闭而销毁

1.5 Filter 基本使用

开发步骤:

  1. 自定义类实现 Filter 接口
  2. 重写 init() doFilter() destroy() 三个方法
  3. 在 web.xml 中配置过滤器:①声明过滤器 ②配置过滤器的过滤路径

创建一个过滤器:

public class Filter01 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter01 过滤器的初始化");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Filter01 放行之前");
        // 通过过滤器链操作:放行
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("Filter01 放行之后");
    }
    @Override
    public void destroy() {
        System.out.println("Filter01 过滤器的销毁");
    }
}

配置过滤器,使其在服务器中指定资源下生效:

<!--声明Filter01过滤器-->
<filter>
    <filter-name>Filter01</filter-name>
    <filter-class>com.demo.filter.Filter01</filter-class>
</filter>
<!--配置Filter01的过滤路径-->
<filter-mapping>
    <filter-name>Filter01</filter-name>
    <!--/* 代表所有资源都过滤-->
    <url-pattern>/*</url-pattern>
</filter-mapping>

1.6 Filter 配置方式 × 2

  • web.xml
    <url-pattern>标签中的匹配规则:
    • / 完全匹配
    • /开头,*结尾 目录匹配
    • *开头,后缀名结尾 后缀名匹配
<!--无限接近SpringMVC中文乱码解决方案-->
<filter>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>com.demo.filter.EncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
        </init-param>
</filter>
<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  • 注解配置
    @WebFilter注解
    WebInitParam[] initParams() default {}; //配置初始化参数
    String filterName() default “”; //配置过滤器名称
    String[] servletNames() default {}; //配置过滤的Servlet
    String[] urlPatterns() default {}; //配置过滤路径
@WebFilter(filterName = "Demo04Filter" ,
        urlPatterns = "/demo01",
        servletNames = "Demo01Servlet" ,
        initParams = {
                @WebInitParam(name = "username",value = "root"),
                @WebInitParam(name = "password",value = "root123")
        })
public class Demo04Filter implements Filter { }

1.7 Filter 中文乱码处理

  • 方式一:使用默认 .jsp 动态页面,中文乱码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆</title>
</head>
<body>
<form action="/demo/login" method="post">
    账户:<input type="text" name="username"/><br>
    密码:<input type="text" name="password"/><br>
    7天内自动登录:<input type="checkbox" name="autoLogin" value="logined"><br>
    <button type="submit">登录</button>
</form>
</body>
</html>
  • 方式二:使用过滤器对 request 中的服务器编码/客户端解码进行设置
    (服务器初始化时会从web.xml中读取出filter的配置信息,在任何请求过程中执行Servlet前,/* 即所有的项目资源均会先被该 Filter 过滤,设置编码方式为utf-8)
<!--web.xml-->
    <filter>
        <filter-name>EncodingFilter</filter-name>
        <filter-class>com.demo.filter.EncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>EncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
public class EncodingFilter implements Filter {
    private String encoding = null;

    @Override
    public void init(FilterConfig config) throws ServletException {
        encoding = config.getInitParameter("encoding");
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        // 处理响应中文乱码
        //resp.setContentType("text/html;charset=" + encoding);
        // 处理请求中文乱码
        req.setCharacterEncoding(encoding);
        // 放行
        chain.doFilter(req, resp);
    }

    @Override
    public void destroy() { }
}

2. Filter 过滤器应用

2.1 案例:Filter 过滤器实现自动登陆(流程图+核心实现)

  • 逻辑流程:
    在这里插入图片描述
  • 逻辑流程图梳理(单击放大更易查看):
    Filter过滤器应用:自动登陆实现

login.jsp

<form action="/demo/login" method="post">
    账户:<input type="text" name="username"/><br>
    密码:<input type="text" name="password"/><br>
    7天内自动登录:<input type="checkbox" name="autoLogin" value="logined"><br>
    <button type="submit">登录</button>
</form>

AutoLoginServlet.java

@WebServlet(name = "AutoLoginServlet", urlPatterns = "/login")
public class AutoLoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        if ("root".equals(username) && "1234".equals(password)) {
            String autoLogin = request.getParameter("autoLogin");
            System.out.println(autoLogin);
            if ("logined".equals(autoLogin)) {
                // 进行自动登陆,将用户信息保存起来,cookie最合适
                Cookie cookie = new Cookie("autoLogin", username + "-" + password);
                cookie.setMaxAge(7*24*60*60);
                response.addCookie(cookie);
            }
            // 登陆成功,转发到一个页面,显示用户信息
            Userinfo userinfo = new Userinfo();
            userinfo.setUsername(username);
            userinfo.setPassword(password);
            request.getSession().setAttribute("existUser", userinfo);
            request.getRequestDispatcher("/showIndex").forward(request, response);
        } else {
            // 登陆失败,转发到登陆页面
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

AutoLoginFilter.java

public class AutoLoginFilter implements Filter {
    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        // 获取请求路径
        String requestURI = request.getRequestURI();
        System.out.println("0 requestURI:" + requestURI);
        // 1.判断访问资源是否和登陆相关
        if (requestURI.contains("login")) {
            // 登陆相关资源,直接放行
            chain.doFilter(req, resp);
        } else {
            // 非登陆相关资源
            // 2.判断是否在登陆状态 Session
            Userinfo existUser = (Userinfo) request.getSession().getAttribute("existUser");
            System.out.println("1 existUser:" + existUser);
            if (null == existUser) {
                // 不在登陆状态,进行自动登陆
                // 获取 Cookie
                Cookie cookie = CookieUtils.getCookie(request.getCookies(), "autoLogin");
                // 判断 Cookie 是否为空,存在浏览器被清理掉缓存问题
                if (null == cookie) {
                    // 浏览器清理缓存,相当于自动登陆失败!跳转到登陆页面,进行手动登陆。
                    request.getRequestDispatcher("/login.jsp").forward(request, resp);
                } else {
                    // 缓存还存在,进行自动登陆
                    // 获取用户信息
                    String[] infos = cookie.getValue().split("-");
                    String username = infos[0];
                    String password = infos[1];
                    System.out.println("3 username="+username+",password="+password);
                    if ("root".equals(username) && "1234".equals(password)) {
                        // 自动登陆成功,修改登陆状态,
                        System.out.println("自动登陆成功");
                        existUser = new Userinfo();
                        existUser.setUsername(username);
                        existUser.setPassword(password);
                        request.getSession().setAttribute("existUser", existUser);
                        chain.doFilter(req, resp);
                    } else {
                        // 自动登陆失败(修改了密码)
                        System.out.println("自动登陆失败");
                        request.getRequestDispatcher("/login.jsp").forward(request, resp);
                    }
                }
            } else {
                // 在登陆状态,直接放行
                chain.doFilter(req, resp);
            }
        }
    }

    @Override
    public void init(FilterConfig config) throws ServletException {

    }
}

ShowIndexServlet.java

@WebServlet(name = "ShowIndexServlet", urlPatterns = "/showIndex")
public class ShowIndexServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Userinfo existUser = (Userinfo) request.getSession().getAttribute("existUser");
        StringBuffer respBody = new StringBuffer();
        if (null == existUser) {
            // 不在登陆状态
            respBody.append("您没有登陆, <a href='/demo/login.jsp'>请登陆</a>");
        } else {
            // 还在登陆状态
            respBody.append("欢迎回来,").append(existUser.getUsername());
        }
        response.getWriter().write(respBody.toString());
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

web.xml

    <filter>
        <filter-name>AutoLoginFilter</filter-name>
        <filter-class>com.demo.filter.AutoLoginFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AutoLoginFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

2.2 案例:Filter 过滤器实现敏感词过滤

// 正常情况最好将注解信息配置在 web.xml 中
@WebFilter(
        filterName = "SensitiveWordsFilter",
        urlPatterns = "/*",
        initParams = {
                @WebInitParam(name="word1", value="笨蛋"),
                @WebInitParam(name="word2", value="傻蛋"),
                @WebInitParam(name="word3", value="蠢蛋")
        }
)
public class SensitiveWordsFilter implements Filter {
    // 敏感词
    private List<String> sensitiveWords = new ArrayList<>();
    @Override
    public void init(FilterConfig config) throws ServletException {
        Enumeration<String> parameterNames = config.getInitParameterNames();
        while (parameterNames.hasMoreElements()) {
            String sensitiveWord = config.getInitParameter(parameterNames.nextElement());
            sensitiveWords.add(sensitiveWord);
        }
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        // 增强 request 的 getParameter 方法
        HttpServletRequest requestProxy = (HttpServletRequest) Proxy.newProxyInstance(
                request.getClass().getClassLoader(),
                request.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    Object returnValue = null;
                    String methodName = method.getName();
                    if ("getParameter".equals(methodName)) {
                        // returnValue 就是 getParameter 方法的返回值,需要处理敏感词
                        String word = (String) method.invoke(request, args);
                        // 处理敏感词
                        for (String sensitiveWord : sensitiveWords) {
                            if (word.contains(sensitiveWord)) {
                                // 处理敏感词
                                returnValue = word.replace(sensitiveWord, "***");
                            }
                        }
                    } else {
                        returnValue = method.invoke(request, args);
                    }
                    return returnValue;
                }
        );
        // 放行增强的请求对象 requestProxy(在 Servlet 中可以去使用到过滤后的)
        chain.doFilter(requestProxy, resp);
    }

    @Override
    public void destroy() { }
}
发布了327 篇原创文章 · 获赞 312 · 访问量 67万+

猜你喜欢

转载自blog.csdn.net/sinat_36184075/article/details/105694688
今日推荐