JavaWeb过滤器
过滤器是一个服务器端的组件,它可以截取用户端的请求与响应信息,并对这些信息过滤。
过滤器的工作原理
不存在过滤器的情况下,用户直接访问Web资源。
存在过滤器的情况下:
过滤器的生命周期
创建第一个过滤器程序
创建 FirstFilter.java 实现 java.servlet.Filter 接口package com.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 FirstFilter implements Filter {
public void destroy() {
/**
* Web容器在销毁过滤器实例前调用该方法,在这个方法中可以释放过滤器中占用的资源。
*/
}
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
/**
* 这个方法完成实际的过滤操作,这个地方是过滤器的核心方法,当用户请求访问与过滤器关联的URL时,Web容器将先调用过滤器的doFilter方法。
* FilterChain参数可以调用chain。doFilter方法,将请求传给下一个过滤器(或目标资源),或利用转发、重定向将请求转发到其他资源。
*/
}
public void init(FilterConfig arg0) throws ServletException {
/**
* 这是过滤器的初始化方法,Web容器创建过滤器实例后将调用这个方法,这个方法可以读取Web。xml文件中过滤器的参数
*/
}
}
Web.xml中的配置
过滤器可以改变用户请求的路径。
过滤器不能直接返回数据,处理用户请求。
过滤器链
当有多个过滤器处理一个用户请求的时候,服务器会按照web.xml中过滤器定义的先后顺序组装成一条链。
过滤器链执行顺序案例:
FirstFilter.java
package com.filter;
public class FirstFilter implements Filter {
public void destroy() {
System.out.println("onDestroy");
}
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
System.out.println("start ---do first filter");
arg2.doFilter(arg0, arg1);
System.out.println("end-----do first filter");
}
public void init(FilterConfig arg0) throws ServletException {
System.out.println("init");
}
}
SecondFilter.java
package com.filter;
import java.io.IOException;
public class SecondFilter implements Filter {
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
System.out.println("start do second Filter");
arg2.doFilter(arg0, arg1);
System.out.println("end do secondfilter");
}
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
Web.xml
<filter>
<filter-name>FirstFilter</filter-name>
<filter-class>com.filter.FirstFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>FirstFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>SecondFilter</filter-name>
<filter-class>com.filter.SecondFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecondFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
两个过滤器同时过滤一个请求的结果如下:
过滤器的分类 Dispatch
Request | 用户直接访问页面时,Web容器会调用过滤器 |
Forward | 目标资源是通过RequestDispatcher的 forward访问时,该过滤器会被调用。 |
Include | 目标资源是通过RequestDispatcher的include方法调用时,过滤器被调用。 |
Error | 目标资源是通过声明式异常处理机制调用时,过滤器将被调用。 当跳转到error.jsp 时 ErrorFilter 将被调用。 <error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page> <filter> <filter-name>ErrorFilter</filter-name> <filter-class>com.filter.ErrorFilter</filter-class> </filter> <filter-mapping> <filter-name>ErrorFilter</filter-name> <url-pattern>/error.jsp</url-pattern> <dispatcher>ERROR</dispatcher> </filter-mapping> |
ASYNC | Servlet 3.0 的,支持异步处理 当跳转到Servlet,Servlet会处理一些用户业务,这个业务非常耗时,那么用户就要等待,用户体验不好。 3.0 增加了 @WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。 3.0 声明一个过滤器,无需在xml中配置 @WebFilter(servletNames ={“SimpleServlet”),filterName=”SimpleFilter”} Public class Servelt3Filter implements Filter{…} |
@WebFilter的常用属性
属性名 | 类型 | 描述 |
FilterName | String | 指定过滤器的name属性,等价于<filter-name> |
value | String[] | 该属性等价于urlPatterns属性,但是两者不应该同时使用。 |
urlPatterns | String[] | 指定一组过滤器的URL匹配模式。等价于<url-pattern>标签、 |
servletNames | String[] | 指定过滤器将应用于哪些Servlet。取值是@WebServlet中的name属性的取值,或者是web.xml 中 <servlet-name> 的取值 |
dispatcherTypes | DispatcherType | 指定过滤器的转发模式 ASYNC ERROR FORWARD INCLUDE REQUEST |
initParams | WebInitParam[] | 指定一组过滤器初始化参数,等价于<init-param>标签 |
asyncSupported | Boolean | 声明过滤器是否支持异步操作模式,等价于<async-supported> 标签 |
description | String | 该过滤器的描述信息,等价于<description>标签 |
displayName | String | 该过滤器的显示名,通常配合工具使用,等价于<display-name>标签 |
异步过滤器案例:
创建一个AsyncFilter.java 过滤器@WebFilter(filterName="AsyncFilter",value={"/servlet/AsyncServlet"},asyncSupported=true,dispatcherTypes={DispatcherType.ASYNC})
public class AsyncFilter implements Filter {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
// TODO Auto-generated method stub
System.out.println("dofilter before");
arg2.doFilter(arg0, arg1);
System.out.println("dofilter after");
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
创建一个Servlet 用来处理一些耗时操作
public class AsyncServlet extends HttpServlet {
public AsyncServlet() {
super();
}
public void destroy() {
super.destroy(); // Just puts "destroy" string in log
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//通过这个对象在线程中过去reqeust和reponse
System.out.println("Servlet 执行开始");
AsyncContext context = request.startAsync();
new Thread(new Executor(context)).start();
System.out.println("Servelt 执行结束");
}
public class Executor implements Runnable{
private AsyncContext mContext;
public Executor(AsyncContext context){
this.mContext = context;
}
@Override
public void run() {
// 执行复杂任务
try {
// mContext.getRequest();
// mContext.getResponse();
Thread.sleep(1000*10);
System.out.println("业务执行完成时间"+new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
public void init() throws ServletException {
// Put your code here
}
}
在web.xml 中配置Servlet
注意 需要配置async-supported 为true
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>AsyncServlet</servlet-name>
<servlet-class>com.servlet.AsyncServlet</servlet-class>
<async-supported>true</async-supported>
</servlet>
执行结果:
根据顾虑器的规则,过滤器需要等待servlet执行结束才能停止,所以如果servlet执行很久,过滤器需要等待很久,为了让过滤器早点结束,让servlet在后台执行,使用异步过滤器。
过滤器实际应用场景
对用户请求进行统一认证 |
编码转换 |
对用户发送的数据进行过滤替换 |
转换图像格式 |
对响应的内容进行压缩 |
案例:登录过滤与字符编码
Login.jsp 登录界面
<%@ page language="java" import="java.util.*"
contentType="text/html; charset=utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<form action="<%=request.getContextPath() %>/servlet/LoginInServlet" method="post">
用户名:<input type="text" name="username" /><br />
密码:<input type="password" name="password"><br />
<input type="submit" value="提交" />
</form>
</body>
</html>
<%@ page language="java" import="java.util.*"
contentType="text/html; charset=utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
恭喜你,登录成功 欢迎你 <%=request.getSession().getAttribute("username") %>
</body>
</html><strong>
</strong>
登录失败界面 fail.jsp
<%@ page language="java" import="java.util.*"
contentType="text/html; charset=utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
Sorry,登录失败
</body>
</html><strong>
</strong>
LoginInServlet.java 用来处理登录并且保存session
package com.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInServlet extends HttpServlet {
/**
* Constructor of the object.
*/
public LoginInServlet() {
super();
}
/**
* Destruction of the servlet. <br>
*/
public void destroy() {
super.destroy(); // Just puts "destroy" string in log
// Put your code here
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println(username);
if("admin".equals(username) && "admin".equals(password)){
//校验成功
HttpSession session = request.getSession();
session.setAttribute("username", username);
response.sendRedirect(request.getContextPath()+"/success.jsp");
}else{
//校验失败
response.sendRedirect(request.getContextPath()+"/fail.jsp");
}
}
/**
* Initialization of the servlet. <br>
*
* @throws ServletException if an error occurs
*/
public void init() throws ServletException {
// Put your code here
}
}
LoginInFilter.java 用来处理过滤请求
package com.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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInFilter implements Filter {
//获取web.xml 中的过滤器的配置信息
private FilterConfig config = null;
@Override
public void destroy() {
}
@Override
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 noLoginPaths = config.getInitParameter("noLoginPath");
//处理中文乱码问题
String charSet = config.getInitParameter("charSet");
if(charSet == null){
charSet = "UTF-8";
}
request.setCharacterEncoding(charSet);
if(noLoginPaths != null){
//循环遍历,如果请求中包含了不进行过滤的参数,则放行
String[] strArray = noLoginPaths.split(";");
for(int i = 0;i<strArray.length;i++){
if(strArray[i]!=null || "".equals(strArray[i])) continue;
if(request.getRequestURI().indexOf(strArray[i]) != -1){
arg2.doFilter(arg0, arg1);
return;
}
}
}
//下面这种做法不好,如果有多个需要放行的请求,则需要一直修改代码
/*if(request.getRequestURI().indexOf("login.jsp")!=-1
|| request.getRequestURI().indexOf("LoginInServlet")!= -1){
arg2.doFilter(arg0, arg1);
return;
}*/
if(session.getAttribute("username")!= null){
arg2.doFilter(arg0, arg1);
}else{
response.sendRedirect("login.jsp");
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
config = arg0;// 初始化配置的参数
}
}
web.xml 配置过滤器和Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>LoginInFilter</display-name>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>LoginInServlet</servlet-name>
<servlet-class>com.servlet.LoginInServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginInServlet</servlet-name>
<url-pattern>/servlet/LoginInServlet</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>LoginInFilter</filter-name>
<filter-class>com.filter.LoginInFilter</filter-class>
<init-param>
<param-name>noLoginPath</param-name>
<param-value>login.jsp;fail.jsp;LoginInServlet</param-value>
</init-param>
<init-param>
<param-name>charSet</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>LoginInFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
使用过滤器可以对某些非法请求进行过滤,比如,用户没有登录就直接进入 success.jsp 页面,这都是不允许的,使用过滤器可以进行过滤。过滤所有请求使用/* 不需要过滤的请求我们可以在FilterConfig 中配置参数,进行放行。可以在过滤器中设置所有请求的编码,防止乱码。