大数据WEB阶段(十五)JavaEE三大核心技术之过滤器

Filter过滤器

一、Filter 过滤器概述

  1. Filter是JavaEE三大核心技术(Servlet 、 Filter 、 Listener)之一
  2. FIlter作用是拦截对资源的访问 , 拦截下来后可以控制是否允许通过 , 或者在允许通过前后做一些额外的操作 。
  3. 所谓的拦截其实就是对代表请求的request、对象和代表 响应的response对象拦截下来 , 进行控制
  4. 一个过滤器可能拦截多个资源 , 一个资源也可能被多个过滤器拦截
  5. 这种多个拦截器拦截一个资源的模式成为责任链模式 。
  6. 常用场景:
    1. 就与URL的访问权限控制
    2. 全站乱码解决过滤器
    3. 过滤敏感词汇
    4. 压缩响应

二、 过滤器的开发

  1. 想要开发一个过滤器 , 需要两个步骤
    1. 写一个类实现Filter接口
    2. 在web.xml中配置过滤器
  2. Filter接口:
    1. init(FilterConfig)
      1. 初始化的方法 , 当Filter被初始化时 , 调用此方法 , 执行初始化操作
    2. destory()
      1. 销毁方法, 在Filter被销毁之前调用 , 执行善后操作
    3. doFilter(ServletRequest request , ServletResponse response , FilterChain chain)
      1. 核心方法 ,在存活期间 , 过滤器拦截到对资源的访问 会造成此方法的执行 , 需要在这个方法中设计过滤器的核心逻辑代码
  3. 配置过滤器

    <filter> -- 配置一个过滤器
        <filter-name>FirstFilter</filter-name> -- 过滤器的名字
        <filter-class>com.tarena.filter.FirstFilter</filter-class> -- 过滤器的类
    </filter>
    <filter-mapping> -- 过滤器的拦截路径配置,可以配置多个
        <filter-name>FirstFilter</filter-name> -- 为哪个名字的过滤器配置
        <url-pattern>/*</url-pattern> -- 拦截哪个路径资源可以配置多个
        <servlet-name>XxxServlet</servlet-name> -- 拦截哪个名字的Servlet
        <dispatcher></dispatcher> -- 指定过滤器拦截哪种方式对资源的访问,可以取值为REQUEST FORWARD INCLUDE ERROR,如果不配置,默认只拦截REQUEST方式的访问。可以配置多个。
    </filter-mapping>
    

三、生命周期

  1. 在web应用启动时 , 会创建处web应用中配置的过滤器对象 , 创建出过滤器对象会立即调用init方法进行初始化操作 , 之后一直存活 , 直到web应用被销毁时 , Filter跟着被销毁.在销毁之前会自动调用destory方法执行善后操作 。 在存活期间 , 每当拦截到资源访问 , 就执行doFilter方法 , 来执行过滤器的 逻辑 , 如果不做操作 , 则 默认拦截 , 可以通过FilterChain类的对象的 doFilter方法实现对资源访问的放行 。 并且可以在doFilter前后做一些操作 。

四、 细节

  1. 如果一个资源被多个过滤器拦截 , 多个 拦截器的拦截顺序取决于在web.xml文件中配置过滤器时的先后顺序 。
  2. 多个 过滤器的执行 , 类似于方法 一层一层调用的过程 ,, 一层一层往里钻, 然后在一层层一层往外出 。

五、 和Filter开发相关的对象

  1. FilterConfig:

    1. init方法的参数
    2. 代表FIlter在web.xml文件中的配置对象
    3. 可以用来获取Filter在Web.xml文件中的初始化配置参数
    4. 可以用来获取ServletContext对象

      public void init(FilterConfig filterConfig) throws ServletException {
          System.out.println("init....");
      
          //1.filterConfig功能1:获取filter的初始化参数
          Enumeration<String> names = filterConfig.getInitParameterNames();
          while(names.hasMoreElements()){
              String name = names.nextElement();
              String value = filterConfig.getInitParameter(name);
              System.out.println(name+"~"+value);
          }
      
      
          //2.获取ServletContext对象
          ServletContext sc = filterConfig.getServletContext();
      
      }
      
  2. FilterChain:
    1. doFilter方法参数
    2. 代表过滤器链
    3. 提供了doFilter方法 ,放行当前过滤器 , 执行后续过滤器 , 如果后续没有过滤器则调用到相应的资源 。

六、Filter案例

  1. 全站乱码解决过滤器

    1. 在web开发过程中 , 存在请求参数乱码和响应输出乱码 。
    2. 之前的开发中 , 在所有的Servlet和jsp页面中 , 需要手动解决这两种乱码
    3. 可以通过开发过滤器拦截所有的资源访问 , 在过滤器中解决全站乱码问题 。
    4. 具体解决请求响应乱码问题:

      1. 在web.xml文件 中配置全局的编码类型
      <!-- 全局配置 -->
      <context-param>
          <param-name>encode</param-name>
          <param-value>utf-8</param-value>
      </context-param>
      2. 在过滤器初始化时获取全局配置的编码 , 并保存到过滤器中
      private String encode = null;
      public void init(FilterConfig config) throws ServletException {
          encode = (String) config.getServletContext().getAttribute("encode");
      }
      
      3. 解决响应乱码:
      在doFilter方法中
      response.setCharacterEncoding(encode);
      response.setContentType("text/html;charset="+encode);
      4. 解决请求参数乱码
          //方案一:
      //      request.setCharacterEncoding(encode); //只能解决Post请求参数的乱码
          //可以解决Post和Get请求类型的参数乱码
          //但是在转码的时候需要指定具体的参数名称  ,转码之后要重新放入request中供servlet拿取更是不能实现 , 所以 不可行
      //      String param = new String(request.getParameter("xxxx").getBytes("iso-8859-1") , encode);
          //方案二:
          //request中的请求参数本身无法改变 
          //那么 换一个思路 想办法改造和获取请求参数相关的方法 在方法内加上解决乱码的代码 
          //这样通过这些方法获取请求参数时 解决好乱码再返回 用起来就感觉 乱码被解决了一样
              //改造原有request方法方案一: 
              //继承
              //继承只能先改造在创建实例 , 但是现在已经有了request对象,  就算通过继承改造了ServletRequest类也不会影响到已有的对象 , 排除
              //改造原有request方法方案二:
              //装饰设计模式
              //1. 新建一个类 , 实现与被改造对象相同的接口
              //2. 通过狗仔方法传入被改造的对象并保存在本类中
              //3. 然后实现接口中所有的方法 , 如果需要改造则在对应的方法里写出逻辑 , 如果不需要改造的方法 ,则直接通过传入的没被改造的参数对象调用原有的方法即可
              //这种方案 的缺点 ,如果被改造的方法 中方法过多时 ,这个操作会十分繁琐 。
              //改造原有的request对象方案三:
              //在装饰设计模式的基础上实现
              //通过源码 发现Servlet包下已经提供了一个ServletRequestWrapper类 , 它实现了与ServletRequest相同的接口
              //也就是说他就是java中已经提供的供开发者修改request对象中方法的入口
              //新建一个类继承ServletRequestWrapper类之后 ,通过构造方法 把原始对象传进去 , 然后只重写需要改造的方法即可
              代码//
              public class EncodeFilter implements Filter{
          /**
           * 当前web应用编码集
           */
          private String encode = null;
      
          /**
           * 初始化方法
           */
          public void init(FilterConfig filterConfig) throws ServletException {
              //获取ServletContext对象
              ServletContext sc = filterConfig.getServletContext();
              //读取初始化参数 中的 编码集 配置
              this.encode = sc.getInitParameter("encode");
          }
      
          /**
           * 过滤方法
           */
          public void doFilter(final ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
              //--全站响应乱码
              response.setCharacterEncoding(encode);
              response.setContentType("text/html;charset="+encode);
              //--全站请求乱码 - 通过装饰器 装饰request 修改其中的和获取请求参数相关的方法 增加了乱码解决的代码
              ServletRequest myReq = new MyServletRequest((HttpServletRequest) request);
              //放行资源
              chain.doFilter(myReq, response);
          }
      
          /**
           * 销毁方法
           */
          public void destroy() {
      
          }
      
          /**
           * 内部类 ServletRequest的装饰类 改造了获取请求参数相关的方法 增加了乱码解决的代码
           */
          //继承了HttpServletRequestWrapper ,这个父类本身就是 HttpServletRequest的装饰器  在其中提供方法的默认的实现 不想改造的方法 不用管 想改造的方法 覆盖父类方法即可
          class MyServletRequest extends HttpServletRequestWrapper{
      
              private ServletRequest request = null;
      
              private boolean hasNotEncode = true;
      
              //构造器 接受传入的request保存在类的内部
              public MyServletRequest(HttpServletRequest request) {
                  super(request);
                  this.request = request;
              }
      
              //覆盖和获取请求参数相关的方法
              @Override
              public Map<String,String[]> getParameterMap() {
                  try {
                      //1.获取真正request的请求参数组成的map
                      Map<String,String[]>  map = request.getParameterMap();
                      if(hasNotEncode){//由于真正的request对此map会缓存 所以解决乱码的操作 只需要做一次 此处通过hasNotEncode来控制
                          //2.遍历map
                          for(Map.Entry<String, String[]>entry : map.entrySet()){
                              //3.获取当前遍历到的值的数组
                              String [] values = entry.getValue();
                              //4.遍历值的数组 
                              for(int i = 0;i<values.length;i++){
                                  //5.解决乱码 存回数组
                                  values[i] = new String(values[i].getBytes("iso8859-1"),encode);
                              }
                          }
                          hasNotEncode = false;
                      }
                      //6.返回解决完乱码的map
                      return map;
                  } catch (Exception e) {
                      e.printStackTrace();
                      throw new RuntimeException(e);
                  }
              }
      
  2. 用户30天内自动登录

    1. 在处理用户登录时 , 判断用户是否勾选30天内自动登录 , 如果用户名密码正确且勾选过该选项 , 则发送cookie , 将用户名密码保存30天。 为了安全起见 , 保存之前先对密码进行MD5加密
    2. 之后用户在来访问时经过自动登录过滤器被拦截 , 如果用户未登录 , 且带了自动登录的cookie , 并且其中的用户名密码都正确 , 则给给用户给自动登录 。但是无论自动登录与否都要对url放行 。
    3. 在LoginServlet中添加用户自动登录逻辑: 如果用户勾选 了自动登录 , 则将用户信息添加进cookie中保存在本地
    4. 在AutoLoginFilter中 拦截所有请求 , 先判断是否登录 , 在判断是否有自动登录cookie ,最后判断用户密码是否正确
    5. 自动登录过滤器源码

      public class AutoLoginFilter implements Filter{
      
          public void destroy() {
      
          }
      
          public void doFilter(ServletRequest request, ServletResponse response,
                  FilterChain chain) throws IOException, ServletException {
              HttpServletRequest req = (HttpServletRequest) request;
      //      1. 判断该用户是否 已经登录
              if(req.getSession(false) == null ||req.getSession(false).getAttribute("user") == null){
                  System.out.println("用户未登录");
      //      2. 判断访问时是否带有自动登录cookie
                  Cookie[] cookies = req.getCookies();
                  for(Cookie c :cookies){
                      if("autologin".equals(c.getName())){
                          System.out.println("带有自动登录cookie");
                          String v = c.getValue();
                          String[] vs = URLDecoder.decode(v , "utf-8").split("#");
                          UserService us = BaseFactory.getBase().getInstance(UserService.class);
      //      3. 验证用户名密码是否正确
                          User user = us.login(vs[0], vs[1]);
                          if(user != null){
                              System.out.println("开始登录");
                              //三个条件都满足 , 添加登录标记
                              req.getSession().setAttribute("user", user);
                              System.out.println("自动登录成功");
                          }
                          break;
                      }
                  }
              }
      //      4. 无论是否自动 登录成功 , 都放行访问。  放行访问
              chain.doFilter(request , response);
          }
      
          public void init(FilterConfig arg0) throws ServletException {
              // TODO Auto-generated method stub
      
          }
      
      }
      

七、MD5加密算法

  1. 又称数据摘要算法 , 数据指纹算法
  2. 任意长度的二进制文件计算出128位二进制的再要信息 , 通常转换为32位16进制显示 。
  3. 明文相同算出的密文一定相同
  4. 明文不同算出的密文一定不同 (概率极低 , 所以一般认为是唯一的)
  5. 只能由明文算成密文 ,, 不能有密文算成明文
  6. 应用:
    1. 加密存储数据
    2. 文件完整性校验
    3. 数字签名
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值