今天我们来介绍 Filter、Listener 这两个模块一些简单的知识和应用,接下来我们开始我们的正题 !
1. Filter(过滤器)
1.1 对 Servlet 容器调用 Servlet 的过程进行拦截,从而在 Servlet 进行响应的前后实现一些特殊的功能,我们需要知道 JSP 的底层实现也是 Servlet 所以所拦截的当然包括 JSP
1.2 如何写一个 FIiter?
a. 实现 Filter 接口(类似于 Servlet 接口,我们可以对比 Servlet 接口学习 Filter)
b. 在 web.xml 文件中映射文件 Filter
c. 配置所要拦截的资源
1.3 Filter 接口
a. Filter 对象在被 WEB 应用加载的时候便被创建, init(FilterConfig filterConfig) 方法在创建 Filter 对象的时候别调用,且只有一次;FilterConfig 对象类似于 ServletConfig 对象,可以获取当前 FilterConfig 的初始化参数;
b. doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 每次请求的拦截都会调用该方法,所以便是执行逻辑代码的方法,利用此方法拦截请求后使用方法 chain.doFilter(requset, response); 释放链接,或者传给下一个 Filter 进行拦截,或者将请求放回请求资源
c. 对于多个 Filter,其执行顺序按照 web.xml 文件中的映射顺序执行,与创建时间无关
d. 小案例之 HelloWorld
Ⅰ. 演示
Ⅱ. 功能介绍
a. 一个小登录页面,用户名和密码分别配置为当前 WEB 的应用初始化参数,通过获取请求参数和配置的初始化参数进行比较,若正确则响应欢迎页面,否则返回原页面响应错误消息;
Ⅲ. 代码
a. 登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Login</title>
</head>
<body>
<h3>${requestScope.message}</h3>
<form action="hello.jsp" method="post">
UserName: <input type="text" name="name"><br><br>
PassWord: <input type="password" name="password"><br><br>
<button type="submit">Submit</button>
</form>
</body>
</html>
b. web.xml
<context-param>
<!-- 配置用户名为当前 WEB 应用的初始化参数 -->
<param-name>name</param-name>
<param-value>Yin</param-value>
</context-param>
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.javaweb.filter.hello.LoginFilter</filter-class>
</filter>
<filter>
<filter-name>HelloFilter</filter-name>
<filter-class>com.javaweb.filter.hello.HelloFilter</filter-class>
<init-param>
<!-- 配置密码为 Filter 初始化 -->
<param-name>password</param-name>
<param-value>123</param-value>
</init-param>
</filter>
c. UserNameFilter.java
package com.javaweb.filter.hello; import com.sun.org.apache.regexp.internal.RE; import javax.servlet.*;
import java.io.IOException; /**
*/
public class UserNameFilter implements Filter {
private FilterConfig filterConfig; @Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
} @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String userName = servletRequest.getParameter("name");
String nameReal = filterConfig.getServletContext().getInitParameter("name"); if (!userName.equals(nameReal)) {
servletRequest.setAttribute("message", "用户名错误");
servletRequest.getRequestDispatcher("/helloFilter/login.jsp").forward(servletRequest, servletResponse);
return;
}
filterChain.doFilter(servletRequest, servletResponse);
} @Override
public void destroy() { }
}
d. PasswordFilter.java
package com.javaweb.filter.hello; import javax.servlet.*;
import java.io.IOException; /**
* Created by shkstart on 2017/11/23.
*/
public class HelloFilter implements Filter {
private FilterConfig filterConfig; @Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
} @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String password = servletRequest.getParameter("password");
String passReal = filterConfig.getInitParameter("password"); if (!password.equals(passReal)) {
servletRequest.setAttribute("message", "密码错误");
servletRequest.getRequestDispatcher("/helloFilter/login.jsp").forward(servletRequest, servletResponse);
return;
}
filterChain.doFilter(servletRequest, servletResponse);
} @Override
public void destroy() { }
}
e. 案例 -- 字符编码过滤器
Ⅰ. 演示
Ⅱ. 功能介绍
a. 在乱码显示之后我们将此页面加入字符编码的过滤器,从而显示正常
b. 在 web.xml 文件中配置一个编码方式(UTF-8)为当前 WEB 应用的初始化参数,在过滤器中在每一个请求之前改变字符编码,防止乱码
Ⅲ. 代码
a. 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>FormPage</title>
</head>
<body>
<form action="b.jsp" method="post">
UserName: <input type="text" name="name"><br><br>
<button type="submit">Submit</button>
</form>
</body>
</html>
b.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Welcom</title>
</head>
<body>
<h3>
Hello ${param.name}
</h3>
</body>
</html>
web.xml
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.javaweb.filter.hello.encoding.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
EncodingFilter.java
package com.javaweb.filter.hello.encoding.filter; import javax.servlet.*;
import java.io.IOException; public class EncodingFilter implements Filter {
private FilterConfig filterConfig; public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
ServletContext servletContext = filterConfig.getServletContext();
String encoding = servletContext.getInitParameter("encoding");
req.setCharacterEncoding(encoding); chain.doFilter(req, resp);
} public void init(FilterConfig config) throws ServletException {
this.filterConfig = config;
}
}
f. 案例 -- 权限管理
Ⅰ. 演示
Ⅱ. 功能介绍
a. 用户登录成功后可以根据用户所拥有的权限过滤用户可登录的页面,若用户拥有该权限,则可以访问该页面,否则提示用户没有权限;
b. 管理员登录页面,登录成功后可以管理用户所拥有的权限,如上;
c. 该案例采用 Map 变量代替数据库存储用户的权限信息;
d. 流程解析
1. 新建 User、Authority 两个类,User 中包含了 String UserName、List<Authority> authorityList 两个变量,Authority 中包含了 String AuthorityName,String url 两个变量,其中 url 为当前权限所对应的目标页面;
2. 新建 UserDao 类,首先初始化权限信息,模仿数据库将所有的权限信息存入 List<Authority> authorityList 变量中,并编写 getAuthorityList() 方法,用于获取所有的权限;
3. 在 UserDao 类模仿数据库将用户以及用户所拥有的权限信息存入 Map<String, List<Authority> userInfo 变量中,在初始化的时候为用户赋予其所拥有的权限;
4. 我们将 2,3 中模仿数据库的代码写入 static 代码块中,只初始化一次就好;
5. 在 UserDao 类编写方法 getUser(String userName) 和 update(String userName, List<Authority> authority) 两个方法,分别用户获取权限和更新权限;
6. 用户登录成功后显示所有可能被访问的页面,点击超链接后利用过滤器拦截请求根据其所拥有的权限所对应的页面和访问的页面对比给出响应;
7. 管理页面登录成功后可以可以选择更改权限,具体代码如下:
Ⅲ. 代码
User Authority 类
package com.javaweb.authority.domain; import java.util.List; /**
* Created by shkstart on 2017/11/27.
*/
public class User {
private String userName;
private List<Authority> authorityList; public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} public List<Authority> getAuthorityList() {
return authorityList;
} public void setAuthorityList(List<Authority> authorityList) {
this.authorityList = authorityList;
} public User(String userName, List<Authority> authorityList) {
this.userName = userName;
this.authorityList = authorityList;
} public User() { }
}
package com.javaweb.authority.domain; /**
* Created by shkstart on 2017/11/27.
*/
public class Authority {
private String displayName;
private String url; public String getDisplayName() {
return displayName;
} public void setDisplayName(String displayName) {
this.displayName = displayName;
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
} public Authority(String displayName, String url) {
this.displayName = displayName;
this.url = url;
} public Authority() { }
}
package com.javaweb.authority.dao; import com.javaweb.authority.domain.Authority;
import com.javaweb.authority.domain.User; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; public class UserDao {
private static Map<String, User> userMap;
private static List<Authority> authorityList; static {
userMap = new HashMap<String, User>();
authorityList = new ArrayList<Authority>(); authorityList.add(new Authority("Article_1", "/authority/article_1.jsp"));
authorityList.add(new Authority("Article_2", "/authority/article_2.jsp"));
authorityList.add(new Authority("Article_3", "/authority/article_3.jsp"));
authorityList.add(new Authority("Article_4", "/authority/article_4.jsp")); User user1 = new User("YY", authorityList.subList(0, 2)); User user2 = new User("SS", authorityList.subList(2, 4)); userMap.put("YY", user1);
userMap.put("SS", user2);
} public static User getUser(String userName) {
User user = null; if (userMap.containsKey(userName)) {
user = userMap.get(userName);
}
return user;
} public static void update(String userName, List<Authority> authorityList) {
User user = getUser(userName);
user.setAuthorityList(authorityList);
} public static List<Authority> getAuthorityList() {
return authorityList;
}
}
用户登录页面(用户登录成功后重定向到所要访问的页面 ArticleList.jsp)
<%--登录页面--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>LoginPage</title>
</head>
<body>
<h3>${requestScope.message}</h3>
<form action="login.do" method="post">
UserName: <input type="text" name="userName"><br><br>
<button type="submit">Submit</button>
</form>
</body>
</html>
Servlet 和以前一样,利用反射实现多个请求利用一个 Servlet,其 login 方法如下
/*
* 权限过滤功能中的登录函数
* */
protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String userName = request.getParameter("userName");
User user = UserDao.getUser(userName);
// 判断用户是否存在
isUser(request, response);
// 将用户信息封装到 session 中,便于后面获取
getSession(request).setAttribute("loginUserName", userName);
response.sendRedirect(request.getContextPath() + "/authority/ArticleList.jsp");
}
login 方法所使用到的 isUser 方法
/*
* 过滤权限功能的登录页面中对不存在的用户名的处理
* */
protected void isUser(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String userName = request.getParameter("userName");
User user = UserDao.getUser(userName);
if (user == null) {
// 将错误消息加入到 request 中,便于转发会页面时显示
request.setAttribute("message", "该用户不存在");
request.getRequestDispatcher("/index.jsp").forward(request, response);
return;
}
}
ArticleList.jsp 页面 -- 重定向到的页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>ArticleList</title>
</head>
<body>
<a href="article_1.jsp">Article1 Page</a><br><br>
<a href="article_2.jsp">Article2 Page</a><br><br>
<a href="article_3.jsp">Article3 Page</a><br><br>
<a href="article_4.jsp">Article4 Page</a><br><br>
</body>
</html>
由于我们将所有访问页面和登录页面以及其他不需要拦截的页面在一个文件夹下,所以我们将那些不需要拦截的页面配置为 WEB 应用的初始化参数,在拦截器中判断。
每次点击超链接的时候拦截请求,将登录用户所拥有的权限和所访问页面对比,给出响应,AuthorityFilter 代码如下:
package com.javaweb.authority.filter; import com.javaweb.authority.dao.UserDao;
import com.javaweb.authority.domain.Authority;
import com.javaweb.authority.domain.User; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List; /**
* 权限管理小实例:权限过滤
* 1. 将所有页面加入到一个目录下,把其中不需要过滤的页面配置为当前 WEB 应用的初始化参数
* 2. 判断用户信息是否存在,若不存在则重定向会登录页面,防止直接访问 ArticleList 页面
* 3. 判断当前所请求的页面是否为不需要过滤的页面,若是则直接释放请求,并结束方法
* 4. 若不是判断当前登录用户的 List<Authority> 的 URl 值是否和所请求的页面的 url 值是否相等,若相等则释放请求
* 5. 若不相等则重定向到错误页面
*/
public class AuthorityFilter implements Filter {
private FilterConfig filterConfig;
private ServletContext servletContext; public void destroy() {
} public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 强制转换为 HttpServletXxx
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 获取初始化参数,不被过滤的页面
String uncheckedPage = servletContext.getInitParameter("uncheckedPage");
List<String> uncheckedPageList = Arrays.asList(uncheckedPage.split(","));
// 获取请求页面
String servletPath = request.getServletPath();
// 获取用户信息
String userName = (String) request.getSession().getAttribute("loginUserName");
if (userName == null) {
response.sendRedirect(request.getContextPath() + "/index.jsp");
}
// 判断所访问页面是否为不需要拦截的页面,若是则直接释放请求
for (String page : uncheckedPageList) {
if (page.equals(servletPath)) {
chain.doFilter(request, response);
return;
}
}
// 因为对每次请求过滤的时候就会重新从 UserDao 中获取,也就是 Map 中,所以在后台更新后不用刷新就可以使用
List<Authority> authorityList = UserDao.getUser(userName).getAuthorityList();
// 若是需要拦截的页面,则遍历用户所拥有权限,将其 url 和目标页面对比,若是则结束方法,释放连接,若不是则返回错误页面
for (Authority authority : authorityList) {
String url = authority.getUrl();
if (url.equals(servletPath)) {
chain.doFilter(request, response);
return;
}
} response.sendRedirect(request.getContextPath() + "/authority/NoAuthority.jsp");
}
/*
* 初始化参数,FilterConfig 必须放在第一行
* */
public void init(FilterConfig config) throws ServletException {
this.filterConfig = config;
servletContext = config.getServletContext();
}
}
若没有对应的权限响应的页面代码如下:
<%--
Created by IntelliJ IDEA.
User: yin‘zhao
Date: 2017/11/28
Time: 12:41
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>NoAuthority</title>
</head>
<body>
<h3>对不起,你没有权限请<a href="ArticleList.jsp">返回</a></h3>
</body>
</html>
以上代码便是过滤用户权限信息的代码,接下来我们开始权限管理代码:
管理登录页面 authority_manager.jsp(登录代码)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Manager</title>
</head>
<body>
<h3>
<%--显示错误消息的 EL 表达式--%>
${requestScope.message}
<form action="${pageContext.request.contextPath}/query.do" method="post">
<%--用户名--%>
UserName: <input type="text" name="userName" value="${param.userName}"><br>
<button type="submit">Submit</button>
<br><br>
</form>
</h3>
</body>
</html>
提交请求到 Servlet 类中的 query 方法,query 方法代码如下
protected void query(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取请求参数 userName
String userName = request.getParameter("userName");
// 根据 userName 获取 User 对象
User user = UserDao.getUser(userName);
// 判断 user 是否为空,若为空返回原页面结束方法,并提示错误消息
if (user == null) {
request.setAttribute("message", "该用户不存在");
request.getRequestDispatcher("/authority_manager/authority_manager.jsp").forward(request, response);
return;
}
// 若 user 存在则将 User 信息保存到 session 中,方便后面页面的回显
getSession(request).setAttribute("userName", userName);
// 获取用户当前所有的权限信息
List<Authority> authorityList = user.getAuthorityList();
// 将用户的权限信息和所有的权限信息封装到 request 请求域中,转发传回页面
if (authorityList.size() != 0) {
request.setAttribute("userAuthorityList", authorityList);
request.setAttribute("authorityList", UserDao.getAuthorityList());
request.getRequestDispatcher("/authority_manager/authority_manager.jsp").forward(request, response);
}
}
query 方法将登录用户所拥有的权限封装到 request 域中,然后转发回原页面,利用 JSTL 以及 EL 在页面显示权限信息,authority_manager.jsp
<%--
Created by IntelliJ IDEA.
User: yin‘zhao
Date: 2017/11/27
Time: 21:19
To change this template use File | Settings | File Templates.
--%>
<%--权限管理页面--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Manager</title>
</head>
<body>
<h3>
<%--显示错误消息的 EL 表达式--%>
${requestScope.message}
<form action="${pageContext.request.contextPath}/query.do" method="post">
<%--用户名--%>
UserName: <input type="text" name="userName" value="${param.userName}"><br>
<button type="submit">Submit</button>
<br><br>
</form>
<%--多选框提交表单,用户显示更改登录用户的权限信息--%>
<form action="${pageContext.request.contextPath}/update.do" method="post">
<table cellspacing="20" cellpadding="0">
<%--判断 request 域中的权限信息是否为空,若不为空则显示多选框--%>
<c:if test="${!empty requestScope.userAuthorityList}">
<tr>
<th>${sessionScope.userName}的权限是</th>
</tr>
<c:forEach items="${requestScope.authorityList}" var="authority">
<%--设置标识,用于为多选框打勾的标准,此表示需要每次遍历完内层循环后置为 false,因为每次根据 flag 的值只打印一个多选框--%>
<c:set var="flag" value="false"></c:set>
<c:forEach items="${requestScope.userAuthorityList}" var="userAuthority">
<%--若当前 URl 和所有的 URl 有相等值则为其打勾,否则正常显示多选框--%>
<c:if test="${userAuthority.url == authority.url}">
<c:set var="flag" value="true"></c:set>
</c:if>
</c:forEach>
<c:if test="${flag == true}">
<tr>
<td>${authority.displayName}</td>
<td><input type="checkbox" value="${authority.url}" name="authority" checked></td>
</tr>
</c:if>
<c:if test="${flag == false}">
<tr>
<td>${authority.displayName}</td>
<td><input type="checkbox" value="${authority.url}" name="authority"></td>
</tr>
</c:if>
</c:forEach>
<tr>
<td><button type="submit">Update</button></td>
</tr>
</c:if>
</table>
</form>
</h3>
</body>
</html>
在页面上如果需要更改登录用户的权限,我们选择上对应的权限点击提交按钮到 Servlet 的 Update 方法,代码如下
protected void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取多选框的提交信息,即权限信息
String[] authorityVal = request.getParameterValues("authority");
// 获取 session 中保存的用户信息
String userName = (String) getSession(request).getAttribute("userName");
// 用于保存用户更新后的权限信息的 List
List<Authority> authorityList = new ArrayList<Authority>(); // 获取所有的权限信息的第 i 个,和用户权限信息对比,若 URl 相等则将完整的权限信息加到预先定义好的 List 中
List<Authority> secondAuthorityList = UserDao.getAuthorityList();
for (int i = 0; i < authorityVal.length; i++) {
for (Authority authority : secondAuthorityList) {
if (authority.getUrl().equals(authorityVal[i])) {
authorityList.add(authority);
}
}
}
// 执行更新操作,userName 为 session 域中提前保存的,List<Authority> 为上面所对比添加的
UserDao.update(userName, authorityList);
response.sendRedirect(request.getContextPath() + "/authority_manager/authority_manager.jsp");
} protected HttpSession getSession(HttpServletRequest request) throws ServletException, IOException {
return request.getSession();
}
web.xml (配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<context-param>
<param-name>uncheckedPage</param-name>
<param-value>/authority/ArticleList.jsp,/authority/NoAuthority.jsp</param-value>
</context-param>
<filter>
<filter-name>AuthorityFilter</filter-name>
<filter-class>com.javaweb.authority.filter.AuthorityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthorityFilter</filter-name>
<url-pattern>/authority/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>ManagerServlet</servlet-name>
<servlet-class>com.javaweb.authority.controller.ManagerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ManagerServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
下面我贴出案例目录结构,方便大家更好的理解
2. Listener(监听器)
2.1 专门用于对其他对象身上发生的时间或状态改变进行监听和相应处理的对象,当被监听的对象发生变化时立即采取响应的行动
2.2 Servlet 监听器:Servlet 规范中定义了一种特殊类,它用于监听 web 应用程序中的 ServletContext HttpSession ServletRequest 等域对象的创建和销毁事件,以及监听这些域对象中的属性发生修改的事件
2.3 分类
a. 监听域对象自身的创建和销毁的事件监听器(ServletContextListener,HttpSessionListener,ServletRequestListener)
b. 监听域对象中属性的增加和删除的事件监听器(ServletContextAttributeListener, HttSessionAttributeListener,ServletRequestAttributeListener)
c. 监听绑定到 HttpSession 域中某个对象的状态的事件监听器
2.4 ServletContextListener(最常用的 Listener)
a. ServletContextListener 接口用于监听 ServletContext 对象的创建和销毁事件
b. 当 ServletContext 对象被创建后,激发 contextInitialize(ServletContextEvent sce) 方法
Ⅰ. 可以在当前 WEB 应用被加载时对当前 WEB 应用的相关资源进行初始化操作:比如:创建数据库连接池,创建 Spring 的 IOC 容器,读取当前 WEB 应用的初始化参数;
c. 当ServletContext 对象被销毁前,激发 contextDestroyed(ServletContextEvent sce) 方法(当前 WEB 应用从服务器中卸载的时候)
2.5 HttpSessionListener
a. 创建后激发 sessionCreated(HttpSessionEvent se) 方法
b. 销毁前激发 sessionDestroyed(HttpSessionEvent se) 方法
2.6 ServletRequestListener
a. 创建后激发 requestInitialized(ServletRequestEvernt sre) 方法
b. 销毁前激发 requestDeatroyed(ServletRequestEvent sre) 方法
2.7 创建一个监听器
a. Servlet 规范为每种事件监听器都定义了相应的接口,只需实现这些接口即可,web 服务器根据所实现的接口把他注册到相应的被监听对象,web 服务器根据在 web.xml 文件注册的顺序来加载和注册这些 Servlet 监听器
2.8 属性监听器
a. 如上所说那三个监听器,在此类监听器中可以得到属性值和属性名 getName(), getValue ,具体读者可以参看 API
以上就是本次的内容,还是一样,谢谢大家愿意花时间阅读完我的文章,如果其内有写的不合适、不正确的地方还望大家指出!