Java Web之过滤器

java

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. 以常规方式调用资源(即,调用servlet或JSP页面)

    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代码 

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 {

}

}

 

以上是 Java Web之过滤器 的全部内容, 来源链接: utcz.com/z/390610.html

回到顶部