Filter 过滤器
Filter 过滤器说明
为啥要过滤器-需求示意图
过滤器介绍
1. Filter 过滤器它是 JavaWeb 的三大组件之一(Servlet 程序、Listener 监听器、Filter 过滤器)
2. Filter 过滤器是 JavaEE 的规范,是接口
3. Filter 过滤器它的作用是:拦截请求,过滤响应。
4. 应用场景
● 权限检查
● 日记操作
● 事务管理
Filter 过滤器基本原理
代码示例
需求: 在 web 工程下,有后台管理目录 manage,要求该目录下所有资源(html、图片、jsp 、Servlet 等)用户登录后才能访问
login.jsp
1. <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2. <html> 3. <head> 4. <title>管理后台登录</title> 5. </head> 6. <body> 7. <h1>管理后台登录</h1> 8. <% 9. System.out.println("request=" + request); 10. %> 11. <form action="<%=request.getContextPath() %>/loginCheckServlet" method="post"> 12. u:<input type="text" name="username"/> <br/><br/> 13. p:<input type="password" name="password"/> <br/><br/> 14. <input type="submit" value="用户登录"/></form> 15. </body> 16. </html>
LoginCLServlet.java
1. public class LoginCheckServlet extends HttpServlet { 2. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 3. //获取到用户名和密码->DB 4. //假设密码是123456, 就可以通过 5. String username = request.getParameter("username"); 6. String password = request.getParameter("password"); 7. System.out.println("request=" + request); 8. if("123456".equals(password)) { 9. 10. //合法, 讲用户名,加入session 11. request.getSession().setAttribute("username", username); 12. //请求转发到admin.jsp 13. request.getRequestDispatcher("/manage/admin.jsp") 14. .forward(request,response); 15. } else { 16. //不合法, 返回登录页面 17. request.getRequestDispatcher("/login.jsp") 18. .forward(request,response); 19. 20. } 21. } 22. 23. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 24. doPost(request, response); 25. } 26. }
admin.jsp
1. <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2. <html> 3. <head> 4. <title>后台管理</title> 5. <base href="<%=request.getContextPath() %>/manage/"/> 6. </head> 7. <body> 8. <h1>后台管理</h1> 9. <% 10. //验证request对象是和前面的filter是一个对象 11. System.out.println("request=" + request); 12. %> 13. <a href="#">用户列表</a>||<a href="#">添加用户</a>||<a href="#">删除用户</a> 14. <hr/> 15. <img src="shunping.jpg" height="300"/> 16. </body> 17. </html>
ManageFilter.java
1. filter在web项目启动时, 由tomcat 来创建filter实例, 只会创建一个
2. 会调用filter默认的无参构造器, 同时会调用 init方法, 只会调用一次
3. 在创建filter实例时,同时会创建一个FilterConfig对象,并通过init方法传入
4. 通过FilterConfig对象,程序员可以获取该filter的相关配置信息
5. 当一个http请求和该filter的的url-patter匹配时,就会调用doFilter方法
6. 在调用doFilter方法时,tomcat会同时创建ServletRequest 和ServletResponse 和 FilterChain对象, 并通过doFilter传入.
7. 如果后面的请求目标资源(jsp,servlet..) 会使用到request,和 response,那么会继续传递
1. /** 2. * 1. filter在web项目启动时, 由tomcat 来创建filter实例, 只会创建一个 3. * 2. 会调用filter默认的无参构造器, 同时会调用 init方法, 只会调用一次 4. * 3. 在创建filter实例时,同时会创建一个FilterConfig对象,并通过init方法传入 5. * 4. 通过FilterConfig对象,程序员可以获取该filter的相关配置信息 6. * 5. 当一个http请求和该filter的的url-patter匹配时,就会调用doFilter方法 7. * 6. 在调用doFilter方法时,tomcat会同时创建ServletRequest 和 ServletResponse 和 FilterChain对象 8. * , 并通过doFilter传入. 9. * 7. 如果后面的请求目标资源(jsp,servlet..) 会使用到request,和 response,那么会继续传递 10. */ 11. public class ManageFilter implements Filter { 12. 13. private int count = 0; 14. 15. @Override 16. public void init(FilterConfig filterConfig) throws ServletException { 17. //当Tomcat 创建 Filter创建,就会调用该方法,进行初始化 18. //提醒:回忆我们自己实现tomcat底层机制+servlet程序 19. 20. System.out.println("ManageFilter init被调用..."); 21. } 22. 23. @Override 24. public void doFilter(ServletRequest servletRequest, 25. ServletResponse servletResponse, 26. FilterChain filterChain) throws IOException, ServletException { 27. 28. System.out.println("ManageFilter doFilter() 被调用=" + (++count)); 29. 30. //到每次调用该filter时,doFilter就会被调用 31. 32. //如果这里,没有调用继续请求的方法,则就停止 33. //如果继续访问目标资源-> 等价于放行 34. 35. //说明:在调用过滤器前,servletRequest对象=request已经被创建并封装 36. //所以:我们这里就可以通过servletRequest获取很多信息, 比如访问url , session 37. //比如访问的参数 ... 就可以做事务管理,数据获取,日志管理等 38. //获取到session 39. //可以继续使用 httpServletRequest 方法. 40. HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; 41. System.out.println("输入密码=" + httpServletRequest.getParameter("password")); 42. HttpSession session = httpServletRequest.getSession(); 43. //获取username session对象, 还可以继续使用 44. Object username = session.getAttribute("username"); 45. if (username != null) { 46. //filterChain.doFilter(servletRequest, servletResponse) 47. //1. 继续访问目标资源url 48. //2. servletRequest 和 servletResponse 对象会传递给目标资源/文件 49. //3. 一定要理解filter传递的两个对象,再后面的servlet/jsp 是同一个对象(指的是在一次http请求) 50. System.out.println("servletRequest=" + servletRequest); 51. System.out.println("日志信息=="); 52. System.out.println("访问的用户名=" + username.toString()); 53. System.out.println("访问的url=" + httpServletRequest.getRequestURL()); 54. System.out.println("访问的IP=" + httpServletRequest.getRemoteAddr()); 55. filterChain.doFilter(servletRequest, servletResponse); 56. } else {//说明没有登录过..回到登录页面 57. servletRequest.getRequestDispatcher("/login.jsp"). 58. forward(servletRequest, servletResponse); 59. } 60. } 61. 62. @Override 63. public void destroy() { 64. //当filter被销毁时,会调用该方法 65. System.out.println("ManageFilter destroy()被调用.."); 66. } 67. }
xml配置
filter一般写在其它servlet的前面
- 1. 观察我们发现filter 配置和 servlet 非常相似. filter也是被tomcat管理和维护
- 2. url-pattern 就是当请求的url 和 匹配的时候,就会调用该filter
- 3. /manage/* 第一个 / 解析成 http://ip:port/工程路径
- 4. 完整的路径就是 http://ip:port/工程路径/manage/* 当请求的资源url满足该条件时都会调用filter , /manage/admin.jsp
1. <!--解读:filter一般写在其它servlet的前面 2. 1. 观察我们发现filter 配置和 servlet 非常相似. filter也是被tomcat管理和维护 3. 2. url-pattern 就是当请求的url 和 匹配的时候,就会调用该filter 4. 3. /manage/* 第一个 / 解析成 http://ip:port/工程路径 5. 4. 完整的路径就是 http://ip:port/工程路径/manage/* 当请求的资源url满足该条件时 6. 都会调用filter , /manage/admin.jsp 7. --> 8. <filter> 9. <filter-name>ManageFilter</filter-name> 10. <filter-class>com.filter.ManageFilter</filter-class> 11. </filter> 12. <filter-mapping> 13. <filter-name>ManageFilter</filter-name> 14. <url-pattern>/manage/*</url-pattern> 15. </filter-mapping>
Filter 过滤器 url-pattern
1、url-pattern: Filter的拦截路径,即浏览器在请求什么位置的资源时,过滤器会进行拦截过滤
2.、精确匹配 <url-pattern>/a.jsp</url-pattern> 对应的 请求地址 http://ip[域名]:port/工程路径/a.jsp 会拦截
3、目录匹配 <url-pattern>/manage/*</url-pattern>对应的 请求地址 http://ip[域名]:port/工程路径/manage/xx , 即 web 工程 manage 目录下所有资源 会拦截
4、后缀名匹配 <url-pattern>*.jsp</url-pattern> 后缀名可变,比如 *.action *.do 等等对应的 请求地址 http://ip[域名]:port/工程路径/xx.jsp , 后缀名为 .jsp 请求会拦截
5、Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在
Filter 过滤器生命周期
● Filter 生命周期图
1. filter在web项目启动时, 由tomcat 来创建filter实例, 只会创建一个
2. 会调用filter默认的无参构造器, 同时会调用 init方法, 只会调用一次
3. 在创建filter实例时,同时会创建一个FilterConfig对象,并通过init方法传入
4. 通过FilterConfig对象,程序员可以获取该filter的相关配置信息
5. 当一个http请求和该filter的的url-patter匹配时,就会调用doFilter方法
6. 在调用doFilter方法时,tomcat会同时创建ServletRequest 和 ServletResponse 和 FilterChain对象, 并通过doFilter传入.
7. 如果后面的请求目标资源(jsp,servlet..) 会使用到request,和 response,那么会继续传递
FilterConfig
● FilterConfig 接口
● FilterConfig 说明
1. FilterConfig 是 Filter 过滤器的配置类
2. Tomcat 每次创建 Filter 的时候,也会创建一个 FilterConfig 对象,这里包含了 Filter 配置文件的配置信息。
3. FilterConfig 对象作用是获取 filter 过滤器的配置内容
应用实例
WyxFilterConfig
1. public class WyxFilterConfig implements Filter { 2. 3. private String ip; //从配置获取的ip 4. 5. @Override 6. public void init(FilterConfig filterConfig) throws ServletException { 7. System.out.println("WyxFilterConfig init() 被调用.."); 8. 9. //通过filterConfig 获取相关的参数 10. String filterName = filterConfig.getFilterName(); 11. ip = filterConfig.getInitParameter("ip"); 12. 13. ServletContext servletContext = filterConfig.getServletContext(); 14. 15. //可以获取到该filter所有的配置参数名 16. Enumeration<String> initParameterNames = 17. filterConfig.getInitParameterNames(); 18. 19. //遍历枚举 20. while (initParameterNames.hasMoreElements()) { 21. System.out.println("名字=" + initParameterNames.nextElement()); 22. } 23. 24. System.out.println("filterName= " + filterName); 25. System.out.println("ip= " + ip); 26. System.out.println("servletContext= " + servletContext); 27. 28. 29. } 30. 31. @Override 32. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 33. 34. //通过forbidden ip 来进行控制 35. //先获取到访问ip 36. 37. String remoteAddr = servletRequest.getRemoteAddr(); 38. if(remoteAddr.contains(ip)) { 39. 40. System.out.println("封杀该网段.."); 41. servletRequest.getRequestDispatcher("/login.jsp"). 42. forward(servletRequest,servletResponse); 43. return; //直接返回 44. 45. } 46. 47. //继续访问目标资源 48. filterChain.doFilter(servletRequest,servletResponse); 49. } 50. 51. @Override 52. public void destroy() { 53. 54. } 55. }
web.xml
1. <filter> 2. <filter-name>WyxFilterConfig</filter-name> 3. <filter-class>com.filter.WyxFilterConfig</filter-class> 4. <!--这里就是给该filter配置的参数-根据业务逻辑来设置--> 5. <init-param> 6. <param-name>ip</param-name> 7. <param-value>127.0</param-value> 8. </init-param> 9. <init-param> 10. <param-name>port</param-name> 11. <param-value>8888</param-value> 12. </init-param> 13. <init-param> 14. <param-name>email</param-name> 15. <param-value>123@qq.com</param-value> 16. </init-param> 17. </filter> 18. <filter-mapping> 19. <filter-name>WyxFilterConfig</filter-name> 20. <url-pattern>/abc/*</url-pattern> 21. </filter-mapping>
FilterChain 过滤器链
一句话: FilterChain: 在处理某些复杂业务时,一个过滤器不够,可以设计多个过滤器共同完成过滤任务,形成过滤器链。
基本原理示意图
应用实例
需求: 演示过滤器链的使用
AFilter.java
1. public class AFilter implements Filter { 2. @Override 3. public void init(FilterConfig filterConfig) throws ServletException { 4. 5. } 6. 7. @Override 8. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 9. 10. System.out.println("AFilter---> 线程id=" + 11. Thread.currentThread().getId()); 12. 13. System.out.println("AFilter doFilter 的前置代码..."); 14. System.out.println("执行 AFilter doFilter()"); 15. filterChain.doFilter(servletRequest, servletResponse); 16. System.out.println("AFilter doFilter 的后置代码..."); 17. } 18. 19. @Override 20. public void destroy() { 21. 22. } 23. }
BFilter.java
1. public class BFilter implements Filter { 2. @Override 3. public void init(FilterConfig filterConfig) throws ServletException { 4. 5. } 6. 7. @Override 8. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 9. 10. System.out.println("BFilter---> 线程id=" + 11. Thread.currentThread().getId()); 12. 13. System.out.println("BFilter doFilter 的前置代码..."); 14. 15. System.out.println("执行 BFilter doFilter()"); 16. filterChain.doFilter(servletRequest, servletResponse); 17. System.out.println("BFilter doFilter 的后置代码..."); 18. } 19. 20. @Override 21. public void destroy() { 22. 23. } 24. }
hi.jsp
1. <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2. <html> 3. <head> 4. <title>hi</title> 5. </head> 6. <body> 7. <h1>admin目录下的 hi.jsp</h1> 8. <h1>后台管理</h1> 9. <a href="#">用户列表</a>||<a href="#">添加用户</a>||<a href="#">删除用户</a> 10. <hr/> 11. </body> 12. </html>
xml
1. <filter> 2. <filter-name>AFilter</filter-name> 3. <filter-class>com.filter.AFilter</filter-class> 4. </filter> 5. <filter-mapping> 6. <filter-name>AFilter</filter-name> 7. <url-pattern>/admin/*</url-pattern> 8. </filter-mapping> 9. 10. <filter> 11. <filter-name>BFilter</filter-name> 12. <filter-class>com.filter.BFilter</filter-class> 13. </filter> 14. <filter-mapping> 15. <filter-name>BFilter</filter-name> 16. <url-pattern>/admin/*</url-pattern> 17. </filter-mapping>
FilterChain 注意事项和细节
1. 多个 filter 和目标资源在一次 http 请求,在同一个线程中
2. 当一个请求 url 和 filter 的 url-pattern 匹配时, 才会被执行, 如果有多个匹配上,就会顺序执行,形成一个 filter 调用链 (底层可以使用一个数据结构搞定)如LinkedHashMap
3. 多个 filter 共同执行时,因为是一次 http 请求, 使用同一个 request 对象
4. 多个 filter 执行顺序,和 web.xml 配置顺序保持一致.
5. chain.doFilter(req, resp)方法 将执行下一个过滤器的 doFilter 方法, 如果后面没有过滤器,则执行目标资源。
6. 小结:
注意执行过滤器链时, 顺序是(用前面的案例分析) Http请求 -> A 过滤器 dofilter()-> A 过滤器前置代码 -> A 过滤器 chain.doFilter() -> B 过滤器 dofilter() -> B 过滤器前置代码 -> B过滤器 chain.doFilter() -> 目标文件 -> B过滤器后置代码 -> A过滤器后置代码 ->返回给浏览器页面/数据
综合代码示例
● 需求分析: 使用过滤器, 完成如下要求
1) 点击发表评论页面 topic.jsp, 可以在 showTopic.jsp 显示评论内容
2) 如果发表的评论内容,有关键字比如 "苹果" "香蕉", 就返回 topic.jsp, 并提示有禁用词
3) 要求发表评论到 showTopic.jsp 时,经过过滤器的处理
4) 禁用词, 配置在过滤器, 在启动项目时动态的获取, 注意处理中文
运行效果图
topic.jsp
1. <%@ page contentType="text/html;charset=UTF-8" language="java"%> 2. <html> 3. <head> 4. <title>Title</title> 5. </head> 6. <body> 7. <h1>发表对阿凡达电影评论</h1> 8. 过滤词: 苹果, 香蕉 ${errorInfo} 9. <form method="post" action="<%=request.getContextPath()%>/topic/showTopic.jsp"> 10. 用户: <input type="text" name="username"><br/> 11. 评论: <textarea rows="10" name="content" cols="20"></textarea><br/> 12. <input type="submit" value="发表评论"> 13. </form> 14. </body> 15. </html>
TopicServlet.java
1. public class TopicFilter implements Filter { 2. 3. //属性-> 存放禁用词 4. private String[] forbiddenWords = null; 5. 6. @Override 7. public void init(FilterConfig filterConfig) throws ServletException { 8. //获取禁用词 9. String forbiddenword = filterConfig.getInitParameter("forbiddenword"); 10. forbiddenWords = forbiddenword.split(","); 11. } 12. 13. @Override 14. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 15. 16. //解决从topic.jsp 提交的中文乱码问题 17. servletRequest.setCharacterEncoding("utf-8"); 18. 19. //判断评论是不是有禁用词 20. String content = servletRequest.getParameter("content"); 21. //循环遍历一把,看看有没有禁用词 22. for (String forbiddenWord : forbiddenWords) {//java基础 23. if (content.contains(forbiddenWord)) { 24. //返回topic.jsp 25. servletRequest.setAttribute("errorInfo", "你输入的有禁用词"); 26. servletRequest.getRequestDispatcher("/topic.jsp") 27. .forward(servletRequest, servletResponse); 28. return;//返回 29. } 30. } 31. 32. //继续到目标 33. filterChain.doFilter(servletRequest, servletResponse); 34. } 35. 36. @Override 37. public void destroy() { 38. 39. } 40. }
配置过滤器
1. <filter> 2. <filter-name>TopicFilter</filter-name> 3. <filter-class>com.filter.TopicFilter</filter-class> 4. <!--配置禁用词--> 5. <init-param> 6. <param-name>forbiddenword</param-name> 7. <param-value>苹果,香蕉</param-value> 8. </init-param> 9. </filter> 10. <filter-mapping> 11. <filter-name>TopicFilter</filter-name> 12. <url-pattern>/topic/*</url-pattern> 13. </filter-mapping>
showTopic.jsp
1. <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2. <html> 3. <head> 4. <title>Title</title> 5. </head> 6. <body> 7. <h1>你发表的评论是</h1> 8. 评论内容: <%=request.getParameter("content")%> 9. </body> 10. </html> 11.