zoukankan      html  css  js  c++  java
  • Java Web之过滤器

    1.过滤器的概念

    过滤器是一个服务器端的组件,它可以拦截客户端的请求和响应信息,并对这些信息进行过滤。

    注意:1. javaWeb三大组件:Filter、Servlet、Listener

       2. Filter 程序可以拦截 Jsp, Servlet, 静态图片文件和静态 html 文件。

    Servlet API中提供了一个Filter接口,如果编写额类实现了这个接口,则称这个类为过滤器。Filter接口源码如下:

    package javax.servlet;
    import java.io.IOException;
    public interface Filter {
        public void init(FilterConfig filterConfig) throws ServletException;
        public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;
        public void destroy();
    }

    接口里有三个抽象方法,分别是init() 初始化  ,   doFilter()  执行过滤   和destory()销毁。

    2. 过滤器的特点     请记住:一般处理方式是放行,转发

        1. 以常规方式调用资源(,调用servletJSP页面)

        2. 利用修改过的请求信息调用资源

        3. 调用资源,但在发送响应到客户机前对其进行修改,修改响应

        4. 阻止该资源调用,代之以转到其他的资源,返回一个特定状态代码或生成替换输出

        5. 阻止资源调用,不转到其它资源(错误的情况)

    3. 过滤器的生命周期

    Filter的创建和销毁由WEB服务器负责在启动tomcat的时候就行创建过滤器,并且执行init方法,完成对象的初始化。filter对象只会创建一次,init方法也只会执行一次。通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。 每次拦截到都会去执行doFilter方法。

    1.  实例化———————————————–web.xml
    2.  初始化———————————————–init()
    3.  执行过滤——————————————–doFilter()
    4.  销毁————————————————–destory()

    1.实例化,执行构造方法。 :在web.xml中对过滤器进行配置和映射,也可以使用注解@WebFilter。

    2.初始化:web容器创建过滤器实例后将调用init方法,这个方法的参数FilterConfig对象可以获取web.xml文件中过滤器的初始化参数。在Filter的生命周期中,只会初始化一次。

    3.执行过滤:当用户访问的URL与web.xml中url-pattern配置的值一样时,就触发这个过滤器,web容器会先调用过滤器,过滤器会调用doFilter方法,这个方法的参数FilterChain对象可以调用doFilter方法,将请求传给下一个过滤器(过滤器链的情况下)或目标资源,也可以利用重定向或转发的方式将请求转发到其他资源。在Filter的生命周期中,可以执行0到n次过滤。

    4.销毁:web容器在销毁过滤器实例前调用这个方法(比如stop tomcat),在这个方法中可以释放过滤器占用的资源。在Filter的生命周期中,只会进行一次销毁。

        在web.xml中配置过滤器。这里要谨记一条原则:在web.xml中,监听器>过滤器>servlet。也就是说web.xml中监听器配置在过滤器之前,过滤器配置在servlet之前,否则会出错。

    <!--配置过滤器--> 
    <filter> 
        <filter-name>FilterTest</filter-name> 
        <filter-class>com.codeliu.FilterTest</filter-class> //类的全限定名(通过反射去创建这个过滤器对象)
        <init—param> //可选 
                <param—name>参数名</param-name>//过滤器初始化参数
                <param-value>参数值</param-value>  
        </init—pamm>  
    </filter> 
    
    <!--映射过滤器--> 
    <filter-mapping> 
        <filter-name>FilterTest</filter-name>  
        <url-pattern>/*</url-pattern>
        <dispatcher>请求的类型</dispatcher> 
    </filter-mapping>

    在配置中需要注意的有三处:<filter-class>(类的全限定名(通过反射去创建这个过滤器对象)

                 <url-pattren>要拦截的资源路径  

                  <dispatcher>请求的类型

    <url-pattren>一般有以下规则:

        1:作用与所有web资源:<url—pattern>/*</url-pattern>。则客户端请求访问任意资源文件时都要经过过滤器过滤,通过则访问文件,否则拦截。

        2:作用于某一文件夹下所有文件:<url—pattern>/dir/*</url-pattern>

        3:作用于某一种类型的文件:<url—pattern>*.扩展名</url-pattern>。比如<url—pattern>*.jsp</url-pattern>过滤所有对jsp文件的访问请求。

        4:作用于某一文件夹下某一类型文件:<url—pattern>/dir/*.扩展名</url-pattern>

       如果一个过滤器需要过滤多种文件,则可以配置多个<filter-mapping>,一个mapping定义一个url-pattern来定义过滤规则。

    <filter>
          <filter-name>loginFilter</filter-name>
          <filter-class>com.ygj.control.loginFilter</filter-class>
      </filter>
      <filter-mapping>
          <filter-name>loginFilter</filter-name>
          <url-pattern>*.jsp</url-pattern>
      </filter-mapping>
      <filter-mapping>
          <filter-name>loginFilter</filter-name>
          <url-pattern>*.do</url-pattern>
      </filter-mapping>
    

     

    <dispatcher>请求的类型,四个取值,分别为 REQUEST | INCLUDE | ORWARD | ERROR,不填时默认为 REQUEST。
      
    1. REQUEST:当用户直接访问页面时,web容器将会调用过滤器,如果目标资源是通过请求转发(request.getRequestDisPatcher)的include方法或forward方法进行访问,那么该过滤器就不会被调用。
    2. INCLUDE:如果目标资源是通过request.getRequestDisPatcher的include方法进行访问,那么该过滤器将会被调用,其他情况下,不会被调用。
    3. FORWAED:如果目标资源是通过request.getRequestDisPatcher的forward方法进行访问,那么该过滤器将会被调用,其他情况下,不会被调用。
    4. ERROR:如果目标资源是通过声明或异常处理机制调用,那么该过滤器将会被调用,除此之外,不会被调用。
    使用注解的方式 (属性urlPatterns指定要过滤的URL模式,也可以用属性value来指定。)
    @WebFilter(urlPatterns = {"/*"},  
               initParams = {@WebInitParam(name = "noFilterPath", value = "login.jsp;LoginServlet;fail.jsp", description = "不触发该过滤器的页面"),
           @WebInitParam(name = "charset", value = "UTF-8")})
    
    

     

    4. 过滤器初始化配置(FilterConfig)

    FilterConfig对象提供对servlet环境及web.xml文件中指派的过滤器名的访问

    常用方法:

    String getFilterName():得到filter的名称。
    String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
    Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
    public ServletContext getServletContext():返回Servlet上下文对象的引用。
    

      

    5.过滤器的执行过程

    滤器链(FilterChain)    

      过滤器对象使用FilterChain对象调用过滤器链中的下一个过滤器,如果该过滤器是链中最后一个过滤器,那么将调用目标资源。

     chain.doFilter(request, response);//放行,通过了当前过滤器,递交给下一个filter进行过滤,写在doFilter ()方法体内最后一行。

    上图是一个滤器链(FilterChain)的执行过程,所谓过滤器链,就是当多个过滤器的URL相同时,就形成了过滤器链。那么在过滤器链里每个过滤器的执行顺序是怎么样的呢?有两种情况:

        1. 如果你是在web.xml中配置的过滤器,那么执行时就按照你配置的<filter-mapping> 顺序进行执行。

                   2. 如果你是通过注解的方式配置的过滤器,那么执行时就按照首字母的大小进行执行,首字母相同看第二个字母。比如我在过滤器链里一个过滤器为TestFilter,一个过滤器为MyFilter,那显然先执行MyFilter再执行TestFilter。

     6.过滤器解决字符编码

    方案一:直接写一个过滤器,在过滤器中的request对象上设置字符编码即可(硬编码方式,通用性不好,后期修改麻烦)

    方案二:编码写到web.xml,过滤器读取编码,当请求中不带编码,咱们才进行自己的编码(设置到request对象即可),并且提供一个force参数确定是否强制使用自己设置的编码

       分析:force如果为true,则全部使用只剩配置好的编码

                  如果为false, 则对自带的编码不进行修改(默认)

    <filter>
    	<filter-name>encoding</filter-name>
    	<filter-class>cn.itsource._02_encoding.EncodingFilter</filter-class>
    	<!-- 设置我们自己的编码 -->
    <init-param>
    	<param-name>encoding</param-name>
    	<param-value>UTF-8</param-value>
    </init-param>
    <!-- 是否强制使用该编码 -->
    	<init-param>
    		<param-name>force</param-name>
    		<param-value>true</param-value>
    	</init-param>
    </filter>
    <filter-mapping>
    	<filter-name>encoding</filter-name>
    	<url-pattern>/*</url-pattern>
    </filter-mapping>	
    
    //编码,会从xml中读取
    private String encoding;
    
    //是否强制使用我们自己的编码
    private boolean force = false;
    
    @Override
    public void init(FilterConfig config) throws ServletException {
    	this.encoding = config.getInitParameter("encoding");
    	this.force = Boolean.valueOf(config.getInitParameter("force")); 
    }
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
    		FilterChain chain) throws IOException, ServletException {
    	//请求中没有编码,但是我们有自己的编码要设置
    	//请求中有编码,但是我们要强制设置编码
    	if((req.getCharacterEncoding()==null || force) && hasLength(encoding)){
    		req.setCharacterEncoding(encoding);
    	}
    	chain.doFilter(req, resp);
    }
    
    private boolean hasLength(String str){
    	return str!=null && !"".equals(str.trim());
    }
    
    @Override
    public void destroy() {
    }
    

      

     7. 权限判断

    情景:系统中的某些页面只有在正常登录后才可以使用,用户请求这些页面时要检查 session 中有无该用户信息,但在所有必要的页面加上session的判断相当麻烦的事情

    解决方案:编写一个用于检测用户是否登录的过滤器,如果用户未登录,则重定向到指定的登录页面.(一般为回到登录页面)

    public void doFilter(ServletRequest arg0, ServletResponse arg1,
                FilterChain arg2) throws IOException, ServletException {
            
            HttpServletRequest request=(HttpServletRequest) arg0;
            HttpServletResponse response=(HttpServletResponse) arg1;
            HttpSession session=request.getSession();
            
            String path=request.getRequestURI();
            
            Integer uid=(Integer)session.getAttribute("userid");
            
            if(path.indexOf("/login.jsp")>-1){//登录页面不过滤
                arg2.doFilter(arg0, arg1);//递交给下一个过滤器
                return;
            }
            if(path.indexOf("/register.jsp")>-1){//注册页面不过滤
                arg2.doFilter(request, response);
                return;
            }
            
            if(uid!=null){//已经登录
                arg2.doFilter(request, response);//放行,递交给下一个过滤器
                
            }else{
                response.sendRedirect("login.jsp");
            }
    
        }

    由于上面的 路径, 用户的id 都是写死的,故引出下面的解决方案:

    解决方法:将写死的这三个东西改成配置文件中去。(虽然这个方案不错,但是约定大于配置

    web.xml

    <filter>
    	<filter-name>checkLoginFilter</filter-name>
    	<filter-class>cn.itsource._03_check.CheckLoginFilter</filter-class>
    	<!-- 不检查的url路径 -->
    	<init-param>
    		<param-name>unCheckUrls</param-name>
    		<param-value>/login.jsp,/login</param-value>
    	</init-param>
    	<!-- 用户存在session中的名称 -->
    	<init-param>
    		<param-name>loginSessionName</param-name>
    		<param-value>USER_IN_SESSION</param-value>
    	</init-param>
    	<!-- 权限检查失败后要返回的路径 -->
    	<init-param>
    		<param-name>backUrl</param-name>
    		<param-value>/login.jsp</param-value>
    	</init-param>
    </filter>
     	<filter-mapping>
     		<filter-name>checkLoginFilter</filter-name>
     		<!-- 对所有的请求都进行拦截 -->
    	<url-pattern>/*</url-pattern>
    </filter-mapping>
    

      

    //不受检查的资源
    private List<String> unCheckUrls;
    //用户存放在session中的名称 -
    private String loginSessionName;
    //登录失败后返回的路径
    private String backUrl;
    
    @Override
    public void init(FilterConfig config) throws ServletException {
    	//从配置中拿到相应的数据
    	String[] urls = config.getInitParameter("unCheckUrls").split(",");
    	this.unCheckUrls = Arrays.asList(urls);
    	this.loginSessionName = config.getInitParameter("loginSessionName");
    	this.backUrl = config.getInitParameter("backUrl");
    }
    
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
    		FilterChain chain) throws IOException, ServletException {
    	HttpServletRequest request =(HttpServletRequest)req;
    	HttpServletResponse response = (HttpServletResponse)resp;
    	//从Session中拿到用户
    	Object user = request.getSession().getAttribute(loginSessionName);
    	//这里可以拿到请求的uri 例:/login.jsp /login
    	String uri = request.getRequestURI();
    	//如果路径列表中不包含相应的路径,则进行权限检查
    	if(!unCheckUrls.contains(uri)){
    		//如果用户不存在,返回登录界面
    		if(user==null){
    			response.sendRedirect(backUrl);
    			return;
    		}
    	}
    	//代码放行
    	chain.doFilter(request, response);
    }
    

      

    8. 顺便介绍下:Arrays.asList()

    使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportOperationException异常
    说明:asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。

    asList的代码:
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }
    

      

    Java代码 
    public static void main(String[] args) {
    
        int[] data = {1,2,3,4,5};
    
        List list = Arrays.asList(data);
    
        System.out.println("列表中的元素数量是:" + list.size());
    
    }    
    asList接受的是一个泛型类型的长度可变的参数,再构造了一个ArrayList。
    然而基本数据类型是不支持泛型化的,但是数组支持,所以采用基本数据类型的数组转化后是将数组放入了构造的ArrayList中,长度是1。
     
    Java代码 复制代码收藏代码Arrays类鈥斺擜rrays.asList()方法使用
    Integer[] data = {1,2,3,4,5};
    
    List list = Arrays.asList(data);
    
    System.out.println("列表中的元素数量是:" + list.size());
    
    输出结果:
    列表中的元素数量是:5  

    说明编译器对Integer[] 处理不一样。传入过程中asList()方法实际是将Integer数组里的元素进行存储。

    9. 文字过滤

    需要的获取参数的时候可以完成敏感字过滤但是HttpServletRequest的实现类的getParameter方法本身不支持敏感字过滤.

    解决办法:在不改变HttpServletRequest默认实现类的基础之上,使用装饰模式增强getParameter方法,使之支持敏感字过滤.

      

    开始使用装饰模式来完成咱们的功能增强吧:

    public class MyHttpServletRequestWapper implements HttpServletRequest

    当我们创建这个类实现HttpServletRequest接口后会发现,它里面有太多的功能(方法)需要我们去实现。这个确实是很麻烦的一件事。

    不过幸好,sun公司早已经想到了这个问题,为我们准备了一个适配器 HttpServletRequestWrapper,让咱们只需要去关注咱们自己的getParameter方法即可:

    <form action="MessageServlet" method="post">
    	标题:<input type="text" name="title"/>
    	内容:<textarea rows="20" cols="20" name="content"></textarea>
    	<input type="submit" value="提交"/>
    </form>
    
    
    @WebServlet("/MessageServlet")
    public class MessageServlet extends HttpServlet {
    	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		String title = request.getParameter("title");
    		String content = request.getParameter("content");
    		System.out.println(title);
    		System.out.println(content);
    	}
    }
    

      

     FilterUtil.java 代码

    public class FilterUtil {
    private static String[] banChars= {"操","日","傻逼","sb","cnm","c","n","m"};//敏感字库
    	
    	public static String filter(String name) {
    		for (String  str: banChars) {
    			if(name.contains(str)){
    				String replaceContent = "";
    				for (int i = 0; i < str.length(); i++) {
    					replaceContent+="*";
    				}
    				name = name.replaceAll(str,replaceContent);
    			}
    		}
    		return name;
    	}
    }
    

      

    MyHttpServletRequestWapper完整代码

    public class MyHttpServletRequestWapper extends HttpServletRequestWrapper{
    
    	public MyHttpServletRequestWapper(HttpServletRequest request) {
    		super(request);
    	}
    	
    	//对getParameter功能进行增强
    	@Override
    	public String getParameter(String name) {
    		if("title".equals(name) || "content".equals(name)){
    			return  FilterUtil.filter(super.getParameter(name));//过滤字符方法 注意传的是name键对应的value
    		}
    		return super.getParameter(name);
    	}
    	
    }
    

    MessageFilter 完整代码

    @WebFilter("/*")
    public class MessageFilter implements Filter {
        @Override
        public void destroy() {
    
        }
        @Override
        public void doFilter(ServletRequest req, ServletResponse resp,FilterChain chain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest)req;
            HttpServletResponse response = (HttpServletResponse)resp;
            
            //将request进行替换
            HttpServletRequest wapper = new MyHttpServletRequestWapper(request);
            chain.doFilter(wapper, response);
        }
        @Override
        public void init(FilterConfig arg0) throws ServletException {
        }
    }
  • 相关阅读:
    mybatis-plus 错误 java.lang.NoClassDefFoundError
    MySQL+navicat-1064 Error解决方案
    cnblogs博客园修改网站图标icon
    python+pycharm+PyQt5 图形化界面安装教程
    vuex的安装与使用
    vue-router的安装和使用
    VUE-CLI3如何更改配置
    VUE-CL3创建项目
    VUE-CLI2的初始化项目过程
    vuecli脚手架的安装与配置
  • 原文地址:https://www.cnblogs.com/gshao/p/10356047.html
Copyright © 2011-2022 走看看