servlet、filter 和 listener 并称为web三大组件,本篇将讲解filter,并能通过filter经典的案例的实现加深学习。
关于servlet,可以看我之前的文章。 学习Servlet看这一篇就够了
Filter
filter与servlet在很多的方面极其相似,但是也有不同,例如filter和servlet一样都有三个生命周期方法,同时他们在web.xml中的配置文件也是差不多的、 但是servlet主要负责处理请求,而filter主要负责拦截请求,和放行。
1.概念
- 当客户端访问服务器资源的时候,中间会存在一些过滤器,可以将请求拦截下来,完成一些特殊的功能。
- 作用:一般用于完成通用的操作.比如:登录验证 统一编码处理 …
2.快速入门
1.步骤
-
定义一个类 实现一个接口Filter
-
重写接口中的方法
-
配置拦截路径(两种方式)
-
web.xml
<!--配置过滤器--> <filter> <filter-name>demo02</filter-name> <filter-class>com.huike.filter.FilterDemo02</filter-class> </filter> <filter-mapping> <filter-name>demo02</filter-name> <!--拦截路径--> <url-pattern>/hello.jsp</url-pattern> <!--拦截方式--> <dispatcher>FORWARD</dispatcher> </filter-mapping>
-
使用注解配置
@WebFilter("/*") //访问所有资源之前,都会经过该过滤器
-
2.filter细节
-
web.xml配置
-
过滤器执行流程:
- 发送请求消息时,经过过滤器,执行过滤器
- 过滤器放行后,执行放行后的资源
- 发送响应消息时,执行过滤器放行后的代码
-
过滤器生命周期方法:
- init():在服务器启动时,会创建Filter对象,然后调用init方法,只执行一次,主要用于资源的加载
- doFilter():拦截方法 每一次请求被拦截时,会执行,会执行多次
- destory():在服务器关闭时,Filter对象被销毁, 如果服务器正常关闭,则会执行Distory()方法,只会执行一次,用于释放资源
-
过滤器配置详解:
-
拦截路径配置
-
具体资源路径: /hello.jsp 只有访问特定资源时,过滤器才会执行
-
拦截目录: /user/* 访问user目录下的所有资源时,过滤器都会被执行
-
后缀名拦截; .jsp|.do 访问所有后缀名为jsp|do的资源时,过滤器都会被执行
-
拦截所有资源:/* 访问所有资源时,过滤器都会被执行
-
-
拦截方式配置:资源被访问的方式 拦截器
-
注解配置:
为注解设置 dispatcherTypes属性:
- REQUEST:默认值浏览器直接请求资源
- FORWARD:转发访问资源
- INCLUDE:包含访问资源
- ASYNC: 异步访问资源
- ERROR: 错误跳转资源
-
web.xml配置:
在标签体中配置 FORWARD标签即可
-
-
-
过滤器链(如果配置多个过滤器)
-
多个过滤器的执行顺序问题: 过滤器1 过滤器2
执行顺序:
- 过滤器1
- 过滤器2
- 资源执行
- 过滤器2
- 过滤器1
-
过滤器执行的先后顺序的规则:
-
注解配置:按照类名的字符串比较规则 值小的先执行
HelloFilter HiFilter
FilterDemo06 FilterDemo07
-
web.xml配置: 谁定义在前面,谁先执行
-
-
3.案例
3.1 使用过滤器filter实现登录验证
- 需求:
- 访问UserListBigDemo案例时,验证其是否登录
- 如果登陆过,直接放行到 index.jsp页面
- 如果没有登陆过,则强制跳转到login.jsp登录页面,提示: “您尚未登录,请先登录…”
//loginFilter
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//1.通过访问路径判断是否为登录相关的路径
//1.1:获取访问路径
HttpServletRequest httpReq = (HttpServletRequest)req;
// /UserListBigDemo/findUserByPageServlet
String requestURI = httpReq.getRequestURI();
System.out.println(requestURI);
//1.2存储包含登录相关的资源路径
List<String> list = new ArrayList();
list.add("/login.jsp");
list.add("/loginServlet");
list.add("/checkCodeServlet");
list.add("/css/");
list.add("/js/");
list.add("/fonts/");
for (String s:list) {
if (requestURI.contains(s)) {
//1.3含登录相关的资源路径的话就直接放行
chain.doFilter(req, resp);
return;
}
}
//不包含登录相关的资源路径
//2.登录过了,就不用重复登录了
//2.1获取到loginServlet中的user
LoginUser user = (LoginUser)httpReq.getSession().getAttribute("user");
//2.2如果之前登陆过,则user不为空,放行
if (user!=null){
chain.doFilter(req, resp);
}else{
//2.3没登陆过,跳转登录页面
req.setAttribute("login_msg", "您尚未登录,请先登录...");
req.getRequestDispatcher("/login.jsp").forward(req,resp);
}
}
@Override
public void destroy() {
}
}
3.2 敏感词汇过滤
@WebFilter("/*")
public class WordsFilter implements Filter {
private List<String> list = new ArrayList();
@Override
public void init(FilterConfig config) throws ServletException {
//1.将存储在txt文本文件中的不文明词汇读取出来
//txt文本放于src目录下
ServletContext servletContext = config.getServletContext();
String realPath = servletContext.getRealPath("/WEB-INF/classes/敏感词汇.txt");
try {
BufferedReader br = new BufferedReader(new FileReader(realPath));
String line = null;
//1.2将流中的内容存储到list中
while((line = br.readLine()) != null){
list.add(line);
}
br.close();
System.out.println(list);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//2.创建代理对象 增强getParameter方法 getParameterMap getParameterValues()
/**
* Proxy.newProxyInstance的三个参数含义:
* 1. 类加载器(固定写法):真实对象.getClass().getClassLoader()
* 2. 接口数组(固定写法):真实对象.getClass().getInterfaces()
* 3. 处理器: new InvocationHandler()
*/
ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(),
req.getClass().getInterfaces(), new InvocationHandler() {
/**
* 代理模式:生成动态代理对象 去增强真实对象
* @param proxy:代理对象
* @param method:代理对象调用的方法 所封装的对象
* @param args:代理对象调用的方法时,所传递的实际参数数组
* @return Object:返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//2.1增强getParameter方法
if ("getParameter".equals(method.getName())) {
System.out.println("getParameter执行了");
//2.1.1增强返回值
String value = (String) method.invoke(req, args);
if (value != null) {
for (String s : list) {
if (value.contains(s)) {
value = value.replaceAll(s, "**");
}
}
}
return value;
}
//2.2增强getParameterMap方法
if ("getParameterMap".equals(method.getName())) {
System.out.println("getParameterMap执行了");
//2.1.1增强返回值
Map<String, String[]> parameterMap = (Map<String, String[]>) method.invoke(req, args);
Set<String> keys = parameterMap.keySet();
for (String key:keys) {
String value = parameterMap.get(key)[0];
if (value!=null && !value.equals("")){
for (String s:list){
if (value.contains(s)){
parameterMap.get(key)[0] = value.replace(s, "**");
}
}
}
}
return parameterMap;
}
return method.invoke(req, args);
}
});
//3.放行
chain.doFilter(proxy_req, resp);
}
@Override
public void destroy() {
}
}