JAVAWEB开发之Servlet Filter(过滤器)详解包括post和get编码过滤器、URL访问权限控制、自动登录。以及装饰模式的使用

Filter简介

  •  Filter也称之为过滤器,它是Servlet技术中最实用的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如JSP,Servlet,静态图片文件或静态HTML文件进行拦截,从而实现一些特殊功能。例如实现URL级别的权限控制、过滤敏感词汇、压缩响应信息等一些高级功能。
  • ServletAPI中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个Java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前对访问的请求和响应进行拦截,如下所示:

Filter是如何实现拦截的?
Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源(拦截url)进行拦截后,web服务器每次在调用web资源之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
(1)调用目标资源之前,让一段代码执行。
(2) 是否调用目标资源(即是否让用户访问web资源)。
    web服务器在调用doFilter方法时,会传递一个  filterChain对象进来,FilterChain对象是filter接口中最重要的一个对     象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会       调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。
(3)调用目标资源之后,让一段代码执行。

Filter开发入门

Filter开发分为两个步骤:
步骤一:编写Java类实现Filter接口,并实现(三个方法)其doFilter方法。
步骤二:在web.xml中使用<filter>和<filter-mapping>元素对编写的filter类进行注册,并设置它所能拦截的资源。

Filter链——FilterChain
在一个Web应用中,可以编写多个Filter,这些Filter组合起来称之为一个Filter链。
web服务器根据Filter在web.xml文件中的注册顺序<filter-mapping>,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建第一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有则调用第二个filter,如果没有则调用目标资源。

Filter生命周期

init(FilterConfig  filterConfig) throws ServletException:
  • 和我们编写的Servlet程序一样,Filter的创建与销毁由WEB服务器负责。web应用程序启动时,web服务器将创建Filter的实例对象,并调用init方法进行初始化(注意:filter对象只会创建一次,init方法也只会执行一次
  • 开发人员通过init方法的参数,可以获得代表当前filter配置信息的FilterConfig对象。
doFilter(ServletRequest, ServletResponse, FilterChain):
  • 每次filter进行拦截都会执行
  • 在实际开发中方法中参数request和response通常转换为HttpServletRequestHttpServletResponse类型进行操作。
destroy():
    在web容器卸载Filter对象之前被调用

FilterConfig接口

用户在配置filter时,可以使用<init-param>为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的FilterConfig对象传递进来,因此开发人员在编写Filter时,通过FilterConfig对象的方法就可获得过滤器的初始化参数有关的信息以及获取ServletContext对象等信息:
  • String getFilterName():得到filter的名称。
  • String getInitParameter(String name):返回在部署描述中指定名称的初始化参数的值。如果不存在则返回null。
  • Enumeration  getInitParameterNames():返回过滤器中的所有初始化参数的名字的枚举集合。
  • public ServletContext getServletContext():返回Servlet上下文对象的引用。

注册与映射Filter

(1)注册Filter
<filter>
      <filter-name>testFitler</filter-name>
    <filter-class>org.test.TestFiter</filter-class>
    <init-param>
<param-name>word_file</param-name>
<param-value>/WEB-INF/word.txt</param-value>
    </init-param>
</filter>

  • <filter-name> 用于为过滤器指定一个名字,该元素的内容不能为空。
  • <filter-class> 元素用于指定过滤器的完整的限定类名。
  • <init-param> 元素用于用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名称,<param-value>指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
(2)映射Filter
<filter-mapping> 元素用于设置一个Filter所负责拦截的资源。一个Filter拦截的资源可以通过两种方式来指定:Servlet名称和资源访问的请求路径。
  • <filter-name> 子元素用于设置Filter的注册名称。该值必须是在<filter>元素中生命过的过滤器的名称。
  • <url-pattern> 设置filter所拦截的请求路径(过滤器关联的URL样式)。
  • <servlet-name>指定过滤器所拦截的Servlet名称。
  • <dispatcher> 指定过滤器所拦截的资源被Servlet容器调用的方式,可以是REQUEST,INCLUDE,FORWARD,和ERROR之一,默认是REQUEST。用户可以设置多个<dispatcher>子元素用来指定Filter对资源的多种调用方式进行拦截。
 注意:<url-pattern>有三种匹配方式和Servlet的配置方式类似
  • 绝对路径匹配:以/开头 不包含通配符 *  是一个绝对访问路径。例如:/demo、/index.jsp
  • 目录匹配:以/ 开头,以 * 结尾。例如:/*、/servlet/*、/servlet/xxx/*
  • 扩展名匹配:不能以/ 开头,也不能以*结尾 只能以后缀名结尾 例如:*.do、*.demo等
映射Filter的多种方式:
  • REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
  • INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
  • FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
  • ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
映射Filter示例:
<filter-mapping>
     <filter-name>testFilter</filter-name>
    <url-pattern>/test.jsp</url-pattern>
</filter-mapping>

<filter-mapping>
    <filter-name>testFilter</filter-name>
   <url-pattern>/index.jsp</url-pattern>
   <dispatcher>REQUEST</dispatcher>
   <dispatcher>FORWARD</dispatcher>
</filter-mapping>

代码解析Filter的使用及原理:

(1)创建Demo1Filter.java  实现Filter接口 并重写有关方法
package cn.itcast.web.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;
// Filter入门
// 1.创建一个类你,实现Filter接口
// 2.重写方法
// 3.在web.xml文件中配置

public class Demo1Filter implements Filter {

	@Override
	public void destroy() {

	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		System.out.println("进行过滤操作......");
		// 放行
		chain.doFilter(request, response);
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("Demo1Filter初始化创建....");
	}

}
配置Demo1Filter的注册与映射 在web.xml中添加如下配置:
<!-- Filter入门 -->
	<filter> 
		<filter-name>demoFilter</filter-name>
		<filter-class>cn.itcast.web.filter.Demo1Filter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>demoFilter</filter-name>
		<url-pattern>/index.jsp</url-pattern>  <!-- 访问index.jsp页面会被demoFilter过滤 -->
	</filter-mapping>

启动服务器 在后台日志打印中发现调用了init方法

访问主页(index.jsp)

(2)演示FilterChain链的执行顺序 以及Filter的过滤顺序。
图解如下:

创建FirstFilter过滤器
package cn.itcast.web.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 {

	@Override
	public void destroy() {

	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		System.out.println("firstFilter....");
		chain.doFilter(request, response);
		System.out.println("firstFilter end....");
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {

	}

}
SecondFilter
package cn.itcast.web.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 SecondFilter implements Filter {

	@Override
	public void destroy() {
		
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		System.out.println("secondFilter....");
		chain.doFilter(request, response);
		System.out.println("secondFilter end....");
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		
	}

}

创建demo.servlet
package cn.itcast.web.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 DemoServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("demo servlet");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}

在web.xml中添加如下配置:
<filter>
      <filter-name>firstFilter</filter-name>
      <filter-class>cn.itcast.web.filter.FirstFilter</filter-class>
   </filter>
   <filter>
      <filter-name>senondFilter</filter-name>
      <filter-class>cn.itcast.web.filter.SecondFilter</filter-class>
   </filter>
    <filter-mapping>
      <filter-name>firstFilter</filter-name>
     <url-pattern>/demo</url-pattern>
    </filter-mapping>
   <filter-mapping>
      <filter-name>senondFilter</filter-name>
     <url-pattern>/demo</url-pattern>
    </filter-mapping>
    
    <servlet>
      <servlet-name>demoServlet</servlet-name>
      <servlet-class>cn.itcast.web.servlet.DemoServlet</servlet-class>
    </servlet>
    <servlet-mapping>
      <servlet-name>demoServlet</servlet-name>
      <url-pattern>/demo</url-pattern>
    </servlet-mapping>
   
启动服务器,访问/demo

将FirstFilter的<filter-mapping>标签对放置在SecondFilter的映射标签对的前面 重启服务器 访问/demo

(3)演示Filter类中init方法中FilterConfig的使用
创建Demo2Filter 
package cn.itcast.web.filter;

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;
// Filter入门
// 1.创建一个类你,实现Filter接口
// 2.重写方法
// 3.在web.xml文件中配置

public class Demo2Filter implements Filter {

	@Override
	public void destroy() {

	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		System.out.println("demo2Filter进行过滤操作......");
		// 放行
		chain.doFilter(request, response);
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// 1. 获取filter名称
		String filterName = filterConfig.getFilterName();
		System.out.println(filterName);

		// 2.获取初始化参数
		String encoding = filterConfig.getInitParameter("encoding");
		System.out.println(encoding);

		Enumeration<String> names = filterConfig.getInitParameterNames();
		while (names.hasMoreElements()) {
			System.out.println(names.nextElement());
		}

		// 3.获取ServletConfig对象
		filterConfig.getServletContext();
	}

}

在web.xml中添加如下配置:
<filter>
		<filter-name>demo2Filter</filter-name>
		<filter-class>cn.itcast.web.filter.Demo2Filter</filter-class>
 
		<!-- 初始化参数 -->
		  
		<init-param>
			<param-name>encoding</param-name>
			<param-value>utf-8</param-value>
		</init-param>
		<init-param>
			<param-name>username</param-name>
			<param-value>liuxun</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>demo2Filter</filter-name>
		<url-pattern>/index.jsp</url-pattern>  <!-- 访问index.jsp页面会被demoFilter过滤 -->
        </filter-mapping> 

重启服务器:

(4)演示<filter-mapping>中<dispatcher>子元素的使用
修改DemoServlet如下:
package cn.itcast.web.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 DemoServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("demo servlet");
		// 请求转发到demo1Servlet
		request.getRequestDispatcher("/demo1").forward(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}
Demo1Servlet
package cn.itcast.web.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 Demo1Servlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
        System.out.println("Demo1Servlet ......");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}
在web.xml中添加如下配置:
 <!-- 使用MyFilter对Demo1Servlet进行拦截 -->
   <filter>
     <filter-name>myFilter</filter-name>
     <filter-class>cn.itcast.web.filter.MyFilter</filter-class>
   </filter>
   <filter-mapping>
    <filter-name>myFilter</filter-name>
    <url-pattern>/demo1</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
   </filter-mapping>
    <servlet>
      <servlet-name>demoServlet</servlet-name>
      <servlet-class>cn.itcast.web.servlet.DemoServlet</servlet-class>
    </servlet>
  <servlet>
    <servlet-name>Demo1Servlet</servlet-name>
    <servlet-class>cn.itcast.web.servlet.Demo1Servlet</servlet-class>
  </servlet>

    <servlet-mapping>
      <servlet-name>demoServlet</servlet-name>
      <url-pattern>/demo</url-pattern>
    </servlet-mapping>
  <servlet-mapping>
    <servlet-name>Demo1Servlet</servlet-name>
    <url-pattern>/demo1</url-pattern>
  </servlet-mapping>
访问/demo1  后台打印如下:

Filter应用示例:

示例1:全站统一字符编码过滤器

编写jsp 输入用户名,在Servlet中获取用户名,将用户名输出到浏览器上 
处理请求post乱码代码
request.setCharacterEncoding("utf-8");
设置响应编码集代码
response.setContentType("text/html;charset=utf-8");
经常会使用,而过滤器可以在目标资源之前执行,将很多程序中处理乱码公共代码,提取到过滤器中 ,以后程序中不需要处理编码问题了
代码如下:
EncodingFilter.java
package cn.itcast.filter.demo1;

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;

public class EncodingFilter implements Filter {

	private String encode;

	@Override
	public void destroy() {

	}

	@Override
	public void doFilter(ServletRequest arg0, ServletResponse arg1,
			FilterChain arg2) throws IOException, ServletException {
		// 1.强制转换
		HttpServletRequest request = (HttpServletRequest) arg0;
		HttpServletResponse response = (HttpServletResponse) arg1;

		// 2.操作
		request.setCharacterEncoding(encode);
		response.setContentType("text/html;charset=utf-8");
		// 3.放行

		arg2.doFilter(request, response);

	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		this.encode = arg0.getInitParameter("encode");
	}

}
Demo1Servlet.java
package cn.itcast.servlet.demo1;

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 Demo1Servlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		// request.setCharacterEncoding("utf-8");
		// 1.获取页面的请求参数
		String username = request.getParameter("username");
		String msg = request.getParameter("msg");

		// 显示
		System.out.println(username);
		System.out.println(msg);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}
demo1.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<title>My JSP 'demo1.jsp' starting page</title>

</head>

<body>
	<form action="${pageContext.request.contextPath }/demo1" method="POST">
		username:<input type="text" name="username"> <br> msg:<input
			type="text" name="msg"><br> <input type="submit"
			value="提交">
	</form>
</body>
</html>
在web.xml中添加如下配置
<!-- post编码过滤器 -->
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>cn.itcast.filter.demo1.EncodingFilter</filter-class>

		<init-param>
			<param-name>encode</param-name>
			<param-value>utf-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
	  <filter-name>encodingFilter</filter-name>
	  <url-pattern>/*</url-pattern>
	</filter-mapping>
	<servlet>
		<servlet-name>Demo1Servlet</servlet-name>
		<servlet-class>cn.itcast.servlet.demo1.Demo1Servlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>Demo1Servlet</servlet-name>
		<url-pattern>/demo1</url-pattern>
	</servlet-mapping>

访问如下:


示例2:禁用所有JSP页面缓存

因为动态页面数据,是由程序生成的,所以如果有缓存,就会发生,客户端查看数据不是最新数据情况 ,对于动态程序生成页面,设置浏览器端禁止缓存页面内容 
response.setDateHeader("Expires",-1);
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
并不是所有的浏览器都能完全支持上面的三个响应头,因此最好是同时使用上面的三个响应头。
Expires数据头:值为GMT时间值,为-1指浏览器不要缓存页面。
Cache-Control响应头有两个常用值:
   no-cache指浏览器不要缓存当前页面。
   max-age:xxx  指浏览器缓存页面xxx秒
将禁用缓存代码,提起到过滤器中,通过url配置,禁用所有JSP页面的缓存
CacheFilter
package cn.itcast.filter.demo2;

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;

public class CacheFilter implements Filter {

	@Override
	public void destroy() {

	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		// 1.强制转换
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;

		// 2.操作
		response.setHeader("pragma", "no-cache");
		response.setHeader("cache-control", "no-cache");
		response.setDateHeader("expires", 0);

		// 3.放行
		chain.doFilter(request, response);
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {

	}

}

在web.xml中注册映射filter
         <filter>
		<filter-name>cacheFilter</filter-name>
		<filter-class>cn.itcast.filter.demo2.CacheFilter</filter-class>
	</filter>
	
	<filter-mapping>
	    <filter-name>cacheFilter</filter-name>
	    <url-pattern>*.jsp</url-pattern>
	</filter-mapping>

示例3:设置图片过期时间

Tomcat缓存策略
  对于服务器端经常不变化的文件,设置客户端缓存时间,在客户端资源缓存时间到期之前,就不会去访问服务器获取该资源(比tomcat内置缓存策略更优的手段)
    减少服务器的请求次数,提升性能。
设置静态资源缓存时间,需要设置Expires过期时间,在客户端资源没有过期之前,是不会产生对该资源的请求的。
设置Expires通常使用response.setDateHeader进行设置 设置毫秒值。
操作如下:
在WebRoot下拖一张filter.jpg的图片
新建图片过滤器ImageCacheFilter
package cn.itcast.filter.demo3;

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;

public class ImageCacheFilter implements Filter {

	@Override
	public void destroy() {

	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		// 1.强制转换
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;

		// 2.操作 设置响应的缓存时间为10天
		response.setDateHeader("expires", System.currentTimeMillis() + 60 * 60
				* 24 * 10*1000);

		// 3.放行
		chain.doFilter(request, response);
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {

	}

}
新建一加载图片的jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    
    <title>My JSP 'index.jsp' starting page</title>
	
  </head>
  
  <body>
    <img src="${pageContext.request.contextPath}/filter.jpg">
  </body>
</html>
配置图片过滤器的注册和映射 在web.xml中添加如下配置
        <filter>
		<filter-name>imageFilter</filter-name>
		<filter-class>cn.itcast.filter.demo3.ImageCacheFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>imageFilter</filter-name>
		<url-pattern>*.png</url-pattern>
		<url-pattern>*.jpg</url-pattern>
		<url-pattern>*.bmp</url-pattern>
	</filter-mapping>
重启服务器 访问 查看如下:

示例4:实现用户自动登录(MD5加密)的过滤器

实现用户自动登陆的过滤器
简介:在用户登陆成功后,以cookis形式发送用户名、密码给客户端
编写一个过滤器,filter方法中检查cookie中是否带有用户名、密码信息,如果存在则调用业务层登陆方法,登陆成功后则向session中存入user对象(即用户登陆标记),以实现程序完成自动登陆。
需求:在访问一个站点,登陆时勾选自动登陆(三个月内不用登陆),操作系统后,关闭浏览器;过几天再次访问该站点时,直接进行登陆后状态 
思路如下:

在数据库中创建user表
create table user (
   id int primary key auto_increment,
   username varchar(20),
   password varchar(40),
   role varchar(10)
);

insert into user values(null,'admin','123','admin');
insert into user values(null,'aaa','123','user');
insert into user values(null,'bbb','123','user');

在用户完成登陆后,勾选自动登陆复选框,服务器端将用户名和密码 以Cookie形式,保存在客户端 。当用户下次访问该站点,AutoLoginFilter 过滤器从Cookie中获取 自动登陆信息 
1、判断用户是否已经登陆,如果已经登陆,没有自动登陆的必要
2、判断Cookie中是否含有自动登陆信息 ,如果没有,无法完成自动登陆
3、使用cookie用户名和密码 完成自动登陆 

如果将用户密码保存在cookie文件中,非常不安全的 ,通常情况下密码需要加密后才能保存到客户端 
* 使用md5算法对密码进行加密 
* md5 加密算法是一个单向加密算法 ,支持明文---密文 不支持密文解密 
注意事项:Cookie中是不能保持中文的可以使用URLEncode类进行encode加密,获取后进行解密 可以保存中文用户名 至于密码 使用以MD5进行加密后的密码即可 二者拼接返回客户端。

MySQL数据库中提供了md5函数,可以完成md5加密
mysql> select md5('123');
+----------------------------------+
| md5('123')                       |
+----------------------------------+
| 202cb962ac59075b964b07152d234b70 |
+----------------------------------+
1 row in set (0.00 sec)
加密后结果是32位数字  16进制进行表示

Java中提供类MessageDigest完成MD5加密
package cn.itcast.utils;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Md5Utils {

	public static String md5(String plainText) { //明文
		byte[] secretBytes = null;
		try {
			secretBytes = MessageDigest.getInstance("md5").digest(
					plainText.getBytes());
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException("没有md5这个算法!");
		}
		String md5code = new BigInteger(1, secretBytes).toString(16);
		for (int i = 0; i < 32 - md5code.length(); i++) {
			md5code = "0" + md5code;
		}
		return md5code;
	}
	
	public static void main(String[] args) {
		
		System.out.println(md5("123"));
	}

}

将数据表中所有密码 变为密文  update user set password=md5(password);

在注册逻辑中,对密码进行MD5加密
在AutoLoginFilter中 因为从Cookie中获得就是加密后的密码,所以登录时无需再次加密
开始实现,步骤如下:
导入jar包:
JSTL(standard.jar、jstl.jar):jstl标签库
C3P0(c3p0-x.x.x.jar):获取数据源与数据库连接
DbUtils(commons-dbutils-1.4.jar):简化数据库操作,可直接将每条数据封装成Java对象
BeanUtils(commons-beanutils-1.8.3.jar、commons-logging-1.1.1.jar) :将表单数据封装成Bean对象
数据库驱动(mysql-connector-java-5.0.8-bin.jar):连接数据库
在classPath路径即src下新建c3p0-config.xml 文件配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
	<default-config>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///mydb1</property>
		<property name="user">root</property>
		<property name="password">root</property>
	</default-config>

</c3p0-config>
	

新建工具包 在包内新建获取数据源的工具类
DataSourceUtil类:
package cn.itcast.utils;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class DataSourceUtils {

	private static ComboPooledDataSource cpds = new ComboPooledDataSource();

	public static Connection getConnection() throws SQLException {
		return cpds.getConnection();
	}

	public static DataSource getDataSource() {
		return cpds;
	}

}
在工具包内新建MD5加密的工具类
MD5Utils
package cn.itcast.utils;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Md5Utils {

	public static String md5(String plainText) { //明文
		byte[] secretBytes = null;
		try {
			secretBytes = MessageDigest.getInstance("md5").digest(
					plainText.getBytes());
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException("没有md5这个算法!");
		}
		String md5code = new BigInteger(1, secretBytes).toString(16);
		for (int i = 0; i < 32 - md5code.length(); i++) {
			md5code = "0" + md5code;
		}
		return md5code;
	}
	
	public static void main(String[] args) {
		
		System.out.println(md5("123"));
	}

}
在工具包内新建根据名称从Cookie[] 数组中取出Cookie的工具类
CookieUtils
package cn.itcast.utils;

import javax.servlet.http.Cookie;

public class CookieUtils {

	public static Cookie findCookieByName(Cookie[] cs, String name) {
		if (cs == null || cs.length == 0) {
			return null;
		}
		for (Cookie c : cs) {
			if (c.getName().equals(name)) {
				return c;
			}
		}

		return null;
	}
}

工具类已建好 开始实现具体逻辑:
新建Bean类User
package cn.itcast.domain;

public class User {
	private int id;
	private String username;
	private String password;
	private String role;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getRole() {
		return role;
	}

	public void setRole(String role) {
		this.role = role;
	}
}
新建DAO  UserDao
package cn.itcast.dao;

import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import cn.itcast.domain.User;
import cn.itcast.utils.DataSourceUtils;

public class UserDao {
	// 根据用户名和密码查找用户
	public User findUserByUsernameAndPassword(String username, String password)
			throws SQLException {
		String sql = "select * from user where username=? and password=?";
		QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());

		return runner.query(sql, new BeanHandler<User>(User.class), username,
				password);
	}
}
新建业务层类UserService 实现登录功能
package cn.itcast.service;

import java.sql.SQLException;

import cn.itcast.dao.UserDao;
import cn.itcast.domain.User;

public class UserService {
	public User login(String username, String password) throws SQLException {
		return new UserDao().findUserByUsernameAndPassword(username, password);
	}
}

新建LoginServlet  /login 处理登录提交的数据
package cn.itcast.servlet.demo4;

import java.io.IOException;
import java.net.URLEncoder;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.itcast.domain.User;
import cn.itcast.service.UserService;
import cn.itcast.utils.Md5Utils;

public class LoginServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		// 1.得到请求参数
		String username = request.getParameter("username");
		String password = request.getParameter("password");

		// 2.登录
		UserService service = new UserService();
		try {
			User user = service.login(username, Md5Utils.md5(password));
			if (user != null) {
				// 登录成功

				// 判断是否勾选了自动登录
				String autologin = request.getParameter("autologin");
				if ("ok".equals(autologin)) {
					// 勾选了自动登录
					Cookie cookie = new Cookie("autologin", URLEncoder.encode(
							username, "utf-8") + "::" + Md5Utils.md5(password));
					cookie.setMaxAge(60 * 60 * 24 * 10); // 存储10天
					cookie.setPath("/");
					response.addCookie(cookie);
				}

				request.getSession().setAttribute("user", user);
				response.sendRedirect(request.getContextPath()
						+ "/demo4/success.jsp");
				return;
			} else {
				request.setAttribute("login.message", "用户名或密码错误");
				request.getRequestDispatcher("/demo4/login.jsp").forward(
						request, response);
				return;
			}
		} catch (SQLException e) {
			e.printStackTrace();
			request.setAttribute("login.message", "登录失败");
			request.getRequestDispatcher("/demo4/login.jsp").forward(request,
					response);
			return;
		}

	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}

新建AutoLoginFilter类 读取Cookie实现自动登录
package cn.itcast.filter.demo4;

import java.io.IOException;
import java.net.URLDecoder;
import java.sql.SQLException;

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.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.itcast.domain.User;
import cn.itcast.service.UserService;
import cn.itcast.utils.CookieUtils;
import cn.itcast.utils.Md5Utils;

public class AutoLoginFilter implements Filter {

	@Override
	public void destroy() {

	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		// 1.强制转换
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;

		// 2.操作

		// 判断用户访问的是登录操作,不进行自动登录
		String uri = request.getRequestURI();
		String contextPath = request.getContextPath();
		String path = uri.substring(contextPath.length());

//		System.out.println(uri);
//		System.out.println(contextPath);
//		System.out.println(path);
		if (!(path.equals("/demo4/login.jsp") || path.equals("/login"))) {
			// 判断用户没有登录,才进行自动登录
			User u = (User) request.getSession().getAttribute("user");
			System.out.println(u);
			if (u == null) {
				// 2.1得到cookie中的username,password
				Cookie cookie = CookieUtils.findCookieByName(
						request.getCookies(), "autologin");
				if (cookie != null) {
					// 找到了则进行自动登录
					String username = URLDecoder.decode(cookie.getValue()
							.split("::")[0], "utf-8");
					String password = cookie.getValue().split("::")[1];
					UserService service = new UserService();
					User user = null;
					try {
						user = service.login(username, password);
						if (user != null) {
							// 查到了用户,则进行自动登录
							request.getSession().setAttribute("user", user);
							return;
						}
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			}
		}

		// 3.放行
		chain.doFilter(request, response);

	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {

	}

}

在web.xml中添加如下配置
<!-- 自动登录Filter -->
	<filter>
		<filter-name>autoLoginFilter</filter-name>
		<filter-class>cn.itcast.filter.demo4.AutoLoginFilter</filter-class>
	</filter>

        <filter-mapping>
                <filter-name>autoLoginFilter</filter-name>
                <url-pattern>/*</url-pattern>
        </filter-mapping>

	<servlet>
		<servlet-name>LoginServlet</servlet-name>
		<servlet-class>cn.itcast.servlet.demo4.LoginServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>LoginServlet</servlet-name>
		<url-pattern>/login</url-pattern>
	</servlet-mapping>

在WebRoot下新建demo文件夹 在文件夹内新建login.jsp、success.jsp
login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'login.jsp' starting page</title>
</head>

<body>
	${ requestScope["login.message"] }
	<br>
	<!-- 注意:pageContext.request代表当前页面的请求对象,我们知道JSP是源于Servlet
                       如果不用pageContext对象,直接使用request 则代表其他页面向该JSP请求的对象
   -->
	<form action="${pageContext.request.contextPath }/login" method="POST">
		username:<input type="text" name="username"><br>
		password:<input type="password" name="password"><br>
		<input type="checkbox" name="autologin" value="ok">自动登录<br>
		 <input type="submit" value="登录">
	</form>
</body>
</html>

success.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'login.jsp' starting page</title>
</head>

<body>
当前用户:${user.username }
</body>
</html>
运行结果如下:

示例5:实现URL级别的权限控制

使用Filter实现URL级别的权限认证
情景:在实际开发中我们经常把一些执行敏感操作的servlet映射到一些特殊目录中,并用filter把这些特殊目录保护起来,限制只能拥有相应访问权限的用户才能访问这些目录下的资源。从而在我们系统中实现一种URL级别的权限功能。
要求:为使Filter具有通用性,Filter保护的资源和相应的访问权限通过filter参数的形式予以配置。

需求:角色role是admin的只可以实现“书架”增删改 角色为user的用户只能进行查询
新建四个Servlet分别代表书架的增删改查
BookAddServlet   url:/book_add
package cn.itcast.servlet.demo5;

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 BookAddServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
     System.out.println("book add ......");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}
BookDeleteServlet  url:/book_delete
package cn.itcast.servlet.demo5;

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 BookDeleteServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
       System.out.println("book delete ......");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}
BookSearchServlet  url: /book_search
package cn.itcast.servlet.demo5;

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 BookSearchServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
    System.out.println("book search ......");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}
BookUpdateServlet  url: /book_update
package cn.itcast.servlet.demo5;

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 BookUpdateServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
  System.out.println("book update ......");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}
新建自定义权限异常类PrivilegeException 继承自RuntimeException
package cn.itcast.exception;

import java.io.PrintStream;
import java.io.PrintWriter;

public class PrivilegeException extends RuntimeException {

	@Override
	public String getMessage() {
		// TODO Auto-generated method stub
		return super.getMessage();
	}

	@Override
	public String getLocalizedMessage() {
		// TODO Auto-generated method stub
		return super.getLocalizedMessage();
	}

	@Override
	public synchronized Throwable getCause() {
		// TODO Auto-generated method stub
		return super.getCause();
	}

	@Override
	public synchronized Throwable initCause(Throwable cause) {
		// TODO Auto-generated method stub
		return super.initCause(cause);
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return super.toString();
	}

	@Override
	public void printStackTrace() {
		// TODO Auto-generated method stub
		super.printStackTrace();
	}

	@Override
	public void printStackTrace(PrintStream s) {
		// TODO Auto-generated method stub
		super.printStackTrace(s);
	}

	@Override
	public void printStackTrace(PrintWriter s) {
		// TODO Auto-generated method stub
		super.printStackTrace(s);
	}

	@Override
	public synchronized Throwable fillInStackTrace() {
		// TODO Auto-generated method stub
		return super.fillInStackTrace();
	}

	@Override
	public StackTraceElement[] getStackTrace() {
		// TODO Auto-generated method stub
		return super.getStackTrace();
	}

	@Override
	public void setStackTrace(StackTraceElement[] stackTrace) {
		// TODO Auto-generated method stub
		super.setStackTrace(stackTrace);
	}

	@Override
	public int hashCode() {
		// TODO Auto-generated method stub
		return super.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		return super.equals(obj);
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return super.clone();
	}

	@Override
	protected void finalize() throws Throwable {
		// TODO Auto-generated method stub
		super.finalize();
	}

}
新建自定义权限异常抛出后跳转的页面 
在WebRoot/error下新建privilege.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
   
    
    <title>My JSP 'privilege.jsp' starting page</title>
    
  </head>
  
  <body>
    <h1>权限不足</h1>
  </body>
</html>
最后自定义URL权限访问过滤器
package cn.itcast.filter.demo5;

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 cn.itcast.domain.User;
import cn.itcast.exception.PrivilegeException;

//权限过滤器--垃圾版本
public class PrivilegeFilter implements Filter {

	public void destroy() {

	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		// 1.强制转换
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;

		// 2.操作

		// 判断当前资源是否需要权限控制.
		String uri = request.getRequestURI();
		String contextPath = request.getContextPath();
		String path = uri.substring(contextPath.length());

		if (path.equals("/book_add") || path.equals("/book_update")
				|| path.equals("/book_delete") || path.equals("/book_search")) {

			// 判断用户是否登录了.
			User user = (User) request.getSession().getAttribute("user");

			if (user == null) {
				throw new PrivilegeException();
			}
			// 判断用户的角色,是否可以访问当前资源路径。
			if ("admin".equals(user.getRole())) {
				// 这是admin角色

				if (!(path.equals("/book_add") || path.equals("/book_update") || path
						.equals("/book_delete"))) {
					throw new PrivilegeException();
				}

			} else {
				// 这是user角色
				if (!(path.equals("/book_search"))) {
					throw new PrivilegeException();
				}
			}

		}
		// 3.放行
		chain.doFilter(request, response);
	}

	public void init(FilterConfig filterConfig) throws ServletException {

	}

}
注意:以上的这个权限过滤器虽然可以使用 但代码过于臃肿,另外访问路径和filter的耦合性很强不便于维护
需要进行改进:应该讲限制访问的路径以一定的格式封装到properties资源文件 然后在filter的init方法内进行读取
步骤如下:
 (1)在src下新建admin.properties和user.properties资源配置文件如下:
admin.properties
url=/book_add,/book_delete,/book_update

user.properties
url=/book_search
(2)修改权限过滤器如下所示:
package cn.itcast.filter.demo5;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

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 cn.itcast.domain.User;
import cn.itcast.exception.PrivilegeException;

//权限过滤器
public class PrivilegeFilter implements Filter {

	private List<String> admins;
	private List<String> users;

	public void destroy() {

	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		// 1.强制转换
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;

		// 2.操作

		// 判断当前资源是否需要权限控制.
		String uri = request.getRequestURI();
		String contextPath = request.getContextPath();
		String path = uri.substring(contextPath.length());

		if (admins.contains(path) || users.contains(path)) {

			// 判断用户是否登录了.
			User user = (User) request.getSession().getAttribute("user");

			if (user == null) {
				throw new PrivilegeException();
			}
			// 判断用户的角色,是否可以访问当前资源路径。
			if ("admin".equals(user.getRole())) {
				// 这是admin角色

				if (!(admins.contains(path))) {
					throw new PrivilegeException();
				}

			} else {
				// 这是user角色
				if (!(users.contains(path))) {
					throw new PrivilegeException();
				}
			}

		}
		// 3.放行
		chain.doFilter(request, response);
	}

	public void init(FilterConfig filterConfig) throws ServletException {
		this.admins = new ArrayList<String>();
		this.users = new ArrayList<String>();

		fillPath("user", users);
		fillPath("admin", admins);

	}

	private void fillPath(String baseName, List<String> list) {
		
		

		ResourceBundle bundle = ResourceBundle.getBundle(baseName);

		String path = bundle.getString("url");

		String[] paths = path.split(",");

		for (String p : paths) {
			list.add(p);
		}

	}

}
修改示例4中的success.jsp 如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<title>My JSP 'index.jsp' starting page</title>
</head>

<body>
	当前用户:${user.username}
	<br>
	<a href="${pageContext.request.contextPath}/book_add">book add</a>
	<br>
	<a href="${pageContext.request.contextPath}/book_update">book update</a>
	<br>
	<a href="${pageContext.request.contextPath}/book_delete">book delete</a>
	<br>
	<a href="${pageContext.request.contextPath}/book_search">book search</a>
</body>
</html>
最后进行web.xml的配置(添加异常类配置和权限过滤器配置)
<!-- URL权限访问控制Filter -->
	<filter>
		<filter-name>privilegeFilter</filter-name>
		<filter-class>cn.itcast.filter.demo5.PrivilegeFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>privilegeFilter</filter-name>
		<url-pattern>/book_delete</url-pattern>
		<url-pattern>/book_add</url-pattern>
		<url-pattern>/book_update</url-pattern>
		<url-pattern>/book_search</url-pattern>
	</filter-mapping>


	<servlet>
		<servlet-name>LoginServlet</servlet-name>
		<servlet-class>cn.itcast.servlet.demo4.LoginServlet</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>BookAddServlet</servlet-name>
		<servlet-class>cn.itcast.servlet.demo5.BookAddServlet</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>BookDeleteServlet</servlet-name>
		<servlet-class>cn.itcast.servlet.demo5.BookDeleteServlet</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>BookUpdateServlet</servlet-name>
		<servlet-class>cn.itcast.servlet.demo5.BookUpdateServlet</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>BookSearchServlet</servlet-name>
		<servlet-class>cn.itcast.servlet.demo5.BookSearchServlet</servlet-class>
	</servlet>

        <servlet-mapping>
		<servlet-name>Demo1Servlet</servlet-name>
		<url-pattern>/demo1</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>LoginServlet</servlet-name>
		<url-pattern>/login</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>BookAddServlet</servlet-name>
		<url-pattern>/book_add</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>BookDeleteServlet</servlet-name>
		<url-pattern>/book_delete</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>BookUpdateServlet</servlet-name>
		<url-pattern>/book_update</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>BookSearchServlet</servlet-name>
		<url-pattern>/book_search</url-pattern>
	</servlet-mapping>


	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	<error-page>
		<exception-type>cn.itcast.exception.PrivilegeException</exception-type>
		<location>/error/privilege.jsp</location>
	</error-page>

分别以admin和aaa 密码123登录运行结果如下:

web.xml中元素的配置顺序

注意:web.xml中可以配置很多内容 过滤器 Servlet 欢迎页 以及错误页 。那么配置的先后顺序是什么?
技巧:将鼠标移到第一个节点 <web-app>上 会有一个弹窗 点击弹窗 下拉即可查看其配置顺序


示例6:通用的get和post乱码过滤器

由于开发人员在filter中可以得到代表用户请求和响应的request、response对象,因此在编程中可以使用Decorator(装饰器)模式对request、response对象进行包装,再把包装对象传给目标资源,从而实现一些特殊需求。
Decorator设计模式的实现
(1)首先看需要被增强对象实现了什么接口或继承自什么类,编写一个类也去实现这些接口或继承自这些类。
(2)在类中定义一个变量,变量类型即需增强对象的类型。
(3)在类中定义一个构造函数,接收需增强的对象。
(4)覆盖需增强的方法,编写增强的代码即可(即利用接收到需增强对象的引用在需要被覆盖的方法内添加操作)

request对象的增强
  • Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper , (HttpServletRequestWrapper 类实现了request 接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 request 对象的对应方法)以避免用户在对request对象进行增强时需要实现request接口中的所有方法。
  • 使用Decorator模式包装request对象,完全解决get、post请求方式下的乱码问题

其实response对象也是如此:
Servlet  API 中提供了response对象的Decorator设计模式的默认实现类HttpServletResponseWrapper , (HttpServletResponseWrapper类实现了response接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 response对象的对应方法)以避免用户在对response对象进行增强时需要实现response接口中的所有方法。

开始进行编码过滤器的编写,其中需要注意的事项如下:
需要增强的方法无非以下三个获取请求参数的方法:

getParameter

getParameterValues

getParameterMap 

在覆盖的时候可以考虑只遍历getParameterMap中的键值对 使用new String(name.getBytes("iso-8859-1"),"utf-8")方法对参数值进行重新编码,这样做必须注意一条,因为第一次调用就已经将所有参数的值进行重新编码,如果再次调用就会将编码后正常的参数值又进行一次编码 造成除了第一次获取后的参数都是乱码,其解决方案就是 加flag限制符保证一次请求只调用获取参数方法时只遍历一次getParameterMap方法。

编写过滤器如下:

package cn.itcast.filter.demo6;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class EncodingFilter implements Filter {

	public void destroy() {

	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {

		// 1.强制转换
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;

		// 2.操作
		HttpServletRequest myrequest = new MyRequest(request); // 增强后的request,解决了编码问题
		response.setContentType("text/html;charset=utf-8");

		// 3.放行
		chain.doFilter(myrequest, response);

	}

	public void init(FilterConfig filterConfig) throws ServletException {

	}

}

// 装饰类
class MyRequest extends HttpServletRequestWrapper {

	private HttpServletRequest request;

	public MyRequest(HttpServletRequest request) {
		super(request);
		this.request = request;
	}

	// 重写关于获取请求参数的方法.
	@Override
	public String getParameter(String name) {
		Map<String, String[]> map = getParameterMap();

		if (name == null) {
			return null;
		}
		String[] st = map.get(name);
		if (st == null || st.length == 0) {
			return null;
		}

		return st[0];
	}

	@Override
	public String[] getParameterValues(String name) {
		Map<String, String[]> map = getParameterMap();

		if (name == null) {
			return null;
		}
		String[] st = map.get(name);

		return st;

	}

	private boolean flag = true;

	@Override
	public Map getParameterMap() {
		// 1.得到所有请求参数的Map集合
		Map<String, String[]> map = request.getParameterMap(); // 有编码问题.

		// 2.解决编码问题.
		if (flag) {
			for (String key : map.keySet()) {
				String[] values = map.get(key);

				for (int i = 0; i < values.length; i++) {
					try {
						values[i] = new String(values[i].getBytes("iso8859-1"),
								"utf-8");
					} catch (UnsupportedEncodingException e) {
						e.printStackTrace();
					}
				}

			}
			flag = false;
		}
		return map;
	}

}
编写Servlet处理表单的示例 进行测试

Demo6Servlet

package cn.itcast.servlet.demo6;

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 Demo6Servlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
     System.out.println(request.getParameter("msg1"));
     System.out.println(request.getParameter("msg2"));
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doGet(request, response);
	}

}
demo6.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'demo6.jsp' starting page</title>

  </head>
  
  <body>
   <form action="${pageContext.request.contextPath }/demo6" method="GET">
      msg1:<input type="text" name="msg1"><br>
      msg1:<input type="text" name="msg2"><br>
    <input type="submit" value="提交"><br>
   </form>
  </body>
</html>

在web.xml添加如下配置 进行注册和映射编码过滤器

    <filter>
     <filter-name>EncodingFilter</filter-name>
     <filter-class>cn.itcast.filter.demo6.EncodingFilter</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>EncodingFilter</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>
    

运行结果如下:




示例7:response增强压缩响应

应用HttpServletResponseWrapper对象,压缩响应正文内容。思路:
  • 通过filter向目标页面传递一个自定义的response对象。
  • 在自定义的response对象中,重写getOutputStream方法和getWriter方法,使目标资源调用此方法输出页面内容时,获得的是我们自定义的ServletOutputStream对象。
  • 在我们自定义的ServletOuputStream对象中,重写write方法,使写出的数据写出到一个buffer中。
  • 当页面完成输出后,在filter中就可得到页面写出的数据,从而我们可以调用GzipOuputStream对数据进行压缩后再写出给浏览器,以此完成响应正文件压缩功能。
具体代码与示例6类似,在此不再赘述。


你可能感兴趣的:(JAVAWEB开发之Servlet Filter(过滤器)详解包括post和get编码过滤器、URL访问权限控制、自动登录。以及装饰模式的使用)