day036 过滤器 filter

1案例1-自动登录

1.1 需求及流程介绍

当用户直接访问登录后的页面(例如:购物车页面,订单页面等)直接显示出上一次登录的用户对象,这个功能就是自动登录的功能。如果用户在上一次登录的时候,没有勾选自动登录,那么用户直接访问登录后的页面时,不显示用户是谁,加购物车,提交订单等都需要让用户先登录。

(简单说就是跟jd一样。今天只做前一句话,后一句话是权限控制,是后面的内容。)

 

用户自动登录的流程介绍.png

1.2 技术分析

1:可以使用会话技术让服务器知道浏览器发送的多次请求。

2:需要使用过滤器技术,对用户发送的每一次请求都先进行过滤,过滤后,才让用户执行真正的资源。

1.3 过滤器filter概述

过滤器就是运行在服务器最前端的java小程序。

servlet,filter,listener合称为服务器的三大组件,都是被tomcat服务器创建,被服务调用。

servlet与filter的区别:

0:servlet能做的filter一定能做,filter能做的servlet不一定能做。

1:servlet通常用于专一的请求处理,而filter通常用于通用的请求处理;

2:filter一定优先于servlet执行;

进servlet之前要经过filter,从servlet出去的时候也要经过filter。

1.4 过滤器filter的应用场景

1:网站所有请求的中文参数乱码和响应乱码处理。

2:网站访问权限控制。

3:自动登录。

4:敏感字过滤。

1.5 过滤器filter的编写步骤

过滤器的英文名:filter,是javaEE定义的一个接口。

程序员只需实现filter接口,编写相应的方法即可。

image.png

image.png

image.png

关于filter接口中的doFilter方法的3个参数:

ServletRequest类型的参数:代表的是请求对象。

ServletResponse类型的参数:代表的是响应对象。

FilterChain类型的参数:代表的是用于放行的对象。(如果不放行,那么浏览器请求的资源将得不到任何响应)

1.6 自动登录案例的实现

1:案例环境搭建。(数据库创建,静态页面复制,jar包选择,工具类复制)

2:编写3层包结构。

3:将login.html文件修改成login.jsp文件。

4:编写LoginServlet,完成用户完整的登录流程,当用户登陆成功的时候需要将用户对象保存到session中。

5:编写service和dao。

6:修改loginServlet,当用户登录成功的时候,判断用户是否希望帮用户完成自动登录的动作。

如果希望,则创建一个cookie对象,并保存用户名和密码为7天

如果不希望,立刻删除原来的cookie。

7:编写一个过滤器,在过滤器中获取cookie,并使用cookie中的用户名和密码查询数据库,如果能查到则帮用户把用户对象保存到session中。

 

注意:

所谓的登录就是session中有用户对象。

部分参考代码:

说明:

页面里

用户名标签处:name="username"

密码标签处:name="password"

是否自动登录勾选选项处标签:name="autoLogin" value="ok"

 

 

过滤器AutoLoginFilter.java:

package com.itheima.anli00_filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.itheima.anli02_service.UserService;
import com.itheima.anli04_domain.User;

/**
 * 完成自动登录的filter
 */
public class AutoLoginFilter implements Filter {

    public void destroy() {
    }
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        /*
         * 思路:
         * 
         *  1:判断session中是不是已经拥有了user对象,如果有,则证明用户已经登录了,可以直接放行,否则继续判断下一步;
         *  2:获取浏览器携带的所有cookie,并查找出名字为auto的cookie;如果没有cookie,直接放行,否则继续下一步;
         *  3:获取cookie中的用户名和密码查询数据库;如果查询成功,则保存到seesion中,如果没查到,直接放行;
         * 
         */
        
        HttpServletRequest r = (HttpServletRequest)request;
        HttpSession se = r.getSession();
        Object user = se.getAttribute("user");
        if(user==null){
            //需要继续下一步
            Cookie[] cs = r.getCookies();
            if(cs!=null){
                //说明有一部分cookie
                Cookie c=null;//准备使用c保存我们想要的cookie对象
                for (Cookie cookie : cs) {
                    if(cookie.getName().equals("auto")){
                        c=cookie;
                    }
                }
                //判断有没有找到cookie
                if(c!=null){
                    String value = c.getValue();
                    String[] split = value.split("<itheima>");
                    UserService us = new UserService();
                    User u2 = us.findUserByUsernameAndPassword(split[0],split[1]);
                    if(u2!=null){
                        //帮这个用户保存session
                        se.setAttribute("user",u2);
                    }
                }
            }
        }
        //放行
        chain.doFilter(request, response);
    }

    public void init(FilterConfig fConfig) throws ServletException {
    }

}

LoginServlet.java

package com.itheima.anli01_web;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.itheima.anli02_service.UserService;
import com.itheima.anli04_domain.User;

/**
 * 用户登录的servlet
 */
public class LoginServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //System.out.println(request);
        //1:参  调   存   转
        String un = request.getParameter("username");
        String ps = request.getParameter("password");
        String au = request.getParameter("autoLogin");
        //2:调用业务层
        UserService  us = new UserService();
        User u = us.findUserByUsernameAndPassword(un,ps);
        //3:存,一定要将用户保存到session对象中,
        if(u==null){
            //说明用户名或密码错误,回到login.jsp,让用户重新登录
            request.setAttribute("msg","用户名或密码错误");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
            return;//结束方法
        }
        //4:说明查到了用户,保存到session中
        request.getSession().setAttribute("user", u);
        //判断用户是否希望我们完成自动登录功能 
        Cookie c = new Cookie("auto",un+"<itheima>"+ps);
        c.setPath("/");
        if("ok".equals(au)){
            //说明用户希望自动登录,将cookie设置为7天
            c.setMaxAge(60*60*24*7);
        }else{
            //说明用户不希望自动登录,将cookie设置为0秒
            c.setMaxAge(0);
        }
        response.addCookie(c);
        //转发的购物网站,可以让用户去购物了
        request.getRequestDispatcher("/index.jsp").forward(request, response);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

2 filter总结

2.1 Filter的生命周期

从filter创建一直到filter死亡,这个过程就是filter的生命周期。

Filter的生命周期方法有3个:

init()  初始化

doFilter()  执行过滤

destroy ()  死亡

 

doFilter()方法:只要能匹配路径成功一定会执行,即使有多个filter都匹配了路径也会逐个执行。执行顺序仅仅与web.xml中filter-mapping的配置顺序有关,从上至下,逐个执行。

init()方法:随着tomcat启动就会创建filter对象。

destroy()方法:随着tomcat服务器正常停止而死亡。

 

2.2 多个filter顺序问题:

先后顺序仅仅与在web.xml的配置顺序有关。

filter.png

2.4 FilterConfig对象(了解)

在web.xml中的filter标签中配置init-param属性和属性值时,可以使用FilterConfig对象,获取这些初始化配置参数。

例如:

<filter>
    <display-name>AutoLoginFilter</display-name>
    <filter-name>AutoLoginFilter</filter-name>
    <filter-class>com.itheima.anli00_filter.AutoLoginFilter</filter-class>
    <init-param>
        <param-name>aaa</param-name>
        <param-value>111</param-value>
    </init-param>
</filter>

javaee的api中有。

常用API:

getInitParameter(属性名);

2.5 关于filter中的url-pattern的配置

在filter中有3种配置:

1:完全匹配    /xxx

2:目录匹配(通配符匹配)   /*       (用的最多)

3:后缀名匹配   *.xxx                        (偶尔使用)

注意:

三种匹配方式不分优先级,只要与路径匹配成功,都会执行,执行顺序仅与web.xml中的配置顺序有关。

2.6 关于filter的拦截方式问题

在filter-mapping中,也可以不使用url-pattern。

使用servlet-name标签替代(了解,不常用)

例如:

 image.png

关于过滤的方式:

 image.png

3 案例3-中文编码过滤器

3.1 需求

当用户访问工程中的任意一个servlet的时候,在servlet中无需再处理中文参数,也无需处理中文响应,都能保证参数和响应数据的正常。(不能乱码)

3.2 技术分析

在servlet中获取参数的方法有3个,分别是:getParameter   getParameterValues    getParameterMap

只要对这3个方法进行增强,即可实现中文参数乱码问题的结果。

响应的时候,只需在调用getWriter或getOutputStream之前,设置响应的Content-Type头信息即可。

使用过滤器就能完成这个事情。

3.3 增强一个类的方法的技术

方式1:继承,重写方法。

缺点:

子类与父类完全绑在了一起,耦合度太高,代码不灵活。(此案例中和tomcat耦合度太高,换个服务器不用tomcat了直接就不好使了)

好处:

简单,好写,快捷。

方式2:装饰者模式

(ps:关于各种各样的设计模式,不管是什么设计模式,都是在抽取工具类的时候才会用)

装饰者模式是指A类与B类是兄弟类,将来面向接口开发的时候,使用A类中的方法,替代B类中的方法,那么此时就可以称为A装饰了B。A就是装饰者,B就是被装饰者。

 

好处:

代码灵活度相对高,只要不更换接口,子类可以替换。

使用前提:

在装饰者中,必须能使用被装饰者的对象,且需要与被装饰者实现相同的接口。

代码步骤:

1:编写一个类,实现与被装饰者相同的接口。(成为兄弟)

2:在类中定义一个接口类型的成员变量用于保存被装饰者对象。

3:编写增强后的方法即可。(增强方法)

本案例中由于直接使用HttpServletRequest接口,需要重写的方法太多,因此选择继承HttpServletRequestWrapper类来与HttpServletRequest接口产生关系。

3.4 过滤器代码实现:

实际装饰的是request对象。

无标题.png

package com.itheima.anli00_filter;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

/**
 * 我们自己采用装饰者模式,对reqeust对象进行增强的工具类
 */
public class MyEncodingFilter implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //1:将request和response转成子接口类型
        HttpServletRequest r = (HttpServletRequest)request;
        HttpServletResponse res = (HttpServletResponse)response;
        //2:获取请求方式,如果是get请求,则使用装饰者模式解决乱码,如果是post请求,则直接面向原始的request对象,调用setCharacterEncoding方法解决乱码
        String method = r.getMethod();
        //3:先解决响应乱码问题
        res.setContentType("text/html;charset=utf-8");
        if("GET".equalsIgnoreCase(method)){
            //说明需要使用装饰者
            MyRequest z=new MyRequest(r);
            //放行,传递装饰后的对象
            chain.doFilter(z, res);
        }else{
            //可以直接解决
            r.setCharacterEncoding("utf-8");
            chain.doFilter(r, res);
        }
    }
    public void init(FilterConfig fConfig) throws ServletException {
    }
}

class MyRequest extends HttpServletRequestWrapper{
    //定义一个接口类型的成员变量,用于保存被装饰的对象
    private HttpServletRequest request ;
    //为了解决多次调用方法的时候,第一次不乱,之后反而乱码的问题,我们需要定义一个flag,让转换的过程仅执行一次
    private boolean flag = true;//默认可以进行转换
    public MyRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }
    //增强方法;
    //1:增强getParameterMap
    public Map<String, String[]> getParameterMap() {
        //1:先使用request获取原始的浏览器传递过来的参数
        Map<String, String[]> map = request.getParameterMap();
        //2:此时如果map中有中文参数,一定是乱码的!!!  仅考虑get请求,post请求直接面向原始对象解决即可
        if(flag){
            Set<String> set = map.keySet();
            for (String key : set) {
                String[] vs = map.get(key);
                //对数组中的参数进行处理,处理后需要将参数重新保存回数组
                for(int i=0;i<vs.length;i++){
                    //只要对数组的每一个元素进行了修改,那么map中的数组中的元素自然就跟着变化了(已经解决中文乱码了)
                    try {
                        vs[i]=new String(vs[i].getBytes("iso8859-1"),"utf-8");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    }
                }
            }
            //修改flag为false
            flag = false;
        }
        //返回已经解决了乱码的map
        return map;
    }
    //重写getParameterValues方法,这个方法中的数据,都可以直接从map中获取,因为map已经解决了乱码
    @Override
    public String[] getParameterValues(String name) {
        return getParameterMap().get(name);
    }
    @Override
    public String getParameter(String name) {
        String[] vs = getParameterValues(name);
        if(vs==null){
            return "";
        }
        return vs[0];
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值