Filter
一个实现了特殊接口(javax.servlet.filter )的Java类,实现对请求资源(Jsp,servlet,html,js,css,前两者属于动态资源)的过滤功能;是运行在服务器的程序,优先于Servlet或者jsp执行,过滤器是Javaweb中最为常用的功能。
(filter的功能比servlet更加强大,servlet可以干的活filter都可以干,获得参数,调用业务,分发和专项)
filter是对客户端访问资源的过滤,符合条件放行,不符合条件不放行,并且可以对目标资源访问前后进行逻辑处理
过滤器的作用:
对目标资源(Servlet,jsp)进行过滤
应用场景:自动登录,解决网站乱码,过滤敏感字符
api详解
Filter接口有三个方法,并且这个三个都是与Filter的生命相关的方法
* init(Filterconfig):代表filter对象初始化方法 filter对象创建时执行
* doFilter(ServletRequest ,ServletResponse ,FilterChain ):代表filter执行过滤的核心方法,如果某资源在已经被配置到这个filter进行过滤的话,那么每次访问这个资源都会执行doFilter方法
* destory():代表是filter销毁方法 当filter对象销毁时执行该方法
生命周期:
Filter何时创建:服务器启动时就创建该filter对象(服务器一启动就创建了 )
Filter何时销毁:服务器关闭时filter销毁
区别于Servlet的生命周期(Servlet生命周期很重要(面试经常遇到),Filter生命周期掌握就好了 )
对比:
Servlet(Servlet是单例多线程。 )
* init:默认情况下,第一次请求的时候(服务器启动的时候不一定调用,要有请求才调用)
* service:一次请求就调用一次
* destory:服务器正常关闭或者项目从服务器移除的时候
Filter
* init:服务器一启动就创建了
* doFilter:其他的和servlet的时候相同
* destory
Filter入门
编写步骤:
- 1)编写一个过滤器的类实现Filter接口
javax.servlet.filter
- 2)实现接口中尚未实现的方法(着重实现doFilter方法)
filterchain.doFilter(request , response ) //放行,相当于一个转发。
导致链中的下一个过滤器被调用,如果调用的过滤器是链中的最后一个过滤器,则导致调用链末尾的资源。
- 3)在web.xml中进行配置(主要是配置要对哪些资源进行过滤)(匹配方式有三种,完全匹配,目录匹配,扩展名匹配)filter拦不拦截取决于路径的配置
实际编写过程中,在eclipse之中可以直接new一个filter,可以不用配置;
案例: 实现自动登录
登录成功后保存用户的登录状态
分析思路:
只要登录成功都要保存登录状态,通用规则
/*就是拦截本工程中的所有网页,只要登录一次就可以访问本工程下的所有的页面了
1)登录成功之后,判断用户是不是已经勾选了自动登录
Login.servlet
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//首先判断验证码是不是正确
HttpSession session = request.getSession();
String rightCode = (String) session.getAttribute("code");
session.removeAttribute("code");
//System.out.println("rightCode:"+rightCode);
String userCode = (String) request.getParameter("userCode");
//System.out.println("userCode:"+userCode);
if(userCode == null || userCode.trim().equalsIgnoreCase("")){
request.setAttribute("msg", "请输入验证码");
request.getRequestDispatcher("/login.jsp").forward(request, response);
return;
}else if (!userCode.equalsIgnoreCase(rightCode)){
request.setAttribute("msg", "您输入的验证码不正确,请重新输入");
request.getRequestDispatcher("/login.jsp").forward(request, response);
return;
}
//获取登录参数
String username = request.getParameter("username");
String password = request.getParameter("password");
LoginService ls = new LoginService();
User user = null;
try {
user = ls.userLogin(username,password);
} catch (SQLException e) {
e.printStackTrace();
}
if(user!=null){
//如果登录成功
//保存用户登录状态
request.getSession().setAttribute("userInfo", user);
//获取参数auto,看用户是否勾选了自动登录
String auto = request.getParameter("auto");
//如果勾选了自动登录
if(auto != null && auto.equalsIgnoreCase("ok")){
//配置cookie的参数,然后写回到response中去
Cookie cookie = new Cookie("autoInfo", user.getUsername()+"-"+user.getPassword());
cookie.setMaxAge(60*60*24*7);
cookie.setPath(request.getContextPath());
response.addCookie(cookie);
}
//重定向
response.sendRedirect(request.getContextPath()+"/index.jsp");
}else{
response.getWriter().print("登录失败");
request.setAttribute("msg", "请确认您登录信息是否正确");
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
2)新建过滤器,拦截/*
AutoFilter.java
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
//强制类型转换
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//优化,判断如果已经是登录状态,就不需要自动登陆了,比如新开标签页
User sessionUser = (User) request.getSession().getAttribute("userInfo");
if(sessionUser == null){
//获得目标cookie,看是否已经登陆过了并且设置了自动登录才会有cookie
Cookie[] cookies = request.getCookies();
Cookie targetCookie = CookieUtils.getTargetCookie(cookies, "autoInfo");
//如果autoInfo不为空,就取出里面的数据进行登录
if(targetCookie != null){
String username = targetCookie.getValue().split("-")[0];
String password = targetCookie.getValue().split("-")[1];
System.out.println("username:"+username+",password:"+password);
//调用业务层进行用户登录
LoginAutoService ls = new LoginAutoService();
try {
User user = ls.LoginAuto(username,password);
System.out.println(user);
if(user != null){
//保存登录状态
request.setAttribute("userInfo", user);
}
} catch (SQLException e) {
System.out.println("登录失败,查询客户信息失败");
e.printStackTrace();
}
}
}
// 放行
chain.doFilter(request, response);
}
优化要点:
不关浏览器,重新开一个标签
判断如果已经是登录状态,就不需要自动登陆了
做一步判断,判断session中是不是已经存在了User了。
生命周期:
Servlet生命周期很重要(面试经常遇到),Filter生命周期掌握就好了
Servlet(Servlet是单例多线程。 )
init:默认情况下,第一次请求的时候(服务器启动的时候不一定调用,要有请求才调用)
service:一次请求就调用一次
destory:服务器正常关闭或者项目从服务器移除的时候
Filter
init:服务器一启动就创建了(项目启动的时候服务器创建filter的对象)
doFilter:每当请求来的时候,服务器截获一个线程,执行dofilter方法,实现过滤的逻辑
destory:当服务器移除filter的时候或者服务器正常关闭的时候,服务器调用destory方法实现销毁。
在Struts2中前端部署器(过滤器)会涉及到
FilterConfig(了解)
FilterConfig对象是在init方法里面有传入进去;
在web.xml里面配置(类似于Servlet的参数配置)
<filter>
<display-name>Test01Filter</display-name>
<filter-name>Test01Filter</filter-name>
<filter-class>Test01Filter</filter-class>
<init-param>
<param-name>username</param-name>
<param-value>jjizh</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Test01Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在init里面获得配置信息:
public void init(FilterConfig fConfig) throws ServletException {
String username = fConfig.getInitParameter("username");
System.out.println("filter初始化了,获得了配置参数username:"+username);
}
Filter的各种配置:
(1)filter的url-pattern的配置
三种匹配方法
* 完全路径匹配:以"/"开始 例如:/aa/bb
* 目录匹配:/开头,*结束 /a/* 可以匹配到 /a/b/c
* 扩展名匹配:以*开头,扩展名结尾 *.jsp *.do
和servlet不同的是:一个路径只能够对应一个servlet
filter:
一个路径可以对应多个filter
谁先谁后呢,就要引用过滤链的概念了
(2)Filter的其他配置:
<filter>
<display-name>Test01Filter</display-name>
<filter-name>Test01Filter</filter-name>
<filter-class>Test01Filter</filter-class>
<init-param>
<param-name>username</param-name>
<param-value>jjizh</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Test01Filter</filter-name>
<url-pattern>/aa/bb/cc</url-pattern>
<servlet-name>Test01</servlet-name>
</filter-mapping>
(3)dispatcher配置(重要)
告知服务器(Filter)如何拦截.有如下四种拦截类型
* - REQUEST:默认值,只过滤从浏览器发送过来的请求,不过滤转发
* - FORWARD:只过滤转发过来的请求 服务器内部的操作
* - INCLUDE:只过滤包含过来的请求(了解 )
* - ERROR:只过滤错误过来的请求(了解 )
(如果要过滤浏览器传输过来的请求的同时也过滤请求转发过来的请求,就两个配置都写上)
<filter>
<display-name>Test01Filter</display-name>
<filter-name>Test01Filter</filter-name>
<filter-class>Test01Filter</filter-class>
<init-param>
<param-name>username</param-name>
<param-value>jjizh</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Test01Filter</filter-name>
<url-pattern>/aa/bb/cc</url-pattern>
<servlet-name>Test01</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
细节:
上述写法:PrivilegesFilter先执行;
FilterChain过滤链(重要)
过滤器链作用:当一个filter收到请求的时候,调用chain.doFilter才可以访问下一个匹配的filter,若当前的filter是最后一 个filter,调用chain.doFilter才能访问目标资源
多个filter的执行顺序是由web.xml中filter-mapping的位置决定的.
过滤链的设计理念:责任链模式
返回的时候,怎么来怎么回去。
过滤链的先后顺序取决于配置filter-mapping的顺序
拓展:
全局错误页面设置,location转发到特定的页面
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>
案例二;统一get和post中文乱码的处理
需求分析:
在整个网站中,可能会有get请求或post请求向服务器提交参数.参数中往往有中文信息.在后台每个Servlet中都需要去处理乱码.
我们想做的是:无论get请求或者是post请求提交到Servlet中.就可以直接调用getParameter方法将乱码处理好.
在过滤器中进行处理CodeFilter
用增强getParameter方法,装饰设计思想
在到达目标资源之前对request中的getParameter方法进行增强
装饰设计思想:
增强类和被增强类需要实现相同的接口
增强类里面需要得到被增强类的引用(构造函数传)
对于需要增强的方法,写自己的逻辑;对于不需要增强的方法,直接调用之前的逻辑即可。
重写httpServletRequest太麻烦,因为里面的函数太多了
httpServletRequestWapper类里面实现了所有的方法,只需要重写所需要的方法就可以了
思路:
- 创建一个MyRequest继承HttpServletRequestWrapper,重写getParameter方法
在getParameter方法里面:
判断是get/post请求,根据不同的请求方式,写处理乱码的逻辑
- 创建一个CodeFilter, 路径/*, 在doFilter里面:
创建增强的MyRequest,把MyRequest放行
增强HttpServletRequestWrapper
public class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
public MyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name){
//判断是get请求还是post请求
String method = request.getMethod();
if(method.equalsIgnoreCase("get")){
String value = request.getParameter(name);
try {
value = new String(value.getBytes("iso8859-1"),"utf-8");
return value;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}else if(method.equalsIgnoreCase("post")){
try {
request.setCharacterEncoding("utf-8");
return request.getParameter(name);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return super.getParameter(name);
}
}
在过滤器中:创建一个CodeFilter, 路径/*, 在doFilter里面:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
MyRequest myRequest = new MyRequest(request);
chain.doFilter(myRequest, res);
}