10 JavaWeb Filter过滤器
课时 1 过滤器的入门
JavaWeb 三大组件
1、都需要在 web.xml 中进行配置
- Servlet
- Filter
- Listener
2、过滤器
会在一组资源(jsp, servlet, css, html 等等)的前面执行
可以让请求得到目标资源,也可以不让请求达到
过滤器有拦截请求的能力
3、编写过滤器
(1)实现 Filter 接口
(2)在 web.xml 中进行配置
(3)Filter 是单例的
4、配置 web.xml
<web-app> <filter> <filter-name>FilerName</filter-name> <filter-class>FilerClass</filter-class> </filter> <filter-mapping> <filter-name>FilerName</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
继承示例
package com.pengshiyu.filtrer; import javax.servlet.*; import java.io.IOException; public class Afilter implements Filter { /** * 创建之后马上执行,用来做初始化 */ @Override public void init(FilterConfig filterConfig) throws ServletException { } /** * 每次过滤都会执行 */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("进入过滤器"); // 调用后序方法 filterChain.doFilter(servletRequest, servletResponse); System.out.println("离开过滤器"); } /** * 销毁之前的调用,用来释放资源 */ @Override public void destroy() { } }
FilterConfig -> 与 ServletConfig 相似
获取初始化参数 getInitParameter()
获取过滤器名称 getFilterName()
获取 application getServletContext()
FilterChain
放行,执行后序方法 doFilter()
课时 2 多个过滤器的执行顺序
执行下一个过滤器或目标资源
FilterChain.doFilter()
Afilter进入过滤器 Bfilter进入过滤器 getAge Bfilter离开过滤器 Afilter离开过滤器
课时 3 四种拦截方式
- 请求 REQUEST 默认
- 转发 FORWARD
- 包含 INCLUDE
- 错误 ERROR
<filter-mapping> <filter-name>FilerName</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping>
页面出错
<error-page> <error-code>500</error-code> <location>500.html</location> </error-page>
课时 4 使用 filter-mapping 控制多个 Filter 的执行顺序
filter-mapping 的配置顺序决定过滤器执行顺序
课时 5 Filter 的应用场景、Filter 的目标资源、小结
预处理:执行目标资源之前做预处理工作,例如设置编码
拦截:通过条件判断是否放行,例如用户登录校验
回程拦截:目标资源执行之后,做一些后序的特殊处理工作,例如目标资源输出的数据进行处理
直接指定 servlet-name
<filter-mapping> <filter-name>FilerName</filter-name> <servlet-name>ServletName</servlet-name> </filter-mapping>
小结
- Filter3 个方法
- FilterChain 类
- 4 种拦截方式
课时 6 案例 1:分 IP 统计访问次数
数据结构:
ip | count
-|-
192.168.0.1 | 32
192.168.0.2 | 22
统计工作在所有资源之前都执行,使用 Filter
这个过滤器只做统计,不做拦截
数据 Map
Map 保存到 ServletContext 中
从 request 中获取客户端 ip
使用监听器创建 map
AListener.java
package com.pengshiyu.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import java.util.LinkedHashMap; import java.util.Map; public class AListener implements ServletContextListener { // 服务器启动时创建map public void contextInitialized(ServletContextEvent sce) { Map<String, Integer> map = new LinkedHashMap<String, Integer>(); sce.getServletContext().setAttribute("map", map); } public void contextDestroyed(ServletContextEvent sce) { } }
使用过滤器统计数据
AFilter.java
package com.pengshiyu.filter; import javax.servlet.*; import java.io.IOException; import java.util.Map; public class AFilter implements Filter { private FilterConfig config; /** * 创建之后马上执行,用来做初始化 */ @Override public void init(FilterConfig filterConfig) throws ServletException { this.config = filterConfig; } /** * 每次过滤都会执行 */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { ServletContext app = this.config.getServletContext(); Map<String, Integer> map = (Map<String, Integer>)app.getAttribute("map"); String ip = request.getRemoteAddr(); System.out.println("ip: " + ip); if(map.containsKey(ip)){ Integer count = map.get(ip); map.put(ip, count+1); } else{ map.put(ip, 1); } // 放行 filterChain.doFilter(request, response); } /** * 销毁之前的调用,用来释放资源 */ @Override public void destroy() { } }
显示数据
BServlet.java
package com.pengshiyu.servlet; import javax.servlet.ServletContext; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Map; public class BServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { ServletContext app = getServletContext(); Map<String, Integer> map = (Map<String, Integer>) app.getAttribute("map"); response.setContentType("text/html; charset=UTF-8"); response.getWriter().println(map.toString()); } }
配置监听器和过滤器生效
web.xml
<?xml version="1.0" encoding="utf-8"?> <web-app> <servlet> <servlet-name>BServlet</servlet-name> <servlet-class>com.pengshiyu.servlet.BServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>BServlet</servlet-name> <url-pattern>/b</url-pattern> </servlet-mapping> <filter> <filter-name>AFilter</filter-name> <filter-class>com.pengshiyu.filter.AFilter</filter-class> </filter> <filter-mapping> <filter-name>AFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>com.pengshiyu.listener.AListener</listener-class> </listener> </web-app>
课时 7 案例 2:粗粒度权限管理
基于角色的权限控制 RBAC
- tb_user
- tb_role
- tb_userrole
- tb_menu
- tb_rolemenu
web.xml
<?xml version="1.0" encoding="utf-8"?> <web-app> <servlet> <servlet-name>AServlet</servlet-name> <servlet-class>com.pengshiyu.servlet.AServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>AServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <filter> <filter-name>AFilter</filter-name> <filter-class>com.pengshiyu.filter.AFilter</filter-class> </filter> <filter-mapping> <!-- 不能将过滤器设置在login.html上,不然没法登录了--> <filter-name>AFilter</filter-name> <url-pattern>/hello.html</url-pattern> </filter-mapping> </web-app>
AServlet.java
package com.pengshiyu.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class AServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); System.out.println("post: " + username); // 设置session request.getSession().setAttribute("username", username); // 跳转页面 request.getRequestDispatcher("hello.html").forward(request, response); } }
过滤器进行简单的权限校验
AFilter.java
package com.pengshiyu.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; public class AFilter implements Filter { private FilterConfig config; @Override public void init(FilterConfig filterConfig) throws ServletException { this.config = filterConfig; } @Override public void doFilter(ServletRequest req, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)req; String username = (String) request.getSession().getAttribute("username"); System.out.println("filter: " + username); if(username != null){ // 放行 filterChain.doFilter(request, response); } else{ // 跳转到登录页 request.getRequestDispatcher("login.html").forward(request, response); } } @Override public void destroy() { } }
课时 8 案例 3:全站编码问题
// post编码 request.setCharacterEncoding("utf-8"); // get编码 String username = request.getParameter("username"); username = new String(username.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); // 响应编码 response.setContentType("text/html; charset=UTF-8");
HttpServletRequest 装饰类
EncodingRequest.java
package com.pengshiyu.filter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.nio.charset.StandardCharsets; // 装饰器 public class EncodingRequest extends HttpServletRequestWrapper { public EncodingRequest(HttpServletRequest request) { super(request); } @Override public String getParameter(String name) { // 处理编码问题 String value = super.getParameter(name); value = new String(value.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); return value; } }
过滤器 AFilter.java
package com.pengshiyu.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; public class AFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; String method = httpServletRequest.getMethod(); // 设置响应编码 response.setContentType("text/html; charset=UTF-8"); if ("GET".equals(method)) { // 放行 EncodingRequest encodingRequest = new EncodingRequest(httpServletRequest); filterChain.doFilter(encodingRequest, response); } else if ("POST".equals(method)) { request.setCharacterEncoding("utf-8"); filterChain.doFilter(request, response); } } @Override public void destroy() { } }
响应处理 AServlet.java
package com.pengshiyu.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class AServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println(request.getParameter("name")); response.getWriter().print("你好"); } }
web.xml
<?xml version="1.0" encoding="utf-8"?> <web-app> <!-- 注册 Servlet,帮助web服务器反射该类 --> <servlet> <servlet-name>AServlet</servlet-name> <servlet-class>com.pengshiyu.servlet.AServlet</servlet-class> </servlet> <!-- 映射 Servlet 资源,用url-pattern元素标示 URL --> <servlet-mapping> <servlet-name>AServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <filter> <filter-name>AFilter</filter-name> <filter-class>com.pengshiyu.filter.AFilter</filter-class> </filter> <filter-mapping> <!-- 不能将过滤器设置在login.html上,不然没法登录了--> <filter-name>AFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
课时 9 案例 4:页面静态化之准备工作(图书管理小项目)
功能: 查询所有 按分类查看 BookServlet findAll() 查询全部 findByCategory() 按分类查询 BookService: 省略 BookDao: List<Book> findAll() List<Book> findByCategory() Book: bid bname price category
静态化:
第一次访问从数据库取数据,保存到 html 中
第二次之后访问就直接从 html 中读取,不再从数据库中取数据
数据准备:
create table tb_book( bid int primary key auto_increment, bname varchar(50), price decimal(10, 2), category int ); insert into tb_book(bname, price, category) values("Java", 12, 1); insert into tb_book(bname, price, category) values("Python", 12, 1); insert into tb_book(bname, price, category) values("JavaScript", 12, 1); insert into tb_book(bname, price, category) values("Go", 12, 1); insert into tb_book(bname, price, category) values("三国演义", 12, 2); insert into tb_book(bname, price, category) values("西游记", 12, 2); insert into tb_book(bname, price, category) values("水浒传", 12, 2); insert into tb_book(bname, price, category) values("红楼梦", 12, 2);
创建对应的 Book 类
package com.pengshiyu.bean; public class Book { private int bid; private String bname; private double price; private int category; public Book() { } public int getBid() { return bid; } public void setBid(int bid) { this.bid = bid; } public String getBname() { return bname; } public void setBname(String bname) { this.bname = bname; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getCategory() { return category; } public void setCategory(int category) { this.category = category; } @Override public String toString() { return "Book{" + "bid=" + bid + ", bname='" + bname + '\'' + ", price=" + price + ", category=" + category + '}'; } }
BookDao.java
package com.pengshiyu.dao; import com.pengshiyu.bean.Book; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanListHandler; import util.TxQueryRunner; import java.sql.SQLException; import java.util.List; public class BookDao { private QueryRunner qr = new TxQueryRunner(); public List<Book> findAll() { String sql = "select * from tb_book"; try { List<Book> list = qr.query(sql, new BeanListHandler<Book>(Book.class)); System.out.println(list); return list; } catch (SQLException e) { throw new RuntimeException(e); } } public List<Book> findByCategory(int category) { String sql = "select * from tb_book where category = ?"; try { return qr.query(sql, new BeanListHandler<Book>(Book.class), category); } catch (SQLException e) { throw new RuntimeException(e); } } }
BookServlet
package com.pengshiyu.servlet; import com.pengshiyu.dao.BookDao; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class BookServlet extends BaseServlet { private BookDao bookDao = new BookDao(); public void findAll(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { request.setAttribute("bookList", bookDao.findAll()); request.getRequestDispatcher("book.jsp").forward(request, response); } public void findByCategory(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int category = Integer.parseInt(request.getParameter("category")); request.setAttribute("bookList", bookDao.findByCategory(category)); request.getRequestDispatcher("book.jsp").forward(request, response); } }
用到的工具类 TxQueryRunner.java
package util; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; public class TxQueryRunner extends QueryRunner { @Override public int[] batch(String sql, Object[][] params) throws SQLException { Connection con = JdbcUtil.getConnection(); int[] result = super.batch(con, sql, params); JdbcUtil.releaseConnection(con); return result; } @Override public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException { Connection con = JdbcUtil.getConnection(); T result = super.query(con, sql, rsh, params); JdbcUtil.releaseConnection(con); return result; } @Override public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException { Connection con = JdbcUtil.getConnection(); T result = super.query(con, sql, rsh); JdbcUtil.releaseConnection(con); return result; } @Override public int update(String sql) throws SQLException { Connection con = JdbcUtil.getConnection(); int result = super.update(con, sql); JdbcUtil.releaseConnection(con); return result; } @Override public int update(String sql, Object param) throws SQLException { Connection con = JdbcUtil.getConnection(); int result = super.update(con, sql, param); JdbcUtil.releaseConnection(con); return result; } @Override public int update(String sql, Object... params) throws SQLException { Connection con = JdbcUtil.getConnection(); int result = super.update(con, sql, params); JdbcUtil.releaseConnection(con); return result; } }
JdbcUtil.java
package util; import com.mchange.v2.c3p0.ComboPooledDataSource; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; public class JdbcUtil { // 需要配置c3p0-config.xml private static ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 返回连接对象 public static Connection getConnection() { try { return dataSource.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } } // 返回连接池对象 public static DataSource getDataSource() { return dataSource; } // 释放连接 public static void releaseConnection(Connection connection) { try { connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } } }
book.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <h2>图书列表</h2> 分类: <a href="book?method=findAll">全部</a> <a href="book?method=findByCategory&category=1">第一类</a> <a href="book?method=findByCategory&category=2">第二类</a> <table border="1"> <tr> <th>ID</th> <th>书名</th> <th>价格</th> <th>分类</th> </tr> <c:forEach items="${bookList}" var="book"> <tr> <td>${book.bid}</td> <td>${book.bname}</td> <td>${book.price}</td> <td>${book.category}</td> </tr> </c:forEach> </table>
配置文件
pom.xml
<dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency>
web.xml
<servlet-mapping> <servlet-name>BookServlet</servlet-name> <url-pattern>/book</url-pattern> </servlet-mapping> <servlet> <servlet-name>BookServlet</servlet-name> <servlet-class>com.pengshiyu.servlet.BookServlet</servlet-class> </servlet>
c3p0-config.xml
<?xml version="1.0" encoding="utf-8"?> <c3p0-config> <!-- 这是默认配置信息 --> <default-config> <!-- 连接四大参数配置 --> <property name="driverClass">com.mysql.cj.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/data</property> <property name="user">root</property> <property name="password">123456</property> <!-- 池参数配置 --> <property name="acquireIncrement">2</property> <property name="initialPoolSize">2</property> <property name="minPoolSize">2</property> <property name="maxPoolSize">10</property> </default-config> </c3p0-config>
访问路径
http://localhost:8080/demo/book?method=findAll
http://localhost:8080/demo/book?method=findByCategory&category=1
课时 10 案例 4:页面静态化之如果文件存在直接重定向到 html
使用一个过滤器,把 servlet 请求的资源输出保存到 html 中
第二次访问资源的时候,如果已存在就直接重定向到 html 文件
课时 11 案例 5:页面静态之生成 html 页面
CacheFilter.java
package com.pengshiyu.filter; import com.pengshiyu.response.StaticResponse; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; public class CacheFilter implements Filter { private FilterConfig config; private final String cacheFileName = "cache"; private String cacheFilePath = null; @Override public void init(FilterConfig filterConfig) throws ServletException { this.config = filterConfig; this.cacheFilePath = this.config.getServletContext().getRealPath(this.cacheFileName); File file = new File(this.cacheFilePath); if(file.exists()){ file.mkdir(); } } /** * 访问路径 * http://localhost:8080/demo/book?method=findByCategory&category=4 */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)servletRequest; HttpServletResponse response = (HttpServletResponse)servletResponse; String category = request.getParameter("category"); String filepath = this.cacheFilePath + "/" + category + ".html"; File file = new File(filepath); // 如果页面不存在就缓存页面 if(!file.exists()){ StaticResponse staticResponse = new StaticResponse(response, filepath); filterChain.doFilter(request, staticResponse); } System.out.println("文件存在了"); request.getRequestDispatcher(this.cacheFileName + "/" + category + ".html").forward(request, response); } @Override public void destroy() { } }
StaticResponse.java
package com.pengshiyu.response; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; public class StaticResponse extends HttpServletResponseWrapper { private PrintWriter pw; public StaticResponse(HttpServletResponse response, String filename) throws FileNotFoundException { super(response); this.pw = new PrintWriter(filename); } @Override public PrintWriter getWriter() throws IOException { // 掉包输出流 return this.pw; } }