您的位置:首页 > 编程语言 > Java开发

java servlet过滤器简解及实例

2016-07-20 14:34 429 查看
在整个概念中,个人觉得有一篇文章写得不错,通俗易懂,这里就直接套用以上原博文,

一、概念:

Filter也称之为过滤器,它是Servlet技术中比较激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。



二、Filter简介

Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。简单说,就是可以实现web容器对某资源的访问前截获进行相关的处理,还可以在某资源向web容器返回响应前进行截获进行处理。

三、快速入门

1、新建一个类,实现Filter接口

2、实现doFilter()方法,打印一句话,来证明能够进行拦截

3、在web.xml中进行配置(参照Servlet配置)

4、访问一个页面,看看能不能拦截

[java] view
plain copy

package com.test.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;

public class Demo1Filter implements Filter {

private FilterConfig filterConfig;

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

System.out.println("Demo1过滤前");

System.out.println(filterConfig.getInitParameter("param1"));

chain.doFilter(request, response);//放行。让其走到下个链或目标资源中

System.out.println("Demo1过滤后");

}

public void init(FilterConfig filterConfig) throws ServletException {

System.out.println("初始化了");

this.filterConfig = filterConfig;

}

public void destroy() {

System.out.println("销毁了");

}

}

在web.xml中进行配置

[html] view
plain copy

<filter>

<filter-name>Demo1Filter</filter-name>

<filter-class>com.itheima.filter.Demo1Filter</filter-class>

<init-param>

<param-name>param1</param-name>

<param-value>value在这里呢</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>Demo1Filter</filter-name>

<url-pattern>/*</url-pattern>

<dispatcher>REQUEST</dispatcher> <!-- 没有配置dispatcher就是默认request方式的 -->

<dispatcher>FORWARD</dispatcher>

<dispatcher>ERROR</dispatcher>

<dispatcher>INCLUDE</dispatcher>

</filter-mapping>

四、Filter的应用场景

通过对filter过滤器的了解,可以得知在以下三种情况下可以做些处理:

1> 通过控制对chain.doFilter的方法的调用,来决定是否需要访问目标资源。

比如,可以在用户权限验证等等。判断用户是否有访问某些资源的权限,有权限放行,没权限不执行chain.doFilter方法。

2> 通过在调用chain.doFilter方法之前,做些处理来达到某些目的。

比如,解决中文乱码的问题等等。可以在doFilter方法前,执行设置请求编码与响应的编码。甚至可以对request接口进行封装装饰来处理get请求方式的中文乱码问题(重写相应的request.getParameter方法)。

3> 通过在调用chain.doFilter方法之后,做些处理来达到某些目的。

比如对整个web网站进行压缩。在调用chain.doFilter方法之前用类A对response对象进行封装装饰,重写getOutputStream和重写getWriter方法。在类A内部中,将输出内容缓存进ByteArrayOutputStream流中,然后在chain.doFilter方法执行后,获取类A中ByteArrayOutputStream流缓存数据,用GZIPOutputStream流进行压缩下。

五、Filter实现拦截的原理

Filter接口中有一个doFilter方法,当开发人员编写好Filter类实现doFilter方法,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前(服务器内部对资源的访问机制决定的),都会先调用一下filter的doFilter方法。

六、Filter生命周期

和Servlet一样Filter的创建和销毁也是由WEB服务器负责。不过与Servlet区别的是,它是1>在应用启动的时候就进行装载Filter类(与Servlet的load-on-startup配置效果相同)。2>容器创建好Filter对象实例后,调用init()方法。接着被Web容器保存进应用级的集合容器中去了等待着,用户访问资源。3>当用户访问的资源正好被Filter的url-pattern拦截时,容器会取出Filter类调用doFilter方法,下次或多次访问被拦截的资源时,Web容器会直接取出指定Filter对象实例调用doFilter方法(Filter对象常驻留Web容器了)。4>当应用服务被停止或重新装载了,则会执行Filter的destroy方法,Filter对象销毁。

注意:init方法与destroy方法只会直接一次。

七、Filter部署应用注意事项

1> filter-mapping标签中servlet-name与url-pattern。

Filter不仅可以通过url-pattern来指定拦截哪些url匹配的资源。而且还可以通过servlet-name来指定拦截哪个指定的servlet(专门为某个servlet服务了,servlet-name对应Servlet的相关配置)。

2> filter-mapping标签中dispatcher。

指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截。

REQUEST:

当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问或ERROR情况时,那么该过滤器就不会被调用。

INCLUDE:

如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。

FORWARD:

如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。

ERROR:

如若在A.jsp页面page指令中指定了error属性=examError.jsp,那么A.jsp中若出现了异常,会跳转到examError.jsp中处理。而在跳转到examError.jsp时,若过滤器配置了ERROR的dispather那么则会拦截,否则不会拦截。

总结:


一个filter 包括:

  1. 在servlet被调用之前截获;

  2. 在servlet被调用之前检查servlet request;

  3. 根据需要修改request头和request数据;

  4. 根据需要修改response头和response数据;

  5. 在servlet被调用之后截获.

  你能够配置一个filter 到一个或多个servlet;单个servlet或servlet组能够被多个filter 使用.几个实用的filter 包括:用户辨认filter,日志filter,审核filter,加密filter,符号filter,能改变xml内容的XSLT filter等.

====================================割割割======================

以上为文章原文,此处尊重原作者附上出处:http://blog.csdn.net/jzhf2012/article/details/8476997



表示自己一直处于仅仅只会用的级别,而不懂得原理不懂得由来,所以此处也只能是需要使用的代码块进行自我复习.

filter作用及原理上面其实已经讲得差不多(要我写还写不出来这么高大上的东西),那么我附上一个简单而实用的代码,

<!-- 自定义的过滤器  -->
<filter>
<filter-name>authorityFilter</filter-name>  <span style="font-family: Arial, Helvetica, sans-serif;">			</span>
<filter-class>cn.thinknet.filter.AuthorityFilter</filter-class><span style="font-family: Arial, Helvetica, sans-serif;"><!-- 自定义过滤器的位置 --></span>

<init-param>
<param-name>allowAuthorityURL</param-name><!-- 不需要过滤的地址 -->
<param-value>login.jsp,register.jsp,login.action,sendMail.action,register.action,getbackPwdOne.jsp,getbackPwdTwo.jsp,getbackPwdThree.jsp,getbackOne.action,getbackTwo.action,getbackThree.action,forget_pwd.action,getHomePageResult.action,index.jsp,index.html,userAuthen.action</param-value>
</init-param>

<init-param>
<param-name>authorityURL</param-name><!-- 只对指定过滤参数后缀进行过滤 -->
<param-value>.action,.jsp,.do</param-value>
</init-param>
<init-param>
<param-name>redirectPath</param-name><!-- 未通过跳转到登录界面 -->
<param-value>/wrtPlatformVt/wrt/login.jsp</param-value>
</init-param>
<init-param>
<param-name>disableFilter</param-name><!-- Y:过滤无效 -->
<param-value>N</param-value>
</init-param>
</filter>

<filter-mapping> <!-- 拦截所有的请求信息   如想通过扩展名匹配拦截。 如:.action后缀的请求 配置则为 *.action-->
<!-- 精确匹配  顾名思义精确到单个请求  如:/manage/login.action 配置则为/manage/login.action -->
<!-- 路劲匹配  通过*配符的方式进行相对匹配,如下的/* 则表示拦截所有信息 -->
<filter-name>authorityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


附上:servlet url-pattern匹配规则:http://zy19982004.iteye.com/blog/1751695

<init-param> 标签对应的是参数名和值,可以用于在init()时通过FilterConfig的对象 filterConfig.getInitParameter("参数名")获取。

附上:<init-param>标签的基本使用:http://blog.csdn.net/yakson/article/details/9203231

自定义过滤器代码:

package cn.thinknet.filter;

import java.io.IOException;
import java.io.PrintWriter;
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.HttpServlet;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import cn.thinknet.utils.others.AKKeysUtil;

/**
* 过滤器
*
*
*
*/
public class AuthorityFilter extends HttpServlet implements Filter
{
/**
*
*/
private static final long serialVersionUID = 4504557649329493897L;

public String[] allowAuthorityURLs;

public String[] authorityURLs;

public FilterConfig config;

/**
* 过滤不能访问的地址
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException
{
// 未登录需要跳转的地址
String redirectPath = config
.getInitParameter(AKKeysUtil.WEB_CONTEXT_REDIRECT_PATH);

// 过滤是否启用
boolean isEnable = true; // 过滤器可用
String disableStr = config
.getInitParameter(AKKeysUtil.WEB_CONTEXT_DISABLE_FILTER);
if (StringUtils.isNotEmpty(disableStr))
{
isEnable = disableStr.equals("N");
}
HttpServletRequest req = (HttpServletRequest) request;

// 判断过滤器是否启用
if (!isEnable)
{
filterChain.doFilter(request, response);
return;
}

// 需要过滤的后缀
String authorityURL = config
.getInitParameter(AKKeysUtil.WEB_CONTEXT_AUTHORITY_URL);
if (StringUtils.isNotEmpty(authorityURL))
{
authorityURLs = authorityURL.split(",");
}

// 判断当前的请求地址中是否存在需要过滤的后缀
if (authorityURL(req))
{
// 不需要过滤的地址
String allowAuthorityURL = config
.getInitParameter(AKKeysUtil.WEB_CONTEXT_ALLOW_AUTHORITY_URL);
if (StringUtils.isNotEmpty(allowAuthorityURL))
{
allowAuthorityURLs = allowAuthorityURL.split(",");
}

// 过滤不拦截的url
if (allowAuthorityURL(req))
{
filterChain.doFilter(request, response);
return;
} else
{
// 判断当前用户是否登录,没有登录直接跳转到登录页面
if (!relogin(redirectPath, response, req))
{
return;
}
}

// 最后对action与jsp进行权限校验
// if (authorityRequestAddress(req))
// {
// 【暂时不实现纵向越权控制】
filterChain.doFilter(request, response);
// }
// else
// {
// 没有权限时
// noAuthority();
// }
} else
{
// 例如js,image,css等文件不列入权限控制范围内
filterChain.doFilter(request, response);
}
}

@Override
public void init(FilterConfig filterConfig) throws ServletException
{
config = filterConfig;
// WebApplicationContext ctx = WebApplicationContextUtils
// .getWebApplicationContext(this.getServletContext());

// menuService = (MenuService) ctx.getBean("menuService");
}

/**
* 在未登陆的情况下允许访问的URL
*
* @return Boolean
*/
private boolean allowAuthorityURL(HttpServletRequest request)
{
boolean isAllow = false;
// 获得当前访问的地址
String current_url = request.getRequestURI();

if (ArrayUtils.isNotEmpty(allowAuthorityURLs))
{
for (String allowUrl : allowAuthorityURLs)
{
if (StringUtils.containsIgnoreCase(current_url, allowUrl))
{
isAllow = true;
break;
}
}
}
return isAllow;
}

/**
* 需要过滤的后缀
*
* @return Boolean
*/
private boolean authorityURL(HttpServletRequest request)
{
boolean isFilter = false;
if (ArrayUtils.isNotEmpty(authorityURLs))
{
for (String suffix : authorityURLs)
{
if (request.getRequestURI().indexOf(suffix) != -1)
{
isFilter = true;
break;
}
}
}
return isFilter;
}

/**
* 判断员工回话是否失效
*
* @param redirectPath
*            需要跳转的页面
* @param response
*            请求响应
*
* @param request
*            请求
*
* @throws IOException
*
* @return boolean 假:代表重新登录,真:代表session存在
*/
private boolean relogin(String redirectPath, ServletResponse response,
HttpServletRequest request) throws IOException
{
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
// 判断该用户是否存在session中,如果有直接进入当前action
if (null == request.getSession(true).getAttribute(
AKKeysUtil.USER_EMPLOY_SESSION_KEY))
{
// 跳转到登录界面
out.print("<script language='javascript'>alert('身份验证失效,请重新登录!');window.parent.location.href='"
+ redirectPath + "';</script>");
return false;
}

// 如果用户禁用掉cookie,则跳转到登录界面,提示用户启用cookie
Cookie[] cookies = request.getCookies();
if (null == cookies)
{
// 1.可能用户清除过cookie 2.可能是由于用户禁用了cookie 此时都会跳转到登录界面
// 跳转到登录界面
out.print("<script language='javascript'>alert('Cookie被清理或是已禁用,请尝试重新登录!');window.parent.location.href='"
+ redirectPath + "';</script>");
return false;
}
return true;
}
}


注意,AKKeysUtil这个是封装好的一个工具类,所有使用到的地方对应的均是 <init-param>标签的参数名,自行对应更改即可。

以上过滤器经测试是可用的,不过由于源码并不是我所写(公司前辈所留),所以这里只能是拿出来分享之说,servlet过滤器并不等同于拦截器,常用的拦截器莫过于框架所自带的,如:Struts2,spring mvc等。 如想达到权限认证(是否登陆、未登录返回登陆页面),通过框架的拦截器达到其效果,当然也可以直接使用servlet的filter,毕竟我就是直接使用它的。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息