java web.xml 过滤器_JavaWeb之过滤器

时间:

Talk is cheap  Show me the code

JavaWeb三大组件:

Servlet、Listener、Filter

都需要在web.xml中进行配置,Listener中的两个感知监听器不需要配置。

——过滤器概述

过滤器是JavaWeb的三大组件之一,它与Servlet很相似,不过过滤器是用来拦截请求的,而不是处理请求的。

当用户请求某个Servlet或其他资源(JSP、css、html)时,会先执行部署在这个请求上的Filter,如果Filter“放行”,那么会继续执行用户请求的资源,如果Filter“不放行”,那么就不会执行用户请求的资源。

其实可以这么理解,当用户请求某个Servlet时,Tomcat会去执行注册在这个请求上的Filter,然后是否“放行”由Filter来决定,可以理解为,Filter来决定是否调用Servlet,当执行完成Servlet的代码后,还会执行FilterChain中doFilter()方法后面的代码。

——编写一个过滤器

Filter是单例的。

1、编写一个类,并且实现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;

/**

* 编写过滤器

*  1、写一个类,并且实现Filter接口

*  2、在web.xml中进行相关配置

*

* @author 31067

*

*/

public class AFilter implements Filter {

/**

* 在销毁之前执行,用来对非内存资源进行释放

*/

@Override

public void destroy() {

System.out.println("销毁");

}

/**

* 每次过滤时都会执行

*/

@Override

public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain) throws IOException, ServletException {

System.out.println("拦截");

chain.doFilter(request, response);

System.out.println("放行了");

}

/**

* 在Filter创建后马上调用,用来做初始化操作

*/

@Override

public void init(FilterConfig arg0) throws ServletException {

System.out.println("出生");

}

}

2、在web.xml文件中进行配置

xxx

com.wyc.web.filter.AFilter

xxx

/*    //通常会使用通配符进行url-pattern的配置  /web/*

——Filter的生命周期

1、Filter接口生命周期方法:

*   void init(FilterConfig config)

在服务器启动时会创建Filter的实例对象,并且每个类型的Filter只会创建一个实例对象,也就是说Filter是单例的。

在创建完Filter实例后,会马上调用init()方法完成初始化操作,这个方法只会被执行一次。

*   void destory()

服务器会在创建Filter对象之后,把Filter放到内存中一直使用,通常不会销毁它,一般会在服务器关闭时销毁Filter对象。

在销毁Filter对象之前,服务器会调用Filter对象的destory()方法。

*   void doFilter(ServletRequest , ServletResponse , FilterChain)

这个方法会在用户每次访问“目标资源/index.jsp”时执行。

如果需要“放行”,那么需要调用FilterChain对象的doFilter(ServletRequest, ServletResponse)方法,如果不调用该方法,则无法请求目标资源。

2、生命周期方法中的参数

FilterConfig:

*   获取初始化参数:getInitParameter();

*   获取所有初始化参数的名称:Enumeration getInitParameterNames()。

*   获取过滤器名称:getFilterName();

*   获取application:getServletContext();(获取当前上下文对象)

FilterChain:

*   doFilter(ServletRequest, ServletResponse)

执行“放行”功能,使请求可以访问到资源。

相当于调用了请求Servlet的Service方法。

如果当前过滤器是最后一个过滤器,那么调用chain.doFilter()方法表示执行目标资源,如果不是最后一个过滤器,那么chain.doFilter()表示执行下一个过滤器的doFilter()方法。

面试:Filter接口的doFilter()方法和FilterChain的doFilter()方法有什么区别?

——过滤器的四种拦截方式

拦截方式:

*   请求:DISPATCHER

*   转发:FORWARD

*   包含:INCLUDE

*   错误:ERROR

如果不写,则默认REQUEST,如果只写一种,则默认拦截方式就不存在了。

拦截方式需要在中进行配置:

BFilter

/*

REQUEST    // 默认拦截方式

FORWARD

INCLUDE

ERROR

——过滤器的执行顺序

中的配置执行顺序决定了过滤器的执行顺序。

先配置先执行。

XML配置如下:

AFilter

com.wyc.web.filter.AFilter

AFilter

/*

BFilter

com.wyc.web.filter.BFilter

BFilter

/*

执行效果如下:(与栈相似,当chain的doFilter()方法执行完毕后,还会继续执行剩余代码)

AFilter...start

BFilter...start

index.jsp

BFilter...end

AFilter...end

——Filter的应用场景

1、执行目标资源之前做预处理工作,例如设置编码,这种操作通常都会放行,只是在目标资源执行之前做一些准备工作。

几乎在所有的Servlet中都需要写:request.setCharacterEncoding();,这就可以把代码放到过滤器中。

2、通过条件判断是否放行,例如校验当前用户是否已经登录,或者用户IP是否被禁用。

3、在目标资源执行后,做一些后续的特殊处理工作,例如把目标资源输出的数据进行处理。

也称为回程拦截。

——Filter设置目标资源

在web.xml文件中部署Filter时,可以通过“*”来执行目标资源:

myfilter

/*    // 表示过滤所有资源

这一特性与Servlet完全相同,通过这一特性,可以在用户访问敏感资源时,执行过滤器,例如:/admin/*,可以把所有管理员才能访问的资源放到/admin路径下,这时就可以通过过滤器来校验用户身份了。

还可以为指定目标资源为某个Servlet,例如:

MyServlet

com.wyc.servlet.MyServlet

MyServlet

/MyServlet

MyFilter

com.wyc.filter.MyFilter

MyFilger

MyServlet    // 访问指定的Servlet

当用户访问:localhost:8080/FilterDemo/MyServlet时,会执行名称为MyServlet的Servlet,这时会执行绑定的过滤器。

中可以写以下四种标签:

——Filter小结

1、Filter接口的三个方法:

*   void init(FilterConfig config)

在Tomcat启动时被调用。

*   void destory()

在Tomcat关闭时被调用。

*   void doFilter(ServletRequest, ServletResponse, FilterChain)

每次请求时都会调用该方法。

2、FilterConfig类

与ServletConfig相似,用来获取Filter的初始化参数。

*   ServletContext getServletContext()

获取Servlet上下文对象。

*   String getFilterName()

获取Filter的配置名称。

*   String getInitParameter(String name)

获取Filter的初始化参数,与元素对应。

*   Enumeration getInitParameterNames()

获取所有初始化参数的名称。

3、FilterChain类

*   void doFilter(ServletRequest, ServletResponse)

“放行”方法,表示执行下一个过滤器,或者执行目标资源。可以在调用FilterChain的doFilter()方法前后添加语句。在FilterChain的doFilter()方法之前的语句会在目标资源执行前执行,在FilterChain的doFilter()方法之后的语句会在目标资源执行后执行。

4、四中拦截方式:

REQUEST:拦截直接请求方式

FORWARD:拦截请求转发方式

INCLUDE:拦截请求包含方式

ERROR:拦截错误转发方式

默认是REQUEST。

——分IP统计网站的访问次数

说明:网站统计每个IP地址访问本网站的次数。

分析:

因为一个网站可能有多个页面,无论哪个页面被访问,都要统计访问次数,所以使用过滤器最为方便。

因为需要分IP进行统计,所以可以在过滤器中创建一个Map,使用IP作为key,访问次数为value。当有用户访问时,获取请求的IP,如果IP在Map中存在,说明以前访问过,那么在访问次数上+1即可,如果IP在Map中不存在,那么设置value为1。

整个网站只需要一个Map即可。

Map什么时候创建,将Map保存到那里?

>   Map需要在Filter中使用,也要在页面中使用,所以保存到ServletContext中(application)

>   可以放到ServletContextListener监听器中,在服务器启动时完成创建,并保存到ServletContext中。

代码:

监听器:

public class IPListener implements ServletContextListener {

/**

* 在服务器启动时创建Map,保存到ServletContext中

*/

public void contextInitialized(ServletContextEvent sce) {

// 创建Map

Map map = new LinkedHashMap();

// 得到ServletContext

ServletContext sc = sce.getServletContext();

// 将map保存到ServletContext中

sc.setAttribute("ipMaps", map);

}

public void contextDestroyed(ServletContextEvent arg0) {

}

}

过滤器:

/**

* 从ServletContext中获取Map对象

* 从request中得到请求客户端的IP

* 进行统计工作,将结果保存到Map中

*

* @author 31067

*

*/

public class IPFilter implements Filter {

private FilterConfig config;

public void destroy() {

}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

/*

* 1、得到ServletContext中的Map

* 2、从request中获取当前客户端的IP地址

* 3、查看Map中是否存在这个IP对应的访问次数,如果存在,把次数+1再保存

* 4、如果IP不存在,那么说明是第一次访问,设置访问次数为1

*/

// 1、得到ServletContext

ServletContext app = this.config.getServletContext();

// 得到Map

Map map = (Map) app.getAttribute("ipMaps");

/*

* 2、获取客户端的IP地址

*/

String ip = request.getRemoteAddr();

/*

* 3、进行判断

*/

if (map.get(ip) == null) {

map.put(ip, 1);

} else {

int count = map.get(ip);

map.put(ip, count + 1);

}

chain.doFilter(request, response);

}

/**

* 在服务器启动时就会执行本方法,而且本方法只执行一次

*/

public void init(FilterConfig config) throws ServletException {

this.config = config;

}

}

——粗粒度权限控制

拦截是否登录、拦截用户名、权限拦截。

基于角色的访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注。在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收。角色与角色的关系可以建立起来以囊括更广泛的客观情况。

说明:

给出三个页面:index.jsp  user.jsp  admin.jsp

*   index.jsp:任何用户都可以访问,没有限制。

*   user.jsp:只有登录用户才能访问。

*   admin.jsp:只有管理员才能访问。

代码:

Servlet:

public class LoginServlet extends HttpServlet {

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

request.setCharacterEncoding("utf-8");

response.setContentType("text/html;charset=utf-8");

/*

* 1、获取用户名

* 2、判断用户名中是否包含wyc

*   >  如果包含,就是管理员

*   >  如果不包含,就是普通会员

* 3、要把登录的用户名称保存到Session域中

* 4、转发到index.jsp

*/

String username = null;

String n = request.getParameter("username");

// 判断用户名是否为空并且是否为空字符串

if(n != null && !n.trim().isEmpty()){

username = n;

}

// 如果用户名不为null,则进行判断,避免空指针异常

if(username != null && username.contains("wyc")){

request.getSession().setAttribute("admin", username);

} else {

request.getSession().setAttribute("username", username);

}

request.getRequestDispatcher("/index.jsp").forward(request, response);

}

}

过滤器:

UserFilter:过滤/user/*路径下的请求。

public class UserFilter implements Filter {

public void destroy() {

}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

/*

* 1、得到Session

* 2、判断session域中是否存在admin,如果存在,则放行

* 3、判断session域中是否存在username,如果存在,则放行

* 4、如果都不存在,进行拦截操作,并将页面转发到index.jsp

*/

HttpServletRequest req = (HttpServletRequest)request;

String name = (String)req.getSession().getAttribute("admin");

if(name != null) {

chain.doFilter(request, response);

return;

}

name = (String)req.getSession().getAttribute("username");

if(name != null) {

chain.doFilter(request, response);

return;

} else {

request.setAttribute("msg", "请先登录");

request.getRequestDispatcher("/login.jsp").forward(request, response);

}

}

public void init(FilterConfig fConfig) throws ServletException {

}

}

AdminFilter:过滤/admin/*路径下的请求。

public class AdminFilter implements Filter {

public void destroy() {

}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

/*

* 1、得到session

* 2、判断session域中是否存在admin,如果存在,则放行

*/

HttpServletRequest req = (HttpServletRequest)request;

String name = (String)req.getSession().getAttribute("admin");

if(name != null) {

chain.doFilter(request, response);

return;

} else {

request.setAttribute("msg", "管理员,请先登录");

request.getRequestDispatcher("/login.jsp").forward(request, response);

}

}

public void init(FilterConfig fConfig) throws ServletException {

}

}

——解决全站字符乱码问题(POST和GET中文编码问题)

POST请求:

>   request.setCharacterEncoding("utf-8");

GET请求:

>   String username = request.getParameter("username");

>   username = new String(username.getBytes("iso-8859-1"), "utf-8");

过滤器:

Encodingfilter:

public class EncodingFilter implements Filter {

public void destroy() {

}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

// 处理POST请求编码

request.setCharacterEncoding("utf-8");

/*

* 处理GET请求的编码问题

*/

/*

* 将原请求对象转换为自己的HttpServletRequest对象

* 1、写一个request的装饰类

* 2、增强getParameter()方法

* 3、在“放行”时使用自己的request对象

*/

/*

* 判断请求方式

* 不同的请求方式应该使用不同的编码处理方式

*/

HttpServletRequest req = (HttpServletRequest)request;

if(req.getMethod().equals("GET")){

EncodingRequest er = new EncodingRequest(req);

/*

* 将实现了HttpServletRequest接口的对象传入

*/

chain.doFilter(er, response);

} else if(req.getMethod().equals("POST")) {

chain.doFilter(request, response);

}

}

public void init(FilterConfig fConfig) throws ServletException {

}

}

HttpServletRequest装饰类:

可以将此类保存为工具类,只需要在web.xml中进行配置即可使用。

/**

* 装饰request

*

* @author 31067

*

*/

public class EncodingRequest extends HttpServletRequestWrapper {

private HttpServletRequest request;

public EncodingRequest(HttpServletRequest request) {

/*

* 父类实现了“一切拜托你”

*/

super(request);

this.request = request;

}

@Override

public String getParameter(String name) {

String value = request.getParameter(name);

/*

* 处理编码问题

*/

try {

value = new String(value.getBytes("iso-8859-1"), "utf-8");

} catch (UnsupportedEncodingException e) {

throw new RuntimeException(e);

}

return value;

}

}

——页面静态化

说明:

你到“当当”搜索最多的是什么分类呢?没错,就是Java分类。当你去搜索Java分类时,“当当”会不会去查询数据库呢?当然会了,不查询数据库怎么获取Java分类下的图书呢?其实每天都会有很多人去搜索“Java分类”的图书,每次都需要访问数据库,这会有性能上的缺失,如果是访问静态页面(HTML)那么就会快得多了,静态页面本身就比动态页面快很多倍,而且动态页面总是要去查询数据库,这会更加的降低速度。

页面静态化是把动态页面生成的HTML保存到服务器的文件上,然后当有相同请求时,不再去执行动态页面,而是直接给用户响应上次已经生成的静态页面,而且静态页面还有助于搜索引擎找到你。

步骤:

1、写一个小项目,图书管理系统

页面:

*   jsp:index.jsp

链接页面,有四个超链接。

>   查询所有

>   查看SE分类

>   查看EE分类

>   查看框架分类

*   show.jsp

显示查询结果。

Servlet:

BookServlet

>   findAll()

查看所有图书

>   findByCategory(int category)

按分类查询

BookService:

BookDao:

>   List findAll()

>   List findByCategory(int category)

domain:

Book类

SQL脚本:

create database bookdb;

create table book (

id char(32) primary key,

name varchar(100),

price numeric(10, 2),

category int

);

insert into book values ('b1', 'JavaSE_1', 10, 1);

insert into book values ('b2', 'JavaSE_2', 15, 1);

insert into book values ('b3', 'JavaSE_3', 20, 1);

insert into book values ('b4', 'JavaSE_4', 25, 1);

insert into book values ('b5', 'JavaEE_1', 30, 2);

insert into book values ('b6', 'JavaEE_2', 35, 2);

insert into book values ('b7', 'JavaEE_3', 40, 2);

insert into book values ('b8', 'Java_frameword_1', 45, 3);

insert into book values ('b9', 'Java_frameword_2', 50, 3);

select * from book;

===============================================================================

com.wyc.book.dao.BookDao

import java.sql.SQLException;

import java.util.List;

import org.apache.commons.dbutils.QueryRunner;

import org.apache.commons.dbutils.handlers.BeanListHandler;

import com.wyc.book.domain.Book;

import com.wyc.jdbc.TxQueryRunner;

public class BookDao {

private QueryRunner qr = new TxQueryRunner();

public List findAll() {

String sql = "select * from book";

try {

return qr.query(sql, new BeanListHandler(Book.class));

} catch (SQLException e) {

throw new RuntimeException(e);

}

}

public List findByCategory(int category) {

String sql = "select * from book where category = ?";

Object[] params = { category };

try {

return qr.query(sql, new BeanListHandler(Book.class), params);

} catch (SQLException e) {

throw new RuntimeException(e);

}

}

}

===============================================================================

com.wyc.book.domain.Book

public class Book {

private String id;

private String name;

private double price;

private int category;

@Override

public String toString() {

return "Book [id=" + id + ", name=" + name + ", price=" + price + ", categroy=" + category + "]";

}

public String getId() {

return id;

}

public void setId(String id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public double getPrice() {

return price;

}

public void setPrice(double price) {

this.price = price;

}

public int getCategory() {

return category;

}

public void setCategory(int category) {

this.category = category;

}

}

===============================================================================

com.wyc.book.service.BookService

import java.util.List;

import com.wyc.book.dao.BookDao;

import com.wyc.book.domain.Book;

public class BookService {

private BookDao bookDao = new BookDao();

public List findAll(){

return bookDao.findAll();

}

public List findByCategory(int category){

return bookDao.findByCategory(category);

}

}

===============================================================================

com.wyc.book.web.servlet.BookServlet

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import com.wyc.book.service.BookService;

import com.wyc.servlet.BaseServlet;

public class BookServlet extends BaseServlet {

private BookService bookService = new BookService();

public String findAll(HttpServletRequest request, HttpServletResponse response) {

request.setAttribute("bookList", bookService.findAll());

return "f:show.jsp";

}

public String findByCategory(HttpServletRequest request, HttpServletResponse response) {

String value = request.getParameter("category");

int category = Integer.parseInt(value);

request.setAttribute("bookList", bookService.findByCategory(category));

return "f:show.jsp";

}

}

===============================================================================

c3p0-config.xml

jdbc:mysql://localhost:3306/bookdb

com.mysql.jdbc.Driver

root

Admin123

3

10

2

10

===============================================================================

index.jsp

链接页面

查询所有

查询SE

查询EE

查询Framework

===============================================================================

show.jsp

图书列表

书名单价分类
${book.name }${book.price }JavaSEJavaEEJavaFramework

===============================================================================

2、页面静态化

首次访问时去数据库获取数据,然后把数据保存到HTML页面中。

第二次访问时,就不再去访问数据库了,而是直接显示HTML。

给出一个过滤器,把Servlet请求的资源所做的输出,保存到HTML中,然后再重定向到HTML页面,当第二次访问时,这个HTML已经存在,那么直接重定向即可,不需要再次访问Servlet了。

com.wyc.book.web.filter.StaticFilter

import java.io.File;

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 StaticFilter implements Filter {

private FilterConfig config;

public void destroy() {

}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

HttpServletRequest req = (HttpServletRequest)request;

HttpServletResponse resp = (HttpServletResponse)response;

/*

* 1、第一次访问时,要查找请求对应的HTML页面是否存在,如果存在,则重定向到指定页面

* 2、如果HTML不存在,则放行,把Servlet访问数据库后输出给客户端的数据保存到一个HTML页面中,再重定向到HTML页面

*/

/*

* 一、获取Category参数,来判断请求的页面

*   category有四种可能:

*   >   null -->null.html

*   >   1 --> 1.html

*   >   2 --> 2.html

*   >   3 --> 3.html

* HTML页面保存的路径,在htmls目录下

*

* 判断对应的HTML文件是否存在,如果存在,直接重定向

*/

String category = request.getParameter("category");

// 得到对应的文件名称

String htmlPage = category + ".html";

// 得到文件的存放目录,获取的是真实目录

String htmlPath = config.getServletContext().getRealPath("/htmls");

// 创建文件

File destFile = new File(htmlPath, htmlPage);

// 如果文件存在,则重定向到该文件

if(destFile.exists()) {

// 重定向,重定向要使用完整的绝对路径

resp.sendRedirect(req.getContextPath() + "/htmls/" + htmlPage);

return;

}

/*

* 二、如果HTML文件不存在,需要生成HTML页面并保存

* 1、放行,show.jsp页面会做出输出操作,可以将输出的内容输出到指定的HTML页面,而不是输出到客户端

* 2、调包response,让它的getWriter()与一个HTML文件绑定,而不是客户端,那么show.jsp的输出操作就输出到了指定的HTML文件中了

*/

// 传递文件的绝对路径

StaticResponse sr = new StaticResponse(resp, destFile.getAbsolutePath());

/*

* 放行,即生成了HTML文件

*

* 在放行(输出)之前,需要解决乱码问题:

*   因为所有的数据都是通过show.jsp页面进行输出的,所以可以在show.jsp页面中添加如下meta标签

*   

*/

chain.doFilter(request, sr);

// 此时HTML文件已经存在了,可以重定向到指定页面了

sr.sendRedirect(req.getContextPath() + "/htmls/" + htmlPage);

}

public void init(FilterConfig fConfig) throws ServletException {

this.config = fConfig;

}

}

===============================================================================

com.wyc.book.web.filter.StaticResponse

HttpServletResponse装饰类

import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpServletResponseWrapper;

public class StaticResponse extends HttpServletResponseWrapper {

private PrintWriter pw;

/*

* String path:指向HTML文件的路径

*/

public StaticResponse(HttpServletResponse response, String path) {

super(response);

try {

// 创建一个HTML文件并创建一个与HTML文件路径绑定在一起的流对象

this.pw = new PrintWriter(path, "utf-8");

} catch (Exception e) {

throw new RuntimeException(e);

}

}

public PrintWriter getWriter(){

// 返回一个与HTML绑定在一起的printWriter对象

// JSP会使用它进行输出,这样数据都会输出到指定的HTML文件中了

return pw;

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值