项目第7天—Filter&Listener

项目中遇到的问题?

现在项目如果不登录,其实也可以访问页面资源,这样是不对的。

一、Web项目三大组件

Servlet , Filter , Listener HTML开发的三剑客 —(HTML,CSS,JS)

二、Filter—过滤器

1、概念

生活中的过滤器:空气净化器、净水器
项目第7天 - 图1

2、案例

服务器中的资源—》 图片,css,js,音频视频,html,jsp 等都是资源
第一步:编写一个Filter 通过实现Filter接口

  1. package com.qfedu.filter;
  2. import javax.servlet.*;
  3. import java.io.IOException;
  4. public class Demo01Filter implements Filter {
  5. // init 方法 ==-》 Filter创建的时候调用
  6. @Override
  7. public void init(FilterConfig filterConfig) throws ServletException {
  8. System.out.println("我被创建了....");
  9. }
  10. // 当程序被拦截器拦截之后,会执行doFilter里面的代码
  11. @Override
  12. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  13. System.out.println("这是我的第一个过滤器");
  14. // 获取到资源之后,根据条件选择是否放行
  15. filterChain.doFilter(servletRequest,servletResponse);
  16. }
  17. // filter即将回收的时候调用
  18. @Override
  19. public void destroy() {
  20. System.out.println("我即将被回收");
  21. }
  22. }

这个里面有三个方法:
init —> 该Filter被创建的时候调用,只调用一次
destory—> 该Filter对象被销毁的时候调用,也只调用一次
doFilter —> 核心方法,过滤器没拦截一次请求,就执行一次方法。
不管是做什么操作,记得放行:
filterChain.doFilter(servletRequest,servletResponse);

第二步: 配置Filter
1) 在web.xml中编写xml

  1. <filter>
  2. <filter-name>filter01</filter-name>
  3. <filter-class>com.qfedu.filter.Demo01Filter</filter-class>
  4. </filter>
  5. <filter-mapping>
  6. <filter-name>filter01</filter-name>
  7. <url-pattern>/*</url-pattern>
  8. </filter-mapping>

2、注解的方案: @WebFilter(“/*”)

执行顺序:
浏览器 —> 过滤器去拦截 —> 获取资源 —> 又到 过滤器这里—> 过滤器放行—>浏览器展示资源
问题:如果有两个 过滤器,都满足拦截条件,先执行哪个,后执行哪个?
1、如果你是注解配置的方式编写的过滤器:执行顺序按照类名进行比较
AFilter BFilter A先执行,B后执行
A10Filter A2Filter 先执行A10Filter ,后执行A2Filter.
2、web.xml方式编写的配置
定义在最前面的,先执行,定义在后面的后执行
一个项目中,要么都使用xml,要么都使用注解,所以注解和xml混着写的方式一般不存在。

拦截的规则有如下几种写法:
1、拦截具体的路径 /index.jsp 只拦截index.jsp
2、拦截目录: /customer/ 只拦截以customer开头的二级访问路径
3、拦截以某种后缀名结尾的请求
.do .action .jsp
4、拦截所有请求 /*

在过滤器中,还可以设置拦截被访问的方式 DispatcherType 【了解】
DispatcherType
REQUEST: 默认值,拦截浏览器直接请求的资源,类似于重定向
FORWARD: 内部转发 只拦截内部转发的资源
INCLUDE: 一个资源被包含在另一个资源中,发出的请求
ERROR: 错误跳转的资源
* ASYNC: 异步访问的资源_

3、实战

通过过滤器,完善只有登录才能操作系统中的资源

  1. package com.qfedu.filter;
  2. import javax.servlet.*;
  3. import javax.servlet.annotation.WebFilter;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import javax.servlet.http.HttpSession;
  7. import java.io.IOException;
  8. @WebFilter({"/customer/*"})
  9. public class LoginFilter implements Filter {
  10. @Override
  11. public void init(FilterConfig filterConfig) throws ServletException {
  12. }
  13. // ServletRequest 是 HttpServletRequest的父类
  14. @Override
  15. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  16. System.out.println("进入到登录过滤器");
  17. HttpServletRequest req = ((HttpServletRequest) servletRequest);
  18. // 先判断该用户有没有登录,如果没有登录,直接跳转到登录页面
  19. HttpSession session =req.getSession();
  20. Object obj = session.getAttribute("USERNAME");
  21. if(obj!=null){
  22. // 如果判断登录过了,直接放行
  23. filterChain.doFilter(servletRequest,servletResponse);
  24. }else{
  25. // 没有登录过,跳转到登录页面
  26. ((HttpServletResponse)servletResponse).sendRedirect(req.getContextPath()+"/login.jsp");
  27. }
  28. }
  29. @Override
  30. public void destroy() {
  31. }
  32. }

字符集的一个设置:
我们有很多页面,都需要设定字符集,防止中文乱码 编写一个字符集过滤器,为指定的servlet设置字符集,以后就不用我们设置了。

三、Listener—> 监听器

类似于窃听器,按钮上加事件等此类性质的情况。
Java Web监听器很多中,最常用的,以后能用的上的,就这一种:ServletContextListener
监听ServetContext的诞生的。
如果ServletContext对象被创建了,就会触发ServletContextListener中的代码。
ServletContext对象有什么用,它创建不创建跟我的代码有什么关系?
ServletContext对象是在tomcat已启动就被创建的,被tomcat所创建。
换句话说:当一个项目已启动就会触发ServletContextListerner.
我们可以将项目启动时需要加载的数据或者资源放入到该ServletContextListener中。
static {

}
static 静态代码块是一加载某个类,就去执行某些代码。
项目中的真实案例:
群发短信功能—-> 每一个手机号码都需要进行一个红名单的校验
红名单:此类手机号码千万不要给它发垃圾短信。
红名单的数据在你们公司的数据库中,大约有100多万条。
发短信要校验红名单。
项目一启动,就开始查询红名单数据,将数据存放在内存中,将来拿到一个手机号去校验是否是红名单数据就非常快。

代码演示:

  1. package com.qfedu.listener;
  2. import javax.servlet.ServletContextEvent;
  3. import javax.servlet.ServletContextListener;
  4. import javax.servlet.annotation.WebListener;
  5. @WebListener
  6. public class ContextLoaderListener implements ServletContextListener {
  7. //监听ServletContext被创建就执行如下代码
  8. @Override
  9. public void contextInitialized(ServletContextEvent servletContextEvent) {
  10. System.out.println("servletContext被创建了");
  11. }
  12. //监听ServletContext被销毁,触发如下代码
  13. @Override
  14. public void contextDestroyed(ServletContextEvent servletContextEvent) {
  15. System.out.println("servletContext被销毁了");
  16. }
  17. }

Listener编写完之后也有两种配置方案
xml

  1. <listener>
  2. <listener-class>com.qfedu.listener.ContextLoaderListener</listener-class>
  3. </listener>

注解方案: @WebListener
监听器什么时候执行,不是由我们决定的,而是监听器本身监听的对象决定,监听器监听的对象发生改变了,监听器中的代码就自动执行了。一个对象,同一个动作一般只有一个监听器。我们学习的是ServletContextListener!

四、总结三大web组件

1、web.xml中都可以写什么内容?
项目中的web.xml其实是tomcat服务器中web.xml的子集。它可以配置servlet,Filter,Listener
welcome-file-list 设置欢迎页,还可以设置一些参数 context_param。
2、各个组件之间的执行顺序说一下?
web.xml中加载顺序: —> listener —> filter —> servlet
filter如果是多个,加载顺序怎样。
servlet很多,加载顺序如何?load-on-startup 有关系。
3、三大组件共同的特点
编写完代码之后,必须进行配置,配置都分为两种 xml 和 注解

五、总结二阶段内容

1、HTML\CSS\JS 【熟悉并且会修改一些样式】
2、MySQL 【一定要非常熟悉】
3、Servlet \ Filter \ Listener 【后面基本都被替换了,是web的三大基石】
Servlet—->SpringMVC
Filter —-> Struts2
4、JSP — 网页模板技术
5、JdbcTemplate 它是指众多操作数据库的小框架而已(工具包)
6、JQuery/Ajax/JSON —一直用下去。
二阶段的目标:可以独立完成一些小的功能,能够排错。

企业看重的—编程经验(开发经验)不等于工作年限
代码技巧(实现技巧)、考虑问题是否全面、排错能力强、独立思考,见识广,各种类型的项目都见过,参与过,职场经验丰富。

六、验证码功能

在公司中如果你能将一个问题差分为多个小问题,你就可以做管理(项目经理,技术总监)
拆分:
1、在页面上修改样式,可以展示验证码

  1. <p style="position: relative;">
  2. <input class="ipt" style="width:150px;" type="text" name="yzm" id="yzm" placeholder="输入验证码" /> <img id="imgCode" src="${pageContext.request.contextPath}/validateCode/create" style="margin-top: 5px;" height="40px" width="100px" onclick="changeImg()">
  3. </p>

页面上还要给验证码图片加上点击事件:
再要我们的url后面添加一个参数随机值,因为相同的url不断的请求,浏览器不会去后台执行代码的,所以搞一个随机值,不断的更改值,骗过浏览器去后台执行代码。

  1. function changeImg(){
  2. console.log("进来了.....");
  3. $("#imgCode")[0].src="${pageContext.request.contextPath}/validateCode/create?data="+Math.random();
  4. }

2、在前端或者后台生成验证码,展示在页面上
借助一个工具类 ValidateCode.jar,需要先导入jar包

  1. package com.qfedu.servlet;
  2. import cn.dsna.util.images.ValidateCode;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.annotation.WebServlet;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import java.io.IOException;
  9. @WebServlet("/validateCode/*")
  10. public class ValidateCodeServlet extends BaseServlet {
  11. public void create(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  12. // 第一个参数 图片的宽度
  13. // 2 图片高度
  14. // 3 图片上包含多少个字母
  15. // 4 生成干扰线的数量
  16. ValidateCode validateCode = new ValidateCode(100,40,4,20);
  17. validateCode.createCode();//生成验证码图片
  18. // 每次生成的验证码都要放入session一个,因为我们将来登录的时候要校验用户的验证码是否正确
  19. String code = validateCode.getCode();
  20. req.getSession().setAttribute("VALIDATECODE",code);
  21. validateCode.write(resp.getOutputStream());//通过流的方式传递给页面
  22. }
  23. }

3、提交表单时验证码一定要是正确的。
需要提前在生成验证码的时候就在session中放入验证码的值,便于以后登录时校验

  1. String code = validateCode.getCode();
  2. req.getSession().setAttribute("VALIDATECODE",code);

4、登录校验:

  1. HttpSession session = req.getSession();
  2. String validatecode = (String)session.getAttribute("VALIDATECODE");
  3. String yzm = req.getParameter("yzm");
  4. if(!validatecode.equals(yzm)){
  5. resp.getWriter().write("2");
  6. }else{
  7. String username = req.getParameter("username");
  8. String password = req.getParameter("password");
  9. String isJiZhu = req.getParameter("isJiZhu");
  10. System.out.println(username+","+password+","+isJiZhu);
  11. boolean result = adminService.validateLogin(username,password);
  12. if(result){
  13. Cookie usernameCookie = new Cookie("username", username);
  14. Cookie passwordCookie = new Cookie("password", password);
  15. if(isJiZhu.equals("true")){
  16. // 设置7天过期
  17. usernameCookie.setMaxAge(7*3600*24);
  18. passwordCookie.setMaxAge(7*3600*24);
  19. }else{
  20. usernameCookie.setMaxAge(0);
  21. passwordCookie.setMaxAge(0);
  22. }
  23. resp.addCookie(usernameCookie);
  24. resp.addCookie(passwordCookie);
  25. // 将用户名保存在session中便于页面获取数据
  26. session.setAttribute("USERNAME",username);
  27. resp.getWriter().write("1");
  28. }else{
  29. resp.getWriter().write("0");
  30. }
  31. }

这是只编写验证码的一种方式而已,还有很多种。