1、Filter过滤器
Servlet是控制器,Filter是过滤器,Listener是监听器,合为Web的三大组件
用于登录验证,统一编码处理,敏感字符过滤
过滤器类的编写:
@WebFilter("/index.jsp") //注解方式指定要过滤的资源public class FilterA implements Filter {public void destroy() {//过滤器销毁时执行}public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {System.out.println("FilterA执行了。。。");chain.doFilter(req, resp); //放行操作,System.out.println("FilterA响应了。。。");}public void init(FilterConfig config) throws ServletException {//服务器启动时执行}}
关于多个过滤器对同一个资源进行过滤:
1、先执行的后响应,后执行的先响应
2、注解方式是按照类名对应的ANSC码表大小顺序执行
xml编写:过滤器从上到下依次执行
<!--注册filter--><filter><filter-name>FilterA</filter-name><filter-class>com.yunhe.filter.FilterA</filter-class></filter><!--配置filter拦截路径--><filter-mapping><filter-name>FilterA</filter-name><url-pattern>/index.jsp</url-pattern></filter-mapping>
2、Filter过滤器细节
生命周期:
init方法:服务器启动时,加载项目,创建filter对象时执行,只执行一次
doFilter方法:用户范文被拦截目标资源时,可执行多次
destroy方法:服务器关闭前,Filter对象被销毁,如果服务器正常关闭,只执行一次
拦截路径:
精准匹配:指定目标的资源路径(如:”/index.jsp”)
目录匹配:指定目标的资源的目录路径(如:”user/“)
后缀匹配:指定目标的资源的后缀名称(如:”.html”)
匹配所有:指定该网站所有的资源(”/“)
拦截方式:
request:默认的拦截方式,浏览器直接发送请求forward:资源A转发到资源B时拦截,(如:ForwardServlet,index.jsp)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~xml方式:<filter><filter-name>ForwardServlet</filter-name><filter-class>com.yunhe.filter.ForwardServlet</filter-class></filter><filter-mapping><filter-name>ForwardServlet</filter-name><url-pattern>/index.jsp</url-pattern><dispatcher>REQUEST</dispatcher><dispatcher>FORWARD</dispatcher></filter-mapping>注解方式:@WebFilter(urlPatterns = "/index.jsp",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})加上拦截方式的话,就不能省略urlPatterns了
过滤器链:
过滤器链执行顺序 (先进后出)1.用户发送请求2.FilterA拦截,放行3.FilterB拦截,放行4.执行目标资源 index.jsp5.FilterB增强响应6.FilterA增强响应7.封装响应消息格式,返回到浏览器xml文件格式:谁先声明,谁先执行
3、Listener监听器
某一个被关注的对象,在对象发生变化时需要执行操作,什么时候变化需要监听器去关注该对象的变化情况,比如网站的访问次数,在线人数,系统启动时的初始化配置
监听器的内容:
监听作用域对象的创建和销毁:*SevletRequestListener:监听请求对象的创建和销毁*HttpSessionListener:监听会话对象(session)的创建和销毁*ServletContextListener:监听应用的创建和销毁监听作用域对象的属性和添加,删除,替换
监听器的web.xml配置:
<listener><listener-class>监听器全类名</listener-class></listener>
4、Filter&Listener案例
前端JSP代码:bbs.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>bbs</title></head><body>在线人数: ${sessionCount}<br><a href="/delSessionServlet">退出系统</a><h3>LPL季后赛观看留言板</h3><hr><form action="${pageContext.request.contextPath}/wordsServlet" method="post"><textarea name="content" id="" cols="206" rows="20" style="resize: none;"></textarea><br><br><input type="submit" value="请留言"></form></body></html>
后端编码过滤器代码:EncodeFilter
package com.yunhe.filter;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;//@WebFilter("/*")public class EncodeFilter implements Filter {//编码问题private String encode ;public void init(FilterConfig config) throws ServletException {encode = config.getInitParameter("encode");//服务器初始化时获取配置文件定义的编码类型}public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {HttpServletRequest request = (HttpServletRequest)req;HttpServletResponse response = (HttpServletResponse)resp;//如果是post请求,设置编码if (request.getMethod().equalsIgnoreCase("post")) {request.setCharacterEncoding(encode);}response.setContentType("text/html;charset="+encode); // 这个操作不太推荐.... 因为把所有响应类型给成了 htmlchain.doFilter(req, resp);//放行}public void destroy() {}}
后端敏感过滤器代码:WordsFilter
package com.yunhe.filter;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.*;import java.util.Arrays;import java.util.List;import java.util.ResourceBundle;@WebFilter("/wordsServlet") //只有执行该类后才执行该类public class WordsFilter implements Filter {//敏感字问题//存储敏感字库中取出来的数据private List<String> wordList;public void destroy() {}public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {// 向下转型HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) resp;/*//获取用户输入的数据String content = request.getParameter("content");//判断是否含有敏感字内容for (String word : wordList){ //遍历从敏感字库中取出的数据System.out.println("content:"+content+"--->"+"word:"+word);if (content.contains(word)){//判断content内容中是否含有word的内容response.getWriter().write("你输入的词汇敏感,拦截了。。。");return;}}chain.doFilter(req, resp);//放行*///判断是否含有敏感字内容,并替换MyRequest requestPro = new MyRequest(request, wordList);chain.doFilter(requestPro, response);//放行}public void init(FilterConfig config) throws ServletException {//服务器初始化就做的事// 1.读取words.properties文件中的内容//ResourceBundle方法可读取properties类型文件内的数据ResourceBundle words = ResourceBundle.getBundle("words");// 2.读取keyword关键字内容// String keyword = words.getString("keyword");String keyword = null;try {keyword = new String(words.getString("keyword").getBytes("ISO-8859-1"),"utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}// 3.split切割,转为list集合wordList = Arrays.asList(keyword.split(",")) ; //数组转list集合System.out.println("加载非法词库:"+wordList);}}
重写方法(增强该方法,使该方法具有过滤敏感字的功能):MyRequest 这里用到设计模式(装饰器模式)
装饰器模式:
1. 包装类和被包装类实现同一个接口(或者继承同一个抽象类)2. 包装类要有被包装类的对象引用3. 对需要增强的方法重写4. 对不需要增强的方法,执行原有的逻辑
package com.yunhe.filter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import java.util.List;public class MyRequest extends HttpServletRequestWrapper {// 非法词库private List<String> wordList;public MyRequest(HttpServletRequest request, List<String> wordList) {super(request);this.wordList= wordList;}//重写getParameter方法,实现非法字段过滤@Overridepublic String getParameter(String name) {//调用原有的方法获取用户输入的值String parameter = super.getParameter(name);//对非法字符过滤for (String word : wordList) {if (parameter.contains(word)){ //判断前端传过来的句子中是否含有word数据内容。//替换句子中的非法字符为***parameter = parameter.replaceAll(word,"***");}}return parameter;}}
退出网站监听器代码:delSessionServlet
package com.yunhe.listener;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/delSessionServlet")public class DelSessionServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.setCharacterEncoding("utf-8");// 处理post请求乱码问题response.setContentType("text/html;charset=UTF-8"); // 响应参数乱码解决方法//退出网站,销毁sessionrequest.getSession().invalidate();}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}}
服务器启动初始化参数:SessionIntListener
package com.yunhe.listener;import javax.servlet.ServletContext;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.annotation.WebListener;@WebListenerpublic class SessionIntListener implements ServletContextListener {//服务器启动时执行,在ServletContext域中初始化用于存储Session的容器@Overridepublic void contextInitialized(ServletContextEvent servletContextEvent) {System.out.println("sessionCreated被访问,存储session的容器初始化完成。。。");//获取ServletContext域对象ServletContext servletContext = servletContextEvent.getServletContext();//在ServletContext域中初始化用于存储Session的容器servletContext.setAttribute("sessionCount", 0);}@Overridepublic void contextDestroyed(ServletContextEvent servletContextEvent) {}}
网站受到访问时统计在线人数:SessionCountListener
package com.yunhe.listener;import javax.servlet.annotation.WebListener;import javax.servlet.http.HttpSessionEvent;import javax.servlet.http.HttpSessionListener;@WebListenerpublic class SessionCountListener implements HttpSessionListener {@Overridepublic void sessionCreated(HttpSessionEvent httpSessionEvent) {System.out.println("session被创建了。。。。。");//创建session时,执行该方法、//获取用于记录Session的计数器sessionCountint sessionCount = (int) httpSessionEvent.getSession().getServletContext().getAttribute("sessionCount");//Session发生变化,哎,来一个用户,Session的数量就加一int num = sessionCount+1;httpSessionEvent.getSession().getServletContext().setAttribute("sessionCount",num);System.out.println("当前网站用户:"+num);}@Overridepublic void sessionDestroyed(HttpSessionEvent httpSessionEvent) {System.out.println("session被销毁了。。。。。");//销毁session时,执行该方法int sessionCount = (int) httpSessionEvent.getSession().getServletContext().getAttribute("sessionCount");int num = sessionCount-1;httpSessionEvent.getSession().getServletContext().setAttribute("sessionCount",num);System.out.println("当前网站用户:"+num);}}
用户发表评论需要跳转的资源:WordsServlet
package com.yunhe.servlet;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/wordsServlet")public class WordsServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1.接收请求参数 contentString content = request.getParameter("content");// 2.将结果响应到 浏览器response.getWriter().write(content);}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}}
储存敏感字的配置文件:words.properties
keyword = 敏感字1, 敏感字2, 敏感字3, ...
web.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"version="3.1"><!--注册过滤器EncodeFilter--><filter><filter-name>EncodeFilter</filter-name><filter-class>com.yunhe.filter.EncodeFilter</filter-class><init-param><!--用于动态获取编码类型--><param-name>encode</param-name><param-value>utf-8</param-value></init-param></filter><!--EncodeFilter的映射--><filter-mapping><filter-name>EncodeFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>
