2-37.2 Filter和综合案例

Session和浏览器之间技术内幕

请添加图片描述

作用域总结【总要】

  • HttpServletRequest:一次请求,请求响应之前有效

  • HttpSession:一次会话开始,浏览器不关闭或不超时之前有效

  • ServletContext:服务器启动开始,服务器停止之前有效

ps:关系到数据的传递

Session和Cookie的区别和使用范围

1、描述Cookie和Session的区别

①存在的位置:

cookie 存在于客户端,临时文件夹中; session存在于服务器的内存中,一个session域对象为一个用户浏览器服务

②安全性
cookie是以明文的方式存放在客户端的,安全性低,可以通过一个加密算法进行加密后存放; session存放于服务器的内存中,所以安全性好

③网络传输量
cookie会传递消息给服务器; session本身存放于服务器,不会有传送流量

④生命周期(以20分钟为例)
cookie的生命周期是累计的,从创建时,就开始计时,20分钟后,cookie生命周期结束;
session的生命周期是间隔的,从创建时,开始计时如在20分钟,没有访问session,那么session生命周期被销毁。但是,如果在20分钟内(如在第19分钟时)访问过session,那么,将重新计算session的生命周期。关机会造成session生命周期的结束,但是对cookie没有影响

⑤访问范围
cookie为多个用户浏览器共享; session为一个用户浏览器独享

2、session产生的session_id放在cookie里面,如果用户把cookie禁止掉,是不是session也不能用了呢?

禁止掉cookie后,理论上session是不可以用,不过通过其他的方式来获得这个sessionid,比如,可以跟在url的后面,或者以表单的形势提交到服务器端。从而使服务器端了解客户端的状态。

3、为什么说session 比cookie更安全?

真正的cookie存在于客户端硬盘上的一个文本文件,如果两者一样的话,只要cookie就好了,让客户端来分提服务器的负担,并且对于用户来说又是透明的。但实际上不是。

session的sessionID是放在cookie里,要想功破session的话,得分两步:

第一要得到sessionID。攻破cookie后,你要得到sessionID,sessionID是要有人登录,或者启动session_start才会有,你不知道什么时候会有人登录。

第二取有效sessionID。sessionID是加密的,第二次session_start的时候,前一次的sessionID就没有用了,session过期时sessionid也会失效,想在短时间内功破加了密的 sessionID很难。session是针对某一次通信而言,会话结束session也就随着消失了。
使session失效的方法:
1.关闭tomcat 2.重启web应用 3.session时间到 4.无效的session

4、cookie和session原理?

cookie采用的是客户端的会话状态的一种储存机制。它是服务器在本地机器上存储的小段文本或者是内存中的一段数据,并随每一个请求发送至同一个服务器。

session是一种服务器端的信息管理机制,它把这些文件信息以文件的形式存放在服务器的硬盘空间上

所谓session你可以这样理解:当你与服务端进行会话时,比如说登陆成功后,服务端会为你开壁一块内存区间,用以存放你这次会话的一些内容,比如说用户名之类的。那么就需要一个东西来标志这个内存区间是你的而不是别人的,这个东西就是session id(jsessionid只是tomcat中对session id的叫法,在其它容器里面,不一定就是叫jsessionid了。),而这个内存区间你可以理解为session。
然后,服务器会将这个session id发回给你的浏览器,放入你的浏览器的cookies中(这个cookies是内存cookies,跟一般的不一样,它会随着浏览器的关闭而消失)。
之后,只有你浏览器没有关闭,你每向服务器发请求,服务器就会从你发送过来的cookies中拿出这个session id,然后根据这个session id到相应的内存中取你之前存放的数据。
但是,如果你退出登陆了,服务器会清掉属于你的内存区域,所以你再登的话,会产生一个新的session了。

JavaWeb三大组件之一Filter【过滤器】

什么是过滤器

Filiter也称为过滤器,它是Servlet技术中心的一个技术分支,WEB开发人员可以用过 Filter技术,对WEB服务器进行管理所有WEB资源

**【JSP、Servlet、静态html、传递信息】**有效拦截和过滤,从而实现某些特殊的功能,例如对所有Service层的代码添加try-catch,实现URL级别的权限控制、过滤敏感词汇,压缩响应等等一些功能

Servelt API中提供了一个Filter接口,开发Web应用时,如果编写的Java类实现了这个接口,则把这个Java类称为过滤器类【即Filter类】,就可以通过Filter技术,实现对某个目标资源进行拦截过滤

ps:JavaWEB的三大组件【Servlet程序、Filter过滤器、Listener监听器】

​ Filter过滤器是JavaEE的规范是一个接口,Filter过滤器它的作用是:拦截请求,过滤响应

如果编写Filter过滤器

步骤:

1.创建一个Java类型,实现Filter接口

2.重写doFilter方法【核心处理的逻辑】

3.设置拦截的URL【那些URL需要过滤】

简单演示:

步骤1:先提供一个Servlet让Filter进行过滤

package com.qfedu.Filter;

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;
//演示让Filter过滤
@WebServlet(name = "Servlet1",value="/servlet1")
public class Servlet1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("我的访问是通过Filter过滤的");
    }
}

步骤2:编写衣一个Filter类完成过滤方式

package com.qfedu.Filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * 过滤路径:
 * 需要注意: 过滤器的作用是在【客户端访问服务器servlet时 提供一种过滤方式】
 *      换句话说:客户端访问服务器Servlet时不是直接访问Servlet,而是通过访问过滤器【满足某项需求】之后在访问Servlet
 *      所以使用注解配置路径的时候,路径配置的是【过滤的资源路径】
 *      filter这个类不需要手动访问,访问被Filter标注的资源自动调用
 */
@WebFilter("/servlet1")
public class MyFilter1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("过滤器初始化......init...."+filterConfig);
    }

    /**
     * 过滤器核心方法,这个方法是对过滤逻辑的实现,主要实现拦截过滤使用
     * @param servletRequest
     * @param servletResponse
     * 这两个参数是基本的请求和响应【因为现实是服务器程序,一次完成会话 需要有请求和响应,过滤器过滤的这个请求和响应】
     * ps:因为这两个数据类型是父类【接口】,所以需要处理一些数据的时候【需要强制类型转换(HttpServletXXXX)】
     * @param filterChain
     * FilterChain数类型【接口类型】  翻译过来是 过滤链
     * 作用:在项目中国可能存在多个过滤器,过滤器彼此之间就需要通过过滤器链来完成传递【操作】
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //这里打印一句话,相当于在可客户端访问过来的时候出现的过滤逻辑
        System.out.println("过滤之前........doFilter");
        //让请求继续向下传递,FilterChain过滤链,如果当前只有一个Filter直接传递下去【Servlet】,若有其他Filter那么就传递到其他的Filter
        //无论如何传递,最终都会回到资源中【JSP、Servlet、静态HTML等等】

            filterChain.doFilter(servletRequest,servletResponse);

        //过滤链发送完毕之后,下面打印语句常出现就相当于对Servlet的过滤处理完毕

        System.out.println("过滤之后.......doFilter");
    }

    @Override
    public void destroy() {
        System.out.println("过滤器销毁了......");
    }
}

步骤3:如果让Filter发挥作用

PS:Filter是不需要手动调用的,Filter是自动触发,触发的原则【就是过滤路径】

路径的配置

注解方式

在自定义Filter类上使用注解@WebFilter(“过滤路径”)

ps:这里参数可以完全参考@WebServlet注解配置,过滤路径是什么就过滤谁

过滤器的过滤路径通常三种形式:

​ 1.精确过滤匹配: 比如: /index.jsp /myservlet1

​ 2.后缀过滤匹配: 比如 *.jsp *.html *.jpg 【千万不要添加(/),即绝对不可以这样配置 / *.jsp】

​ 3.通配符匹配 : 比如 / * 表示拦截过滤所有 ,注意不能使用【/】进行匹配

​ ps:在匹配时可以增加路径 例如 某个路径下的某个资源 —》 /aaa/bbb/* /aaa/bbb/myservlet 允许

web.xml配置

  <!--过滤器XML配置-->
      <filter>
          <!--名称,多使用类名-->
          <filter-name>MyFilter1</filter-name>
          <!--类的全限定名称,获取Filter这个类-->
          <filter-class>com.qfedu.Filter.MyFilter1</filter-class>
      </filter>
      <!--映射路径配置-->
      <filter-mapping>
          <filter-name>MyFilter1</filter-name>
          <!--过滤URL的路径【匹配规则参考注解中】-->
          <url-pattern>/*</url-pattern>
      </filter-mapping>

ps:过滤的过滤路径是可以写多个,在直接中value是一个数组可以使用数组的形式赋值,存在多个过滤路径,在web.xml配置中,可以在filter-mapping标签下的天剑多个url-pattern标签

初级案例(权限验证)

请添加图片描述

login页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录界面</title>
</head>
<body>
    <div>
        <form action="/Filter/login" method="post">
            <label>账号:</label><input type="text" name ="username"/><br/>
            <label>密码:</label><input type="password" name ="password"/><br/>
            <input type="submit" value ="登录"/>
        </form>
    </div>

</body>
</html>

Filter代码

package com.qfedu.Filter.Power;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/*
  Filter中使用的注解是 WebFilter
  注解中配置的路径是要过滤的路径,不是Filter的路径【即不是Filter类的路径】
  /  表示当前工程的绝对路径 =======》 http://localhost:8080/工程名/admin/xxx.jsp
 */
@WebFilter(filterName = "AdminFilter",value="/admin/*")
public class AdminFilter implements Filter {
    public void destroy() {
    }
    /*
      这个才是核心的处理方法
     */
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //filter中如何获获取到数据,可以数据存储在一个作用域中
        //将用户的数据存到session中然后进行取出判断 就可以知道用户是否登录了
        //建议在使用请求和响应参数的时候 进行强制类型转换方便使用 ---》 HttpServletXXX
        HttpServletRequest request = (HttpServletRequest) req;
        HttpSession session = request.getSession();
         //只要获取到session中存储用户
        Object user = session.getAttribute("user");
        //如果等于null,说明还还没有登录
        if (user == null){
            request.getRequestDispatcher("/login.html").forward(req,resp);
            return;
        }else{
            //让程序继续向下访问资源目标
            chain.doFilter(req,resp);
        }
    }

    public void init(FilterConfig config) throws ServletException {

    }

}





servelt代码

package com.qfedu.Filter.Power;

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(name = "LoginServlet",value="/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //登录的核心逻辑
        response.setContentType("text/html;charset=UTF-8");
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        if("zhangsan".equalsIgnoreCase(username) && "123456".equals(password)){
            request.getSession().setAttribute("user",username);
            response.getWriter().println("登录成功!!!");
        }else{
               request.getRequestDispatcher("/login.html").forward(request,response);
        }
    }
}

过滤器连

每个过滤器实现某个特定工程,当一个的Filter中的doFilter方法被调用时,Web服务会创建一个代表Filter类中FilterChain传递该方法,在doFilter方法中,开发人员如果调用了FilterChain对象doFilter方法,则Web服务器会检查FilterChain对象总是否还有filter,如果有则地继续调用另外Filter进行处理,如果没有则直接调用目标【资源】

ps:Filter中如何判断是否还有其他Filter对象进行过滤,这个原则就是value路径

演示

package com.qfedu.Filter.FilterChain;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(filterName = "Filter1",value="/target.jsp")
public class Filter1 implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("Filter1 前置代码1");
        chain.doFilter(req, resp);
        System.out.println("Filter1 前置代码2");
    }

    public void init(FilterConfig config) throws ServletException {

    }

}
package com.qfedu.Filter.FilterChain;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(filterName = "Filter2",value="/target.jsp")
public class Filter2 implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("Filter2 前置代码1");
        chain.doFilter(req, resp);
        System.out.println("Filter2 前置代码2");
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

请添加图片描述

多个Filter过滤器特点展示

所有的filter和相同资源默认都执行在同一个线程中

package com.qfedu.Filter.FilterChain;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(filterName = "Filter1",value="/target.jsp")
public class Filter1 implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("Filter1 前置代码1");
        System.out.println("Filter1中的线程:"+Thread.currentThread().getName());
        chain.doFilter(req, resp);
        System.out.println("Filter1中的线程:"+Thread.currentThread().getName());
        System.out.println("Filter1 前置代码2");
    }

    public void init(FilterConfig config) throws ServletException {

    }

}
package com.qfedu.Filter.FilterChain;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(filterName = "Filter2",value="/target.jsp")
public class Filter2 implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("Filter2 前置代码1");
        System.out.println("Filter2中的线程:"+Thread.currentThread().getName());
        chain.doFilter(req, resp);
        System.out.println("Filter2中的线程:"+Thread.currentThread().getName());
        System.out.println("Filter2 前置代码2");
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
  <%
      System.out.println("target.jsp页面被执行了");
      System.out.println("JSP页面线程:"+Thread.currentThread().getName());
  %>
</body>
</html>

多个Filter共同执行的时候,他们都使用同一个【request】对象

package com.qfedu.Filter.FilterChain;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(filterName = "Filter1",value="/target.jsp")
public class Filter1 implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("Filter1 前置代码1");
        System.out.println("第一个Filter1在这个类中进行值的设置");
        req.setAttribute("key1","value1");
        chain.doFilter(req, resp);
        System.out.println("Filter1 前置代码2");
    }

    public void init(FilterConfig config) throws ServletException {

    }

}
package com.qfedu.Filter.FilterChain;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(filterName = "Filter2",value="/target.jsp")
public class Filter2 implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("Filter2 前置代码1");
        //获取存在request中的值;
        System.out.println("Filter2中获取request中存储的值:"+req.getAttribute("key1"));
        chain.doFilter(req, resp);
        System.out.println("Filter2 前置代码2");
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
  <%
      System.out.println("target.jsp页面被执行了");
      System.out.println("JSP页面线程:"+Thread.currentThread().getName());
      System.out.println("JSP页面中获取Filter1中存储属性值:"+request.getAttribute("key1"));
  %>
</body>
</html>


FilterConfig类过滤器初始化参数(了解)

FilterConfig它就是Filter类的配置类,Tomcat每次创建Filter的时候,也会同时创建一个FilterConfig类,这个类包含了Filter配置文件的配置信息,FilterConfig可以获取Filter中的一些配置内容:【Filter的配置名称 , Filter的初始化参数, 获取ServletContext对象

package com.qfedu.Filter.FilterConfig;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;

@WebFilter(filterName = "FilterConfig",value="/*",initParams = {@WebInitParam(name="username",value="123456")})
public class FilterConfigDemo implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        chain.doFilter(req, resp);
    }

    public void init(javax.servlet.FilterConfig config) throws ServletException {
        System.out.println("获取去Filter名称"+config.getFilterName());
        System.out.println("获取Filter中初始化参数:"+config.getInitParameter("username"));
        System.out.println("获取ServletContext对象:"+config.getServletContext());

    }

}


Filter过滤案例

案例1:过滤脏词

package com.qfedu.Filter.Filterdirty;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@WebFilter(filterName = "DortyFilter",value = "/dirty")
public class DirtyFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
      //可以在这里获取参数值,并对参数值进行判断,并修改为符合词
       chain.doFilter(new Dirty((HttpServletRequest)req), resp);
    }
    /*
      在doFilter方法中ServletRequest接口,有很多无用方法,所以我们可以针对火球参数的方进行一个修改直接对
      属性值进行一个处理
      因为ServletRequest接口如果实现,方法太多,所以可以选着它的一个子类实现HttpServletRequestWrapper

     */

    static class Dirty extends HttpServletRequestWrapper{
        List<String> list = new ArrayList<>();
        public Dirty(HttpServletRequest request){
            super(request);
            //构建的同时将脏词存进来
            list.add("SB");
            list.add("MD");
            list.add("王八蛋");
            list.add("mmp");
            list.add("二狗子");
        }
        //重写方法
        @Override
        public String getParameter(String name) {
            //根据表单中内容,获取数据并进行修改
            String parameter = super.getParameter(name);
            for(String str : list){
                if(parameter.equalsIgnoreCase(str)){
                    parameter = "世界太美好了";
                }
            }
            return  parameter;
        }
    }

    public void init(FilterConfig config) throws ServletException {

    }

}
package com.qfedu.Filter.Filterdirty;

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(name = "DirtyServlet",value="/dirty")
public class DirtyServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //有了过滤器之后,可以将Servlet中的参数进行存储,并交给Filter来处理
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String lovename = request.getParameter("lovename");
        System.out.println("获取的数据为:"+username+"\n"+password+"\n"+lovename);
    }
}


案例2:过滤器解决编码问题

package com.qfedu.Filter.Filterdirty;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
//编码解决
@WebFilter(filterName = "EncodingFilter",value ="/*")
public class EncodingFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //提供统一的请求和响应的乱码处理
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

}


总结

如果是使用Filter过滤Servlet,在执行Servlet之前客户端会先把请求发送给Filter,处理完成之后在到达Servlet,响应时,会根据执行流程再次反向执行Filter

Filter可以解决多个Servlet中共性代码冗余问题(例如:乱码问题,异常问题,登录验证等等)

所有Filter和目标资源默认都执行在同一个线程中,多个Filter共同执行的时候,他们都是用同一个request对象

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值