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. 以常规方式调用资源(即,调用servlet或JSP页面)
2. 利用修改过的请求信息调用资源
3. 调用资源,但在发送响应到客户机前对其进行修改,修改响应
4. 阻止该资源调用,代之以转到其他的资源,返回一个特定状态代码或生成替换输出
5. 阻止资源调用,不转到其它资源(错误的情况)
3. 过滤器的生命周期
Filter的创建和销毁由WEB服务器负责。在启动tomcat的时候就行创建过滤器,并且执行init方法,完成对象的初始化。filter对象只会创建一次,init方法也只会执行一次。通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。 每次拦截到都会去执行doFilter方法。
- 实例化———————————————–web.xml
- 初始化———————————————–init()
- 执行过滤——————————————–doFilter()
- 销毁————————————————–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。
- REQUEST:当用户直接访问页面时,web容器将会调用过滤器,如果目标资源是通过请求转发(request.getRequestDisPatcher)的include方法或forward方法进行访问,那么该过滤器就不会被调用。
- INCLUDE:如果目标资源是通过request.getRequestDisPatcher的include方法进行访问,那么该过滤器将会被调用,其他情况下,不会被调用。
- FORWAED:如果目标资源是通过request.getRequestDisPatcher的forward方法进行访问,那么该过滤器将会被调用,其他情况下,不会被调用。
- 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