过滤器,监听器_1:
新建一个项目,然后我们写一个用于登录的表单,提交到一个servlet中,由servlet调用一个数据处理类,进行模拟的数据处理,然后返回处理结果,再在servlet中根据结果进行跳转转向:
1、index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="LoginServlet" method="post">
username:<input type="text" name="username"/><br>
password:<input type="text" name="password"/><br>
<input type="submit" value="login"/>
</form>
<font color="red">
<%
String error=(String)session.getAttribute("error");
if(error!=null){
out.println(error);
}
%>
</font>
</body>
</html>
2、LoginServlet.java:
package com.yun.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.yun.dao.LoginDao;
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username=request.getParameter("username");
String password=request.getParameter("password");
System.out.println("servlet: username : "+username+" password : "+password);
LoginDao loginDao=new LoginDao();
boolean result=loginDao.login(username, password);
if(result){
request.getSession().setAttribute("username", username);
request.getRequestDispatcher("/main/main.jsp").forward(request, response);
}else {
request.getSession().setAttribute("error", "username or password is wrong");
response.sendRedirect("index.jsp");
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
3、LoginDao.java:
package com.yun.dao;
public class LoginDao {
public boolean login(String username,String password){
System.out.println("dao: username : "+username+" password : "+password);
if("zhangsan".equals(username)&&"zhangsan".equals(password)){
return true;
}
return false;
}
}
在LoginServlet中根据LoginDao的模拟比对数据的结果,把页面转向到相应的页面中,如果比对结果为true,就转向到/main/main.jsp中,如果失败,重定向到index.jsp中。
4、/main/main.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<%
String username=(String)session.getAttribute("username");
out.println("welcome: "+username);
%>
</body>
</html>
然后我们把这个项目部署上,进行登录一下,当我们随意输入用户名和密码的时候,是登录不了的,也就是始终是在index.jsp中的,如果我们输入"zhangsan" ,那么就会登录进去,就会进入到/main/main.jsp中,从session中取出当前用户的用户名进行显示。
但是此时有一个问题,我们操作都是从index.jsp中开始的,也就是说,我们要先登录,然后再去main.jsp中进行显示当前用户信息,但是如果我们直接就访问main.jsp的话,也是可以访问的,即使我们没有进行登录,却也可以访问登录之后的页面,按照这个main.jsp中设置的原意,必须要登录才会有用户的信息,所以必须要进行登录才可以。
也就是说,在访问main.jsp的时候,必须要确定用户已经是登录 的状态才可以。
我们可以在登录后的页面中通过判断用户的信息是否存在来确定是否是正常登录进来的,例如,我们一般是把用户的登录信息放在session范围中,我们就可以在main.jsp中进行判断,如果session中有username,那么就是登录进来的,否则就是没有登录直接进到main.jsp中的,就需要给他response重定向到登录页面中。
这样的方式可以实现用户的恶意访问,但是这样的方式就必须在每一个页面中都进行判断session中是否存在用户信息,处理起来很麻烦。
在servlet中提供了过滤器,我们可以使用过滤器来处理这样的问题:
过滤器就是根据请求的url的格式进行过滤请求,url符合指定格式的请求,需通过过滤器才可以进行请求。
过滤器就是实现了javax.servlet.Filter接口的一个普通的java类。
我们首先新建一个类,然后让这个类实现javax.servlet.Filter接口,然后在这个类中就实现了Filter中的三个方法:
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
// TODO Auto-generated method stub
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
从名字我们就能看到这三个方法的意思,init()方法是用于初始化这个过滤器的,destroy()是用于销毁这个过滤器的。doFilter()方法就是写这个过滤器功能的地方,我们要把过滤器要执行的代码写在这个方法中,doFilter()方法有三个参数:ServletRequest arg0, ServletResponse arg1,FilterChain chain,
其中第一个参数的类型是ServletRequest,而我们在web中使用的是HttpServletRequest,其实ServletRequest是HttpServletRequest的父类,我们在使用的时候需要把这个参数强制转型为HttpServletRequest类型,只有HttpServletRequest才能得到session,然后才能进行验证。ServletResponse arg1这个参数也是同理的:
HttpServletRequest request=(HttpServletRequest)arg0;HttpServletResponse response=(HttpServletResponse) arg1;
第三个参数FilterChain chain,这个参数是用来让程序继续执行用的,也就是说,我们如果通过验证了,需要调用这个参数的doFilter()方法,然后让程序继续往下走,否则程序就停在了过滤器中了:
chain.doFilter(request, response);
这个chain的doFilter()方法需要两个参数,分别是ServletRequest,ServletResponse,我们只要把我们强制转型的request和response传进去就可以了。
我们就可以在这个Filter中的doFilter()方法中进行我们的过滤代码的编写了,因为我们在登录的时候是把用户的信息保存在了session中的,所以我们在这个过滤器中进行验证session中是否存在用户登录的信息即可,如果存在,证明这个用户已经登录,否则,就是没有登录。如果用户没有登录,我们重定向到index.jsp中,让其登录,如果判断session中有用户信息,说明已经登录,那么就使用chain对象让程序继续往下执行即可:
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request=(HttpServletRequest)arg0;
HttpServletResponse response=(HttpServletResponse) arg1;
String username=(String) request.getSession().getAttribute("username");
if(username==null){
response.sendRedirect("index.jsp");
}
chain.doFilter(request, response);
}
这样我们的这个过滤器就写完了:
5、SecurityFilter.java:
package com.yun.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;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SecurityFilter implements Filter {
@Override
public void destroy() {
System.out.println("SecurityFilter destroy...");
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request=(HttpServletRequest)arg0;
HttpServletResponse response=(HttpServletResponse) arg1;
String username=(String) request.getSession().getAttribute("username");
if(username==null){
response.sendRedirect("/testFilterListener001/index.jsp");
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println("SecurityFilter init...");
}
}
但是,这个过滤器写好之后,并不能自己执行,我们还需要把这个过滤器配置在web.xml中才可以。
在web.xml中配置过滤器的方式和配置servlet的方式是一样的,
<filter> <filter-name></filter-name> <filter-class></filter-class> </filter> <filter-mapping> <filter-name></filter-name> <url-pattern></url-pattern> </filter-mapping>
需要配置一个filter和对应的filter-mapping,filter-name可以随意取,filter中的filter-name和filter-mapping中的filter-name要一致;filter-class是类的包名加类名;url-pattern是要过滤的url请求路径,我们是要过滤访问main.jsp的用户必须是登录的,我们一般是把要过滤的文件按照过滤的类别放在不同的文件夹中,如本例我们是把main.jsp放在了WebContent下的main文件夹下了,而这个文件夹下一般也不会只有一个文件,一般都是会有多个文件需要进行过滤,那么我们直接过滤url是请求main这个文件夹的就可以了,这个url-pattern的定义就是采用绝对路径进行定义的,如: <url-pattern>/main/*</url-pattern>代表的就是过滤项目下的main目录下的所有jsp文件。
6、web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>testFilterListener001</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <description></description> <display-name>LoginServlet</display-name> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.yun.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/LoginServlet</url-pattern> </servlet-mapping> <filter> <filter-name>securityFilter</filter-name> <filter-class>com.yun.filter.SecurityFilter</filter-class> </filter> <filter-mapping> <filter-name>securityFilter</filter-name> <url-pattern>/main/*</url-pattern> </filter-mapping> </web-app>
此时,我们部署项目,启动服务器,启动的时候我们会在控制台中看到 我们在过滤器中的init()方法中打出的 SecurityFilter init... 这句话,代表启动服务器的时候过滤器已经进行了初始化了。
我们直接在地址栏输入:http://localhost:8080/testFilterListener001/main/main.jsp即直接访问main目录下的main.jsp这个文件,发现就直接跳转到了index.jsp中,这说明我们的SecurityFilter已经起作用了。然后我们可以在main目录中多建立几个jsp,然后直接去访问这个jsp,发现都是重定向到了index.jsp了。
当我们通过index.jsp登录后,进入到main.jsp的时候,就是可以正常进去的,因为此时的session中是有用户的信息的。
二、中文乱码:
我们知道在从jsp到后台的servlet的时候会出现中文乱码的问题,我们在Servlet中通过request.setCharacterEncoding("utf-8");来设置请求的编码,解决这个乱码的问题。但是我们需要在每一个Servlet中都要进行编码的处理。其实在处理乱码的时候,我们就可以使用过滤器来处理。
我们新建一个类,让这个类实现javax.servlet.Filter接口,然后我们在这个过滤器中编写对编码处理的代码:
7、EncodeFilter.java:
package com.yun.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;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class EncodeFilter implements Filter {
@Override
public void destroy() {
System.out.println("EncodeFilter destroy...");
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request=(HttpServletRequest)arg0;
HttpServletResponse response=(HttpServletResponse) arg1;
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println("EncodeFilter init...");
}
}
这样,只要我们让每一个请求都经过这个过滤器就可以了,我们还需要在web.xml中进行配置,我们需要对所有的请求都进行编码过滤,所以我们的url-pattern配置为/*,即代表所有请求:
<url-pattern>/*</url-pattern>
8、web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>testFilterListener001</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <description></description> <display-name>LoginServlet</display-name> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.yun.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/LoginServlet</url-pattern> </servlet-mapping> <filter> <filter-name>securityFilter</filter-name> <filter-class>com.yun.filter.SecurityFilter</filter-class> </filter> <filter-mapping> <filter-name>securityFilter</filter-name> <url-pattern>/main/*</url-pattern> </filter-mapping> <filter> <filter-name>encodeFilter</filter-name> <filter-class>com.yun.filter.EncodeFilter</filter-class> </filter> <filter-mapping> <filter-name>encodeFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
然后此时我们重新启动服务器,再页面中输入中文之后,打印出来看,就没有变成乱码,说明乱码的问题就通过filter解决了。
刚才我们写的编码的过滤器是写好的,把编码统一改为utf-8的,如果我们想要把编码改成其他编码方式的话,就必须去改这个过滤器的源代码,而源代码是不建议去更改的。在web.xml中我们可以给这个过滤器配置一个参数,这个参数的值就是我们的编码类型,然后在过滤器中去获得这个参数就可以了:
配置方式:
<filter> <filter-name>encodeFilter</filter-name> <filter-class>com.yun.filter.EncodeFilter</filter-class> <init-param> <param-name>encode</param-name> <param-value>UTF-8</param-value> </init-param> </filter>
就是在filter中用<init-param>配置参数,这个param-name就是参数的名字,param-value就是这个参数的值,这样配置上之后就相当于encode=UTF-8。
然后我们在过滤器中通过配置的参数的名字来获得值。
9、web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>testFilterListener001</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <description></description> <display-name>LoginServlet</display-name> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.yun.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/LoginServlet</url-pattern> </servlet-mapping> <filter> <filter-name>securityFilter</filter-name> <filter-class>com.yun.filter.SecurityFilter</filter-class> </filter> <filter-mapping> <filter-name>securityFilter</filter-name> <url-pattern>/main/*</url-pattern> </filter-mapping> <filter> <filter-name>encodeFilter</filter-name> <filter-class>com.yun.filter.EncodeFilter</filter-class> <init-param> <param-name>encode</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodeFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
在过滤器中获得配置的参数,是在init()这个方法中的,因为init()方法是在过滤器被创建的时候就执行的。
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("EncodeFilter init...");
this.filterConfig = filterConfig;
this.encode = filterConfig.getInitParameter("encoding");///得到在web.xml中配置的编码
}
我们通过init方法的FilterConfig 参数的getInitParameter()方法来获得初始化参数,我们把这个参数设置为了全局变量,得到这个参数的值之后,直接赋值给全局变量即可,然后在doFilter()方法中就可以使用这个配置的初始化参数进行编码了:
10、EncodeFilter.java:
package com.yun.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;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class EncodeFilter implements Filter {
protected String encode = null;/要制定的编码,在web.xml中配置
protected FilterConfig filterConfig = null;
@Override
public void destroy() {
System.out.println("EncodeFilter destroy...");
this.encode=null;
this.filterConfig=null;
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request=(HttpServletRequest)arg0;
HttpServletResponse response=(HttpServletResponse) arg1;
if (request.getCharacterEncoding() == null){
String encode = getEncode();得到指定的编码名字
if (encode != null)
request.setCharacterEncoding(encode);设置request的编码
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("EncodeFilter init...");
this.filterConfig = filterConfig;
this.encode = filterConfig.getInitParameter("encode");///得到在web.xml中配置的编码
}
public String getEncode() {
return encode;
}
}
在Filter销毁的时候,调用destroy()方法,把这两个全局变量销毁掉。
这样,在我们需要更改编码的时候,只要改web.xml的encode的值就可以了。
过滤器还可以用于敏感字的过滤的问题,例如论坛里面,发表帖子的时候,就在敏感字过滤器中进行匹配敏感字,如果帖子中有敏感字(如:**功,**党)的时候,就让这个帖子不可以发表,否则就进行发表。
在servlet中还有一个listener的类,即监听器,顾名思义,这个就是用来进行监听的,监听器可以监听到session等中是否有值,有什么值。监听器定义了三个父接口,ServletContextListener,HttpSessionListener,ServletRequestListener
分别是对应用于监听context,session,request三个对象的,我们要监听哪个对象的信息,就要实现哪个接口。当然,可以同时监听多个,只要实现对应的接口就可以了。
例如,统计网站的在线人数,就是通过监听session来实现的。
如,我们以监听session为例。新建一个类,并实现javax.servlet.http.HttpSessionListener这个接口,建立完之后,我们发现在在这个类中重写了两个方法,sessionCreated和sessionDestroyed,分别是在session建立的时候和session销毁的时候执行。
11、MyListener.java:
package com.yun.listener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class MyListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
// TODO Auto-generated method stub
}
}
和filter一样,Listener要想可以进行监听,也必须要在web.xml中进行配置才可以,配置的方式就是指定这个监听器的类即可:
<listener> <listener-class>com.yun.listener.MyListener</listener-class> </listener>
然后当session发生改变的时候,就会自动在web.xml中找到这个地址,然后执行相应的增加session或者销毁session的方法。
12、web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>testFilterListener001</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <description></description> <display-name>LoginServlet</display-name> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.yun.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/LoginServlet</url-pattern> </servlet-mapping> <filter> <filter-name>securityFilter</filter-name> <filter-class>com.yun.filter.SecurityFilter</filter-class> </filter> <filter-mapping> <filter-name>securityFilter</filter-name> <url-pattern>/main/*</url-pattern> </filter-mapping> <filter> <filter-name>encodeFilter</filter-name> <filter-class>com.yun.filter.EncodeFilter</filter-class> <init-param> <param-name>encode</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodeFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>com.yun.listener.MyListener</listener-class> </listener> </web-app>
这样这个监听器就会在Session发生改变的时候自动的进行执行了。
监听器可用于统计系统在线人数,因为只要登录了,就会产生session信息,就会激发监听器的session产生事件,同样,退出的时候就会销毁session,也会激发session的销毁事件。