过滤器
场景1: request.getParameter("参数名");
遇到参数内容中文乱码问题,解决方法:
- post提交: request.setCharacterEncoding("utf-8");
- get提交: 手动解码 name = new String(name.getBytes("iso-8859-1"),"utf-8");
问题: 每个请求都需要servlet进行上面代码的填写,如何把这些重复的操作抽取出来呢?
使用过滤器,将重复的操作代码写在过滤器中
场景2: 登录页面=>输入用户名或密码=>后台检查是否成功=>登录成功,看到用户首页
(用户名:xxx)登录数据放在session域对象中,如果用户不登录,用户首页看不到:
//判断用户登录权限代码:
HttpSession session = request.getSession(false);
if(session == null){
跳转到登陆页面
}else{
String user = (String)sessin.getAttribute("user");
if(user == null){
跳转到登录页面
}
}
//////////////////////////////////////////////////////
//用户资源修改页面(需要用户登录才能访问),同样需要上面的代码进行判断
问题: 如何把这些登录权限代码抽取出?
使用过滤器,把这些登录权限代码写在过滤器中
过滤器简介
1)过滤器就是一个Filter接口,在javax.servlet.Filter
2)过滤器是servlet的三大组件之一.servlet的三大组件:
(servlet)servlet接口:javax.servlet.Servlet;作用:用于开发动态网页
(过滤器)Filter接口:javax.servlet.Filter;作用:
(监听器)Listener接口:javax.servlet.*
servlet组件的特点:
- 把组件配置到web.xml文件中
- 组件就可以交给tomcat服务器运行
3)作用:
过滤器的作用,就是一个实现了Filter接口的对象,这个对象可以在请求资源(可能是动态网页或者静态网页)时,或者在响应资源时,或者在请求和响应资源时,执行过滤任务.
过滤器的生命周期
构造方法: 创建过滤器对象的时候调用,在加载当前项目时加载过滤器,只调用一次.单例的多线程.
init方法: 创建完过滤器对象后调用,只调用一次.
doFilter方法: 过滤任务方法.会调用n次,每次访问目标资源时被调用
destory方法: 在销毁过滤器对象的时候调用,在web项目重新部署或tomcat服务器停止的时候销毁过滤器对象.
映射配置
精确过滤: /xxx
模糊过滤: /*
*.后缀名
FilterConfig对象
和servletConfig对象类似,这个FilterConfig对象加载初始化参数内容
FilterChain
过滤器链: 当一个资源被多个过滤器所过滤,那么就形成了一个过滤器链
下面通过代码进行说明以上的一些问题:
过滤器的编写
/*输出语句中的序号代表发生顺序*/
//需要执行的servlet,需要被过滤的servlet
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TargetServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("4执行了servlet");
response.getWriter().write("6用户看到响应内容");
}
}
//过滤器的编写
import java.io.IOException;
import java.util.Enumeration;
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 FirstFilter() {
System.out.println("1过滤器创建了");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("2过滤器初始化");
// filterConfig对象封装了所有的当前过滤器配置的初始化参数
System.out.println(filterConfig.getInitParameter("AAA"));
Enumeration<String> enums = filterConfig.getInitParameterNames();
while (enums.hasMoreElements()) {
String paramName = enums.nextElement();
String paramValue = filterConfig.getInitParameter(paramName);
System.out.println(paramName + "=" + paramValue);
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("3过滤器正在执行过滤任务");
// 放行
chain.doFilter(request, response);
System.out.println("5过滤器正在执行任务--过滤响应");
}
// 过滤器对象销毁的时候才会调用;web项目重新部署或者tomcat服务器停止了才会销毁过滤器对象
@Override
public void destroy() {
System.out.println("过滤器被销毁了");
}
}
//web.xml的部分内容
<!-- 配置一个过滤器 -->
<!-- 过滤器配置 -->
<filter>
<filter-name>FirstFilter</filter-name>
<filter-class>cn.itcast.filter.FirstFilter</filter-class>
<!-- 初始化参数 -->
<init-param>
<param-name>AAA</param-name>
<param-value>AAA' value</param-value>
</init-param>
<init-param>
<param-name>BBB</param-name>
<param-value>BBB' value</param-value>
</init-param>
</filter>
<!-- 过滤器映射配置 -->
<filter-mapping>
<filter-name>FirstFilter</filter-name>
<!-- 代表的是要过滤的路径,而不是访问过滤器的路径 -->
<url-pattern>/target</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>TargetServlet</servlet-name>
<servlet-class>cn.itcast.filter.TargetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TargetServlet</servlet-name>
<url-pattern>/target</url-pattern>
</servlet-mapping>
过滤器链
/*过滤器链的说明*/
//被过滤的servlet
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FirstServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("3执行目标资源代码");
}
}
//两个过滤器的编写
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 {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("1执行第一个过滤器的请求过滤");
// 放行
// FilterChain就是过滤器对象,其中doFilter就是把请求或响应交给下一个过滤器,如果没有下一个,就访问目标资源或返回给用户显示
// 过滤器链的过滤执行顺序由web.xml文件中过滤器的filter-mapping的顺序所决定的,先配置的先访问
chain.doFilter(request, response);
System.out.println("5执行第一个过滤器的响应过滤");
}
@Override
public void destroy() {
}
}
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 SecondFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("2执行第二个过滤器的请求过滤");
chain.doFilter(request, response);
System.out.println("4执行第二个过滤器的响应过滤");
}
@Override
public void destroy() {
}
}
//web.xml部分内容
<filter>
<filter-name>First</filter-name>
<filter-class>cn.itcast.chain.FirstFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>First</filter-name>
<url-pattern>/First</url-pattern>
</filter-mapping>
<filter>
<filter-name>Second</filter-name>
<filter-class>cn.itcast.chain.SecondFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Second</filter-name>
<url-pattern>/First</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>cn.itcast.chain.FirstServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/First</url-pattern>
</servlet-mapping>
post get乱码的解决方案
//参数中文乱码的过滤器
import java.io.IOException;
import java.io.UnsupportedEncodingException;
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.HttpServletRequestWrapper;
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
// 强制转换
HttpServletRequest request = (HttpServletRequest) req;
// post提交参数乱码解决方案
request.setCharacterEncoding("utf-8");
// 创建一个HttpServletRequest实现类的装饰者类,重写getParameter方法
MyHttpRequest myRequest = new MyHttpRequest(request);
// 放行,这里放行的应该是我们装饰后的对象
chain.doFilter(myRequest, resp);
}
@Override
public void destroy() {
}
}
// 装饰者类
class MyHttpRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;// 多态
public MyHttpRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
// 对get提交参数乱码处理
// 1得到原来的参数
String value = request.getParameter(name);// iso-8859-1
// 2手动解码
if ("GET".equals(request.getMethod())) {
try {
value = new String(value.getBytes("iso-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
return value;
}
}
//web.xml的配置信息这里省略
压缩网页内容的过滤器解决方案
用户浏览一个网页: 服务器=>发送一个网页的内容给用户(1k)
一个用户一天访问10页,服务器输出10kb内容
网站1天10万用户:
100 000 * 10kb = 1 000 000kb = 1GB 消耗网络带宽
要求:在不影响用户浏览效果前提下,减少服务器输出的数据.用到压缩网页的技术,gzip压缩技术
//需要被拦截的servlet
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ContentServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 2
// 准备内容
StringBuffer sb = new StringBuffer();
for (int i = 0; i <= 3000; i++) {
sb.append("abcd");
}
/*
* 思路: response对象的getWriter()方法得到的是一个没有缓冲功能的PrintWriter(),
* 直接调用write()方法,就是直接把内容输出到浏览器显示
* 如果我们通过改造response对象的getWriter()方法,从而得到一个带有缓冲功能的PrintWriter()对象,
* 那么write写出的网页内容就是写到缓冲区中,我们就可以从PrintWriter的缓冲区中得到网页内容
*/
/* 每次写出的网页内容都是已经经过gzip压缩的内容 */
response.getWriter().write(sb.toString());
}
}
//编写的带有网页压缩技术的过滤器,这里也有一个装饰者模式,用来增强getWriter方法
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;
//集中对网页内容进行gzip压缩
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.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class GZIPFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 1过滤请求
// 创建一个response的装饰者对象
MyHttpResponse myResponse = new MyHttpResponse(
(HttpServletResponse) response);
// 放行
chain.doFilter(request, myResponse);
// 3过滤响应
// 得到压缩前的内容;然而response中没有方法获取实体内容
// 从缓存容器对象得到压缩前的内容
char[] content = myResponse.getCharArray();
// gzip压缩
// 创建一个临时缓存容器
ByteArrayOutputStream buf = new ByteArrayOutputStream();
// 创建对象
GZIPOutputStream gzip = new GZIPOutputStream(buf);
// 进行压缩
gzip.write(new String(content).getBytes());
// 调用结束方法,把缓存内容刷新
gzip.finish();
// 得到压缩后的内容
byte[] result = buf.toByteArray();
/* 现在的内容已经是经过gzip算法压缩,必须要告诉浏览器目前输出的内容是gzip压缩格式的内容 */
// 告诉浏览器发送内容的压缩格式
myResponse.setHeader("content-encoding", "gzip");
// 输出
response.getOutputStream().write(result);
}
@Override
public void destroy() {
}
}
/**
* HttpServletResponse的装饰者模式
*/
class MyHttpResponse extends HttpServletResponseWrapper {
private HttpServletResponse response;
// 定义一个缓冲容器对象
private CharArrayWriter charArray = new CharArrayWriter();
// 提供一个获取charArray内容的方法(包含网页内容)
public char[] getCharArray() {
return charArray.toCharArray();
}
public MyHttpResponse(HttpServletResponse response) {
super(response);
this.response = response;
}
// 重写getWriter()方法,让其返回一个带缓存功能的PrintWriter
@Override
public PrintWriter getWriter() throws IOException {
return new PrintWriter(charArray);
}
}
//web.xml省略
登录权限的过滤问题
login.jsp登录界面省略
LoginServlet登录判断的代码
import java.io.IOException;
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 LoginServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("userName");
String pwd = request.getParameter("userPwd");
if ("james".equals(name) && "12345".equals(pwd)) {
HttpSession session = request.getSession(true);
session.setAttribute("user", name);
response.sendRedirect(request.getContextPath()
+ "/user/indexuser.jsp");//欢迎界面
} else {
request.setAttribute("msg", "用户名或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request,
response);//
}
}
}
SecurityFilter登录权限过滤器
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 SecurityFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
// 强制转换
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 是否登录判断逻辑
// 先判断有无session对象存在
HttpSession session = request.getSession(true);
if (session == null) {
// 没有登录
response.sendRedirect(request.getContextPath() + "/noAuth.jsp");
return;
} else {
String user = (String) session.getAttribute("user");
if (user == null) {
// 没有登录成功
response.sendRedirect(request.getContextPath() + "/noAuth.jsp");
return;
}
}
// 如果已经登录成功了,则放行
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
web.xml的部分编写
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>cn.itcast.cases.SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<!-- user文件夹下的都是和登录之后相关的页面,这里不需要将login.jsp和servlet放在过滤器拦截路径里 -->
<url-pattern>/user/*</url-pattern>
</filter-mapping>