Servlet——Filter(过滤器)

1. 过滤器

Filter接口中有一个doFilter方法,当我们编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前,
都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
调用目标资源之前,让一段代码执行。
是否调用目标资源(即是否让用户访问web资源)。
调用目标资源之后,让一段代码执行。
  web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个
doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,
否则web资源不会被访问。

图示:
在这里插入图片描述

2. 概念

Filter 过滤器
可以对请求(响应)进行过滤

请求 --> 过滤器1 --> 过滤器2 --> … --> 过滤器n --> 目的地(Servlet, jsp)
例如:

 package filter;

 import javax.servlet.*;
 import javax.servlet.annotation.WebFilter;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
//匹配所有路径,所有的jsp或者servlet都得经过此过滤器,这要可以减少编写大量重复的代码
@WebFilter(urlPatterns = "/*")
 public class Filter1 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("进入了Filter1");
    // 设置字符编码,在过滤中写入编码,省去了多次编写同样代码的过程
    servletRequest.setCharacterEncoding("utf-8");
   //转换为子类,进而使用其方法
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    //过滤继续执行
    filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
 }
}

3. 编写过滤器的步骤

  1. 实现Filter接口
  2. 在类上添加@WebFilter(urlPattern=“要过滤的目标路径”)
  3. 在doFilter方法中控制请求是否前进到下一个过滤器
filterChain.doFilter(request, response); // 调用此方法,表示请求继续前进,不调用,请求就停止不前了

4. 匹配路径的三种写法

  1. 精确匹配:针对于过滤具体一个servlet
    例如: /s1 /s2 …

  2. 前缀匹配:针对要同时过滤多个servlet
    例如:有两个servlet,路径分别是 /user/s1, /user/s2
    过滤器的匹配路径就可以写为: /user/ *
    / * 表示匹配此应用程序中所有路径

  3. 后缀匹配:
    .后缀
    注意: 后缀匹配不要以/开头, 精确匹配和前缀匹配需要以/开头
    例如
    .jsp 表示在请求到达所有jsp之前,经过此过滤器

5. 多个过滤器

使用@WebFilter去控制匹配路径时,多个过滤器都匹配目标,执行顺序和过滤器类名有关。
按类名的字母顺序排序
例如在idea编译器里我们有filter1,filte2,过滤顺序将由前之后以此进行,这个顺序属于默认顺序也不是一定的,后面我们会学到在xml文件里修改,可以改变过滤顺序。

6. 过滤器的应用

应用1: 字符编码过滤器
在过滤器中统一调用 request.setCharacterEncoding方法
应用2: 登录检查

HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpSession session = request.getSession();
// 检查是否有登录标记
if(session.getAttribute("isLogin") == null) { // 如果没有找到
    request.getRequestDispatcher("/login.jsp").forward(request, servletResponse);
    return;
} else { // 找到登录标记, 放行请求,让请求继续前进
    filterChain.doFilter(request, servletResponse);
}

应用3: 自动登录

  1. 过滤器

  2. cookie (把用户名和密码的信息记录在cookie)

     package controller;
     import util.Utils;
     import javax.servlet.ServletException;
     import javax.servlet.annotation.WebServlet;
     import javax.servlet.http.*;
     import java.io.IOException;
     import java.sql.Connection;
     import java.sql.PreparedStatement;
     import java.sql.ResultSet;
    
     @WebServlet(urlPatterns = "/login")
    public class LoginServlet extends HttpServlet {
     @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     String username = req.getParameter("username");
     String password = req.getParameter("password");
    
     Connection conn = null;
     PreparedStatement stmt = null;
     ResultSet rs = null;
     try {
         conn = Utils.getConnection();
         stmt = conn.prepareStatement("select password from user where username=?");
         stmt.setString(1, username);
         rs = stmt.executeQuery();
    
         // 有没有用户
         if(rs.next()) {
             // 有用户
             String dbPassword = rs.getString("password");
             // 判断密码是否相等
             if (dbPassword.equals(password)) {
                 HttpSession session = req.getSession();
                 session.setAttribute("isLogin", true);
                 session.setAttribute("username", username);
    
                 // 自动登录(吧用户名,密码记录在cookie里)
                 Cookie c1 = new Cookie("username", username);
                 Cookie c2 = new Cookie("password", password);
                 c1.setMaxAge( 3600 * 24 * 7); // 一周
                 c1.setHttpOnly(true); // 控制这个cookie不能通过js代码修改
                 c2.setMaxAge( 3600 * 24 * 7); // 一周
                 c2.setHttpOnly(true);
                 resp.addCookie(c1);
                 resp.addCookie(c2);
    
                 req.getRequestDispatcher("/welcome.jsp").forward(req,resp);
             } else {
                 req.getRequestDispatcher("/login.jsp").forward(req,resp);
             }
         } else { // 没有这个用户
             req.getRequestDispatcher("/login.jsp").forward(req,resp);
         }
     } catch (Exception e) {
         e.printStackTrace();
     } finally {
         Utils.close(rs, stmt, conn);
     }
    }
    }
    
    
    
    
      package filter;
      import util.Utils;
      import javax.servlet.*;
      import javax.servlet.annotation.WebFilter;
      import javax.servlet.http.Cookie;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import javax.servlet.http.HttpSession;
      import java.io.IOException;
      import java.sql.Connection;
      import java.sql.PreparedStatement;
      import java.sql.ResultSet;
      /*
    检查是否有登录标记,没有就跳转到登录页面
    */
       @WebFilter(urlPatterns = "/user/*")
    public class LoginFilter implements Filter {
       @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    }
     @Override
     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain           filterChain) throws IOException, ServletException {
     HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
       HttpSession session = request.getSession();
       // 如果没有登录,跳转至登录页面
     if(session.getAttribute("isLogin") != null) {
         // 如果登录了,放行
         filterChain.doFilter(request,response);
         return;
    
     } else {
         Cookie[] cookies = request.getCookies();
         Cookie uCookie = null; // 存储username的cookie
         Cookie pCookie = null; // 存储password的cookie
         for (Cookie cookie : cookies) {
             if(cookie.getName().equals("username")) {
                 uCookie = cookie;
             }
             if(cookie.getName().equals("password")) {
                 pCookie = cookie;
             }
         }
         if(uCookie == null || pCookie == null) {
             request.getRequestDispatcher("/login.jsp").forward(request,response);
             return;
         } else {
             Connection conn = null;
             PreparedStatement stmt = null;
             ResultSet rs = null;
             try {
                 conn = Utils.getConnection();
                 stmt = conn.prepareStatement("select password from user where username=?");
                 stmt.setString(1, uCookie.getValue());
                 rs = stmt.executeQuery();
                 if(rs.next()) {
                     // 用户名,密码都正确,放行请求
                     if (rs.getString("password").equals(pCookie.getValue())) {
                         filterChain.doFilter(request, response);
                     } else { // 密码不正确
                         request.getRequestDispatcher("/login.jsp").forward(request,response);
                     }
                 } else { // 用户没查询到
                     request.getRequestDispatcher("/login.jsp").forward(request,response);
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             } finally {
                 Utils.close(rs, stmt, conn);
             }
         }
     }
         }
     @Override
     public void destroy() {
    }
    }
    

7. 密码的加密处理

散列技术(签名技术)
明文 密文
123456 ==> e10adc3949ba59abbe56e057f20f883e
md5 算法 16字节
sha256 算法

特点1:运算不可逆
特点2:运算的结果长度固定
特点3:原始内容一样,运算的结果是一样的

8. jsp 隐式对象(9个)

config jsp的配置信息
page jsp自身对象(this)
exception 异常对象(isErrorPage=true)

response 响应
out 响应字符流

pageContext 页面作用域
request 请求作用域 变量类型 HttpServletRequest
session 会话作用域 (同一个客户端的多次请求) HttpSession
application 应用作用域 变量类型 ServletContext (上下文)

他们都有如下方法
.setAttribute("名", 值);   // 设置
Object 值 = .getAttribute("名"); // 获取
Object 值 = .removeAttribute("名"); // 删除

作用域(Scope)

<%// 向page作用域存入内容
//    pageContext.setAttribute("name", "张三"); // 同一页面中的信息
    request.setAttribute("name", "李四");    // 一次请求有效的信息(例如servlet存入结果,jsp获取结果)
    session.setAttribute("name", "王五"); // 每个用户(浏览器)独有的信息(例如购物车信息)
    application.setAttribute("name", "赵六"); // 适合放全局信息(城市信息, 类别信息)
    %>

    <!-- 从page作用域获取内容-->
    <%= pageContext.getAttribute("name")%>
    <!--  从page作用域获取内容(推荐) -->
    ${pageScope.name}

    <!-- 从request作用域取-->
    <%= request.getAttribute("name")%>
    <!-- 从request作用域取(推荐)-->
    ${requestScope.name}

    <!-- 从session作用域取值 -->
    <%= session.getAttribute("name")%>
    <!-- 从session作用域取值(推荐) -->
    ${sessionScope.name}

    <!-- 从application作用域取值 -->
    <%= application.getAttribute("name")%>
    <!-- 从application作用域取值(推荐) -->
    ${applicationScope.name}

    <hr/>
    <!-- 在el表达式取作用域变量时,不加前缀,先搜索pageScope, 再搜索requestScope, 继续搜索sessionScope, 再搜索applicationScope-->
    ${name}

9. el表达式中的隐式对象(11个)

pageScope page作用域 类型是map集合
requestScope request作用域 类型是map集合
sessionScope 会话作用域 类型是map集合
applicationScope 应用作用域 类型是map集合

param 代表所有请求参数集合 类型是map集合
paramValues 代表所有请求参数集合(用来获取多值参数)

cookie 代表请求中所有的cookie集合 map类型
key是cookie的name, value是cookie对象(name,value)

header 获取请求头的内容 类型是map集合f
key是请求头中key的名称
headerValues 获取请求头的内容(用来获取多值)
initParam 每个jsp可以配置一个初始化参数
initParam.key
pageContext

猜你喜欢

转载自blog.csdn.net/qq_42689450/article/details/82861289