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.jsp
5.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); // 这个操作不太推荐.... 因为把所有响应类型给成了 html
chain.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方法,实现非法字段过滤
@Override
public 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 {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");// 处理post请求乱码问题
response.setContentType("text/html;charset=UTF-8"); // 响应参数乱码解决方法
//退出网站,销毁session
request.getSession().invalidate();
}
@Override
protected 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;
@WebListener
public class SessionIntListener implements ServletContextListener {
//服务器启动时执行,在ServletContext域中初始化用于存储Session的容器
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("sessionCreated被访问,存储session的容器初始化完成。。。");
//获取ServletContext域对象
ServletContext servletContext = servletContextEvent.getServletContext();
//在ServletContext域中初始化用于存储Session的容器
servletContext.setAttribute("sessionCount", 0);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
网站受到访问时统计在线人数:SessionCountListener
package com.yunhe.listener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class SessionCountListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("session被创建了。。。。。");
//创建session时,执行该方法、
//获取用于记录Session的计数器sessionCount
int sessionCount = (int) httpSessionEvent.getSession().getServletContext().getAttribute("sessionCount");
//Session发生变化,哎,来一个用户,Session的数量就加一
int num = sessionCount+1;
httpSessionEvent.getSession().getServletContext().setAttribute("sessionCount",num);
System.out.println("当前网站用户:"+num);
}
@Override
public 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 {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.接收请求参数 content
String content = request.getParameter("content");
// 2.将结果响应到 浏览器
response.getWriter().write(content);
}
@Override
protected 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>