文章目录
- 06【Filter】
- 一、过滤器简介
- 1.1 Filter概述
- 1.2 Filter的使用
- 1.2.1 Filter快速体验
- 1.2.2 XML配置Filter
- 1.2.3 Filter的拦截规则
- 1.3 Filter的生命周期
- 1.3.1 Filter生命周期介绍
- 1.3.2 Filter生命周期相关方法
- 1.3.3 FilterConfig类
- 1.4 Filter的拦截方式
- 1.4.1 REQUEST
- 1.4.2 FORWARD
- 1.4.3 INCLUDE
- 1.4.4 ERROR
- 1.4.5 ASYNC
- 1.5 过滤器的拦截类型小结
- 1.6 Filter小案例
- 二、过滤器链
- 2.1 过滤器链概念
- 2.2 过滤器链案例
- 2.3 过滤器链的执行顺序
- 2.3.1 注解方式
- 2.3.2 xml配置方式
- 三、过滤器综合案例
- 3.1 完成案例功能
- 3.1.1 搭建项目
- 1)执行SQL语句:
- 2)拷贝静态资源
- 3)拷贝jar包
- 4)jdbc.properties:
- 5)DataSourceUtils:
- 6)实体类:
- 3.1.2 登录业务
- 1)修改前端页面
- 2)LoginServlet
- 3)UserService
- 4)UserDao
- 3.1.3 文章列表
- 1)业务分析
- 2)ListServlet:
- 3)ArticleService
- 4)ArticleDao
- 5)list.jsp页面
- 3.1.4 文章添加
- 1)add.jsp页面
- 2)AddServlet:
- 3)ArticleService
- 4)ArticleDao
- 3.2 使用过滤器完成权限校验
- 3.2.1 分析需求
- 3.2.3 编写LoginFilter拦截器
- 3.3 使用过虑器完成特殊字符过滤
- 3.3.1 过滤字符表
- 3.3.2 字符过滤器
06【Filter】
一、过滤器简介
1.1 Filter概述
- Filter:也称之为过滤器,Filter可以在请求执行WEB资源之前或之后执行,他是一段可重用的代码,这就意味着一个Filter可以管理多个web资源;WEB开发人员通过Filter技术对WEB资源进行一些管理,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
过滤器可以作用于动态或静态内容
- Filter的执行流程:
从上图可以看出,在过滤器是在请求到达真实WEB资源之前执行的,因此我们可以在过滤器中做一些统一处理的操作,或者过滤掉一些请求;
1.2 Filter的使用
1.2.1 Filter快速体验
- 定义一个Servlet:
package com.dfbz.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebServlet("/demo01")
public class Demo01Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo01Servlet....");
}
}
- 定义Filter:
package com.dfbz.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebFilter("/*") // Filter的拦截规则
public class Demo01Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
/**
* 当有新的请求被Filter拦截到时执行该方法
*
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter执行了...before");
// 放行该请求
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("filter执行了...after");
}
@Override
public void destroy() {
}
}
访问:http://localhost:8080/demo01
查看IDEA控制台:
需要注意的是,只要访问的路径符合Filter的拦截规则,即使访问的web资源不存在,该请求也会经过Filter
访问:http://localhost:8080/abc
1.2.2 XML配置Filter
注释掉@WebFilter注解,在web.xml中编写配置:
<filter>
<filter-name>demo01Filter</filter-name>
<filter-class>com.dfbz.filter.Demo01Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>demo01Filter</filter-name>
<!--所有的请求都经过该Filter-->
<url-pattern>/*</url-pattern>
</filter-mapping>
1.2.3 Filter的拦截规则
方式 | 示例 |
---|---|
精确匹配 | /user 、/user.jsp |
目录匹配 | /user/* 、/emp/* |
后缀匹配 | *action 、*.html 、*.jsp |
- 练习:
浏览器的访问地址 | 过滤器的配置 | 是否起作用 |
---|---|---|
http://localhost:8080/aaa | /* | 会 |
http://localhost:8080/aaa | /aaa | 会 |
http://localhost:8080/aaa.do | *.do | 会 |
http://localhost:8080/aaa/bbb | /aaa/* | 会 |
http://localhost:8080/bbb.do | /*.do | 不会,配置错误 |
http://localhost:8080/aaa/bbb.action | /aaa/*.action | 不会,配置错误 |
Tips:目录匹配不可用和后缀匹配一起使用;
- 编写Filter测试拦截规则:
package com.dfbz.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
//@WebFilter("/user")
//@WebFilter("/user.jsp")
//@WebFilter("/user/*")
@WebFilter("*.action")
public class Demo02Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Demo02Filter...before");
// 放行该请求
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Demo02Filter...after");
}
@Override
public void destroy() {
}
}
1.3 Filter的生命周期
1.3.1 Filter生命周期介绍
- Filter生命周期:
- 何时创建?
- 服务器启动时创建
- 何时销毁?
- 服务器关闭时
- 创建几次?
- 在整个服务器的运行期间只会创建一次Filter的实例
- 何时创建?
1.3.2 Filter生命周期相关方法
Filter接口中的方法 | 作用和执行次数 |
---|---|
void init() | 初始化的方法,在服务器启动就加载,执行1次 |
void doFilter() | 拦截到请求之后执行的核心逻辑方法,执行N次 |
void destroy() | 销毁,在服务器关闭的时候,执行1次 |
- 编写一个Filter,启动服务器观察控制台输出:
package com.dfbz.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebFilter("/*")
public class Demo03Filter implements Filter {
/**
* 当Filter初始化时执行该方法
*
* @param filterConfig
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("filter初始化了");
}
/**
* 当有新的请求被Filter拦截到时执行该方法
*
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter执行了...before");
// 放行该请求
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("filter执行了...after");
}
/**
* 当Filter销毁时执行该方法
*/
@Override
public void destroy() {
System.out.println("filter销毁了");
}
}
访问一个web资源,观察日志;
1.3.3 FilterConfig类
FilterConfig是Filter的配置类,主要用于获取ServletContext对象,以及获取一些其他的filter信息;
- FilterConfig相关方法:
String getFilterName()
:获取该Filter的名称,默认为该Filter类的全路径名ServletContext getServletContext()
:获取上下文对象(ServletContext)String getInitParameter(String var1)
:获取Filter的初始化参数Enumeration<String> getInitParameterNames()
:获取全部Filter的初始化参数;
- 测试类:
package com.dfbz.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebFilter(
filterName = "demo04Filter", // filter的名称(默认为当前Filter类的全路径名)
value = "/*",
initParams = {
// 自定义一些参数
@WebInitParam(name = "param1", value = "123"),
@WebInitParam(name = "param2", value = "456"),
}
)
public class Demo04Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 获取该Filter的名称
String filterName = filterConfig.getFilterName();
System.out.println("【filterName】: " + filterName);
// 获取初始化的一些自定义参数
String param1 = filterConfig.getInitParameter("param1");
String param2 = filterConfig.getInitParameter("param2");
System.out.println("【param1】: " + param1);
System.out.println("【param2】: " + param2);
// 获取上下文对象
ServletContext app = filterConfig.getServletContext();
System.out.println(app);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Demo04Filter...before");
// 放行该请求
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Demo04Filter...after");
}
@Override
public void destroy() {
}
}
启动服务器,查看IDEA控制台的信息:
1.4 Filter的拦截方式
拦截方式用于指定过滤器在什么样的请求方式下进行拦截,默认是只对直接来至浏览器发送的请求才拦截。
Tips:默认情况下,只拦截浏览器发送过来的请求
1.4.1 REQUEST
REQUEST
:代表只拦截浏览器发送过来的请求,不会拦截RequestDispatcher
的include
和forward
发送过来的请求;也是filter的默认值;
案例:编写一个filter只拦截/demo01
请求,编写一个Demo02Servlet,跳转到/demo01
请求,观察是否会被filter拦截到
- Demo02Servlet:
package com.dfbz.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebServlet("/demo02")
public class Demo02Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("请求到达了demo02");
request.getRequestDispatcher("/demo01").forward(request,response);
}
}
- Demo05Filter:
package com.dfbz.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebFilter(value = "/demo01")
public class Demo05Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Demo05Filter...before");
// 放行该请求
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Demo05Filter...after");
}
@Override
public void destroy() {
}
}
访问:http://localhost:8080/demo01
访问:http://localhost:8080/demo02
1.4.2 FORWARD
FORWARD
:只拦截RequestDispatcher的forward()方法发送过来的请求
- 注解方式配置:
@WebFilter(value = "/demo01", dispatcherTypes = {DispatcherType.FORWARD})
- xml方式配置:
<filter>
<filter-name>demo05</filter-name>
<filter-class>com.dfbz.filter.Demo05Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>demo05</filter-name>
<url-pattern>/demo01</url-pattern>
<!--拦截类型-->
<dispatcher>FORWARD</dispatcher>-->
</filter-mapping>
重启服务器,访问:http://localhost:8080/demo01:
访问:http://localhost:8080/demo02:
1.4.3 INCLUDE
INCLUDE
:只拦截RequestDispatcher的include()方法发送过来的请求
修改Demo02Servlet:
request.getRequestDispatcher("/demo01").include(request,response);
重启服务器,访问:http://localhost:8080/demo02
发现Demo05Filter并不会拦截include发送过来的请求(此时Filter的拦截方式为FORWARD)
修改Demo03Filter:
@WebFilter(value = "/demo01", dispatcherTypes = {DispatcherType.INCLUDE})
重启服务器,访问:http://localhost:8080/demo02
1.4.4 ERROR
ERROR
:只拦截由于异常或错误引发跳转的请求
在web.xml中配置:
<error-page>
<!--根据错误码-->
<!-- <error-code>404</error-code>-->
<!--根据出现异常的类型-->
<exception-type>java.lang.ArithmeticException</exception-type>
<!--如果出现异常就跳转到/demo01-->
<location>/demo01</location>
</error-page>
- 修改Demo02Servlet:
package com.dfbz.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebServlet("/demo02")
public class Demo02Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("请求到达了demo02");
// request.getRequestDispatcher("/demo01").forward(request,response);
// request.getRequestDispatcher("/demo01").include(request,response);
// 模拟出现异常
int i = 1 / 0;
}
}
重启服务器,访问:http://localhost:8080/demo02,出现错误,跳转到/demo01,发现并没有经过Demo05Filter(此时拦截方式是INCLOUD);
- 修改Demo05Filter:
@WebFilter(value = "/demo01", dispatcherTypes = {DispatcherType.ERROR})
重启服务器,访问:http://localhost:8080/demo02,查看控制台;发现经过了拦截器
1.4.5 ASYNC
在Servlet3.0中,支持了异步的Servlet;默认情况下Filter是不能拦截异步的Servlet的,需要开启ASYNC支持;
ASYNC
:开启拦截异步Servlet的支持
- 编写异步的Demo03Servlet:
package com.dfbz.servlet;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebServlet(value = "/demo03", asyncSupported = true) // 1. 开启异步支持
public class Demo03Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 2. 开启异步模式
AsyncContext startAsync = req.startAsync();
System.out.println("主线程开始: " + Thread.currentThread().getName() + "【" + System.currentTimeMillis() + "】");
// 3. 执行任务
startAsync.start(new Runnable() {
@Override
public void run() {
System.out.println("任务线程1: " + Thread.currentThread().getName() + "【" + System.currentTimeMillis() + "】");
// 执行任务
task();
// 异步处理完毕
startAsync.complete();
ServletResponse response = startAsync.getResponse();
try {
// 响应客户端
response.getWriter().write("ok");
} catch (IOException exception) {
exception.printStackTrace();
}
System.out.println("任务线程3: " + Thread.currentThread().getName() + "【" + System.currentTimeMillis() + "】");
}
});
System.out.println("主线程结束: " + Thread.currentThread().getName() + "【" + System.currentTimeMillis() + "】");
}
public void task() {
System.out.println("任务线程2: " + Thread.currentThread().getName() + "【" + System.currentTimeMillis() + "】");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 修改Demo05Filter,让其拦截
/demo03
请求:
@WebFilter(value = "/demo03",dispatcherTypes = {DispatcherType.ASYNC})
public class Demo05Filter implements Filter
访问:http://localhost:8080/demo03
默认情况下,Filter不支持拦截异步的Servlet;
修改Demo05Filter:
@WebFilter(value = "/demo03",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.ASYNC},asyncSupported = true)
重启服务器,访问:http://localhost:8080/demo03
1.5 过滤器的拦截类型小结
过滤类型 | 作用 |
---|---|
REQUEST | 只拦截来自浏览器发送过来的请求,默认值 |
FORWARD | 只拦截通过RequestDispatcher.forward()发送的请求 |
INCLUDE | 只拦截通过RequestDispatcher.include()发送的请求 |
ERROR | 只拦截由于异常/错误触发的请求 |
ASYNC | 拦截异步的Servlet,该Filter也要开启异步支持 |
1.6 Filter小案例
需求:编写过滤器,过滤所有Servlet中使用POST方法提交的汉字的编码。
-
有2个Servlet,一个是LoginServlet登录,一个是RegisterServlet注册
-
有2个JSP页面,1个是login.jsp,有表单,登录名。1个register.jsp,有表单,有注册的名字。都使用POST提交用户名使用汉字提交。
-
使用过滤器,对所有的Servlet的POST方法进行过滤。
-
在没有使用过滤器之前,每个Servlet必须加上汉字编码:request.setCharacterEncoding(字符集);字符集与网页的编码要一致
- login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户登录</title>
</head>
<body>
<h2>用户登录</h2>
<form action="/login" method="post">
登录名:<input type="text" name="user"><br>
<hr>
<input type="submit" value="登录">
</form>
</body>
</html>
- register.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户注册</title>
</head>
<body>
<h2>用户注册</h2>
<form action="/register" method="post">
注册名:<input type="text" name="name"><br>
<hr>
<input type="submit" value="注册">
</form>
</body>
</html>
- LoginServlet.java
package com.dfbz.servlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter out = response.getWriter();
//得到登录的用户名,并且打印输出
String user = request.getParameter("user");
out.print("登录成功,用户名是:" + user);
}
}
- RegisterServlet.java
package com.dfbz.servlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter out = response.getWriter();
//得到注册的用户名
String name = request.getParameter("name");
out.print("注册成功,用户名:" + name);
}
}
- EncodeFilter.java
package com.dfbz.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebFilter("/*")
public class EncodingFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//对汉字进行编码
request.setCharacterEncoding("utf-8");
//放行,将请求向后传递
chain.doFilter(request, response);
}
public void init(FilterConfig config) throws ServletException {
}
}
二、过滤器链
2.1 过滤器链概念
概念:如果一个用户请求到达web资源中间经过多个过滤器,每个过滤器完成一个具体的功能。这多个过滤器就组成了一个过滤链。
- 过滤器链执行流程:
多个过滤器链的执行流程为:过滤器1--->dofilter--->过滤器2---->dofilter---->web资源---->过滤器2--->过滤器1--->客户端
2.2 过滤器链案例
需求:创建两个过滤器OneFilter和TwoFilter,访问ResourceServlet,每个过滤器的请求和响应各输出一句话,观察过滤器的执行过程。
- 第一个过滤器:OneFilter.java
package com.dfbz.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebFilter("/resource")
public class OneFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("过滤器1:before");
chain.doFilter(req, resp);
System.out.println("过滤器1:after");
}
public void init(FilterConfig config) throws ServletException {
}
}
- 第二个过滤器:TwoFilter.java
package com.dfbz.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebFilter("/resource")
public class TwoFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("过滤器2:before");
chain.doFilter(req, resp); //放行
System.out.println("过滤器2:after");
}
public void init(FilterConfig config) throws ServletException {
}
}
- Web资源ResourceServlet.java
package com.dfbz.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@WebServlet("/resource")
public class ResourceServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Resource...");
response.getWriter().write("ok");
}
}
访问:http://localhost:8080/resource
2.3 过滤器链的执行顺序
2.3.1 注解方式
使用注解方式配置:根据过滤器的类名字母的先后顺序再执行,例如A>B>C>D…
我们把TowFilter
的名字改为AowFilter
,重启服务器,访问:http://localhost:8080/resource
2.3.2 xml配置方式
使用xml方式配置时:配置在前面的filter先执行
<filter>
<filter-name>one</filter-name>
<filter-class>com.dfbz.filter.OneFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>one</filter-name>
<url-pattern>/resource</url-pattern>
</filter-mapping>
<filter>
<filter-name>two</filter-name>
<filter-class>com.dfbz.filter.TwoFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>two</filter-name>
<url-pattern>/resource</url-pattern>
</filter-mapping>
重启服务器,访问:http://localhost:8080/resource
三、过滤器综合案例
3.1 完成案例功能
3.1.1 搭建项目
1)执行SQL语句:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for article
-- ----------------------------
DROP TABLE IF EXISTS `article`;
CREATE TABLE `article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文章标题',
`content` varchar(2550) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文章内容',
`publish_date` datetime(0) NULL DEFAULT NULL COMMENT '发布时间',
`publish_user` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '发布人',
`u_id` int(11) NULL DEFAULT NULL COMMENT '发布人id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of article
-- ----------------------------
INSERT INTO `article` VALUES (1, '宝贵的青春', '世界上美丽的东西千千万万,却没有一样比年轻更为美丽;世界上珍贵的东西数也数不清,却没有一样比青春更为宝贵。我们是多么值得骄傲多么让人羡慕啊!而我们若只是挥霍光阴,只是享受,不去奋斗拼搏,那我们真的算拥有青春吗答案是否定的,我们仅有发愤图强,努力耕耘才能做到无愧于青春,无愧于人生,才能拥有一个充实而完美的青春。', '2021-01-10 11:36:26', '张三', 1);
INSERT INTO `article` VALUES (2, '这世界,需要梦想', '这世界,不是所有的地方都能被阳光照耀,也不是时时刻刻都是阳光灿烂。如果你的眼,只是盯着阳光照射不到的地方,或者只是看到眼前的绵绵阴云,那么你的心,就会浸泡在没有阳光的日子里,感受到的,除了负面的情绪,还有什么呢?其实,你完全不必纠结于眼前的景象,你可以放飞自己的那颗自由的心,翱翔在阴云之上。在那时,你的世界就会充满阳光,你的灵魂就会跟着光明飞翔。因而,这世界,需要梦想!', '2020-12-19 11:36:46', '李四', 2);
INSERT INTO `article` VALUES (3, '向着太阳行走', '不改初心,方得始终。这个初心就是对日出的希望。“早晨是岁月的初心,初心在,岁月怎么会老?我每次早起,都是对自己初心的一次回归,不忘初心的人,又怎么会老呢?以朝霞为伴,跟旭日行走,每天都是初生。”这种对日出的初心随时可见,“每一个天亮,我都会重温一次初心,擦拭一下远年的纯真。”每一个天亮,都能感受到作者的初心,真美!', '2020-11-28 11:37:47', '王五', 3);
INSERT INTO `article` VALUES (4, '没尝过努力奋斗的滋味,怎能体会人生的珍贵', '人生的精彩之处,不仅在于取得怎样的成就,更在于拼搏奋斗的过程。\r\n\r\n 奋斗的沿途,也许有痛苦、有失望,但我们终将迎来希望。奋斗的路上,我们会遇见志同道合的朋友,收获更好的自己。我们的人生,也会因为充实的生活而充满色彩。\r\n\r\n 青春不是用来挥霍的,而是要去奋斗的。这是个高速发展的时代,也是奋斗者最好的时代,我们需要随时保持自我学习的能力,朝着前方努力奔跑,越过山丘和沟壑,收获更为精彩的人生。\r\n\r\n 奋斗的滋味,是苦中有甜,甜中带乐。只有品尝过奋斗的滋味,我们才能体会人生的珍贵。趁年轻,去放手一搏吧,这是献给青春最好的礼物,也是对生命最好的馈赠。', '2020-12-30 11:39:30', '张三', 1);
INSERT INTO `article` VALUES (5, '《满江红》· 岳飞', '怒发冲冠,凭栏处、潇潇雨歇。抬望眼、仰天长啸,壮怀激烈。三十功名尘与土,八千里路云和月。莫等闲、白了少年头,空悲切。\r\n靖康耻,犹未雪。臣子恨,何时灭。驾长车,踏破贺兰山缺。壮志饥餐胡虏肉,笑谈渴饮匈奴血。待从头、收拾旧山河,朝天阙。', '2021-01-24 12:18:33', '张三', 1);
INSERT INTO `article` VALUES (6, '《破阵子·为陈同甫赋壮词以寄之》 · 辛弃疾', '醉里挑灯看剑,梦回吹角连营。八百里分麾下炙,五十弦翻塞外声,沙场秋点兵。\r\n马作的卢飞快,弓如霹雳弦惊。了却君王天下事,赢得生前身后名。可怜白发生!', '2021-01-06 12:18:41', '张三', 1);
INSERT INTO `article` VALUES (7, '《过零丁洋》 · 文天祥', '辛苦遭逢起一经,干戈寥落四周星。\r\n山河破碎风飘絮,身世浮沉雨打萍。\r\n惶恐滩头说惶恐,零丁洋里叹零丁。\r\n人生自古谁无死?留取丹心照汗青。', '2021-01-05 12:18:44', '张三', 1);
INSERT INTO `article` VALUES (8, '《滕王阁序》 · 王勃', '豫章故郡,洪都新府。星分翼轸,地接衡庐。襟三江而带五湖,控蛮荆而引瓯越。物华天宝,龙光射牛斗之墟;人杰地灵,徐孺下陈蕃之榻。雄州雾列,俊采星驰。台隍枕夷夏之交,宾主尽东南之美。都督阎公之雅望,棨戟遥临;宇文新州之懿范,襜帷暂驻。十旬休假,胜友如云;千里逢迎,高朋满座。腾蛟起凤,孟学士之词宗;紫电青霜,王将军之武库。家君作宰,路出名区;童子何知,躬逢胜饯。 时维九月,序属三秋。潦水尽而寒潭清,烟光凝而暮山紫。俨骖騑于上路,访风景于崇阿;临帝子之长洲,得天人之旧馆。层峦耸翠,上出重霄;飞阁流丹,下临无地。鹤汀凫渚,穷岛屿之萦回;桂殿兰宫,即冈峦之体势。 披绣闼,俯雕甍,山原旷其盈视,川泽纡其骇瞩。闾阎扑地,钟鸣鼎食之家;舸舰弥津,青雀黄龙之舳。云销雨霁,彩彻区明。落霞与孤鹜齐飞,秋水共长天一色。渔舟唱晚,响穷彭蠡之滨;雁阵惊寒,声断衡阳之浦。 遥襟甫畅,逸兴遄飞。爽籁发而清风生,纤歌凝而白云遏。睢园绿竹,气凌彭泽之樽;邺水朱华,光照临川之笔。四美具,二难并。穷睇眄于中天,极娱游于暇日。天高地迥,觉宇宙之无穷;兴尽悲来,识盈虚之有数。望长安于日下,目吴会于云间。地势极而南溟深,天柱高而北辰远。关山难越,谁悲失路之人?萍水相逢,尽是他乡之客。怀帝阍而不见,奉宣室以何年? 嗟乎!时运不齐,命途多舛。冯唐易老,李广难封。屈贾谊于长沙,非无圣主;窜梁鸿于海曲,岂乏明时?所赖君子见机,达人知命。老当益壮,宁移白首之心?穷且益坚,不坠青云之志。酌贪泉而觉爽,处涸辙以犹欢。北海虽赊,扶摇可接;东隅已逝,桑榆非晚。孟尝高洁,空余报国之情;阮籍猖狂,岂效穷途之哭! 勃,三尺微命,一介书生。无路请缨,等终军之弱冠;有怀投笔,慕宗悫之长风。舍簪笏于百龄,奉晨昏于万里。非谢家之宝树,接孟氏之芳邻。他日趋庭,叨陪鲤对;今兹捧袂,喜托龙门。杨意不逢,抚凌云而自惜;钟期既遇,奏流水以何惭? 呜乎!胜地不常,盛筵难再;兰亭已矣,梓泽丘墟。临别赠言,幸承恩于伟饯;登高作赋,是所望于群公。敢竭鄙怀,恭疏短引;一言均赋,四韵俱成。请洒潘江,各倾陆海云尔: 滕王高阁临江渚,佩玉鸣鸾罢歌舞。 画栋朝飞南浦云,珠帘暮卷西山雨。 闲云潭影日悠悠,物换星移几度秋。 阁中帝子今何在?槛外长江空自流。', '2021-01-23 12:18:48', '李四', 2);
INSERT INTO `article` VALUES (9, '《出师表》 · 诸葛亮', '先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。\r\n\r\n宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。\r\n\r\n侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。\r\n\r\n将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。\r\n\r\n亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。\r\n\r\n臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。\r\n\r\n先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。\r\n\r\n愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。\r\n\r\n今当远离,临表涕零,不知所言。', '2021-01-18 12:18:51', '李四', 2);
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名',
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
`age` int(11) NULL DEFAULT NULL COMMENT '年龄',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'zhangsan', '123', '张三', 20);
INSERT INTO `user` VALUES (2, 'lisi', 'admin', '李四', 19);
INSERT INTO `user` VALUES (3, 'wangwu', '110', '王五', 24);
2)拷贝静态资源
拷贝资源到项目中,将html文件改为jsp文件,在文件首部加入如下代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
3)拷贝jar包
在/WEB-INF
中建立lib
文件夹,拷贝【配套资料】中准备的jar包
4)jdbc.properties:
jdbc.username=root
jdbc.password=admin
jdbc.url=jdbc:mysql://localhost:3306/xb
jdbc.driverClassName=com.mysql.jdbc.Driver
5)DataSourceUtils:
package com.dfbz.utils;
import com.alibaba.druid.pool.DruidDataSource;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 数据源的工具类
*/
public class DataSourceUtils {
private static DataSource ds;
/**
* 在静态代码块中创建数据源对象
*/
static {
// 创建druid数据源
DruidDataSource dataSource = new DruidDataSource();
Properties prop = new Properties();
try {
// 加载配置文件
prop.load(DataSourceUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"));
} catch (IOException e) {
e.printStackTrace();
}
dataSource.setUsername(prop.getProperty("jdbc.username"));
dataSource.setPassword(prop.getProperty("jdbc.password"));
dataSource.setUrl(prop.getProperty("jdbc.url"));
dataSource.setDriverClassName(prop.getProperty("jdbc.driverClassName"));
ds=dataSource;
}
/**
* 得到数据源
*/
public static DataSource getDataSource() {
return ds;
}
/**
* 从连接池中得到连接对象
*/
public static Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 释放资源
*/
public static void close(Connection conn, Statement stmt, ResultSet rs) {
//关闭结果集
if (rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//关闭语句对象
if (stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//关闭连接对象
if (conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 关闭连接
*/
public static void close(Connection conn, Statement stmt) {
close(conn, stmt, null);
}
}
6)实体类:
- User:
/**
* user实体类
*/
public class User {
private Integer id;
private String username;
private String password;
private String name;
private Integer age;
}
- Article:
/**
* article实体类
*/
public class Article {
private Integer id;
private String title;
private String content;
private Date publishDate;
private String publishUser;
private Integer uId;
}
3.1.2 登录业务
1)修改前端页面
<form action="/login" method="post">
2)LoginServlet
package com.dfbz.controller;
import com.dfbz.entity.User;
import com.dfbz.service.UserService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 控制器层
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
// 专门用于处理user的业务
private UserService userService = new UserService();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 解决POST提交乱码问题
request.setCharacterEncoding("UTF-8");
// 1. 接受前端传递的参数
String username = request.getParameter("username");
String password = request.getParameter("password");
// 2.调用service进行业务处理
User user = userService.findByUsername(username);
if (user == null) {
// 说明用户名不存在
request.setAttribute("error","用户名不存在!");
request.getRequestDispatcher("/login.jsp").forward(request,response);
return;
}
if (!password.equals(user.getPassword())) {
// 说明密码错误
request.setAttribute("error","密码错误!");
request.getRequestDispatcher("/login.jsp").forward(request,response);
return;
}
// 说明用户名和密码都正确
HttpSession session = request.getSession();
// 存入到会话域中,方便其他地方取出当前登录用户的信息
session.setAttribute("loginUser",user);
// 重定向到ListServlet进行数据的查询,由ListServlet查询完数据后跳转到list.jsp页面
response.sendRedirect(request.getContextPath()+"/list");
}
}
3)UserService
package com.dfbz.service;
import com.dfbz.dao.UserDao;
import com.dfbz.entity.User;
/**
* 业务逻辑层
*/
public class UserService {
private UserDao userDao=new UserDao();
/**
* 根据用户名查询用户,如果没有查询到返回Null
* @param username
* @return
*/
public User findByUsername(String username) {
User user=userDao.findByUsername(username);
return user;
}
}
4)UserDao
package com.dfbz.dao;
import com.dfbz.entity.User;
import com.dfbz.utils.DataSourceUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 数据层
*/
public class UserDao {
/**
* 根据用户名查询用户,如果没有查询到返回Null
* @param username
* @return
*/
public User findByUsername(String username) {
try {
// 获取数据库连接
Connection connection = DataSourceUtils.getConnection();
// 获取预定义SQL语句对象
PreparedStatement ps = connection.prepareStatement("select * from user where username=?;");
// 设置占位符的值
ps.setString(1,username);
// 执行查询获取结果集
ResultSet rs = ps.executeQuery();
User user=null;
if(rs.next()){
Integer id = rs.getInt("id");
String dbUsername = rs.getString("username");
String password = rs.getString("password");
String name = rs.getString("name");
Integer age = rs.getInt("age");
user=new User(id,dbUsername,password,name,age);
}
return user;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
}
3.1.3 文章列表
1)业务分析
业务分析:
1)页面需要取出当前登录用户(从session取出)
2)来到list.jsp页面需要获取当前数据库中的所有文章信息,因此来到这个页面之前就应该准备好
3)文章列表是根据文章发布时间(publish_date)倒叙排序的
2)ListServlet:
package com.dfbz.controller;
import com.dfbz.entity.Article;
import com.dfbz.service.ArticleService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* 控制器层
*/
@WebServlet("/list")
public class ListServlet extends HttpServlet {
private ArticleService articleService=new ArticleService();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 查询数据库中所有的文章
List<Article> articleList=articleService.findAll();
// 将文章集合添加到域对象
request.setAttribute("articleList",articleList);
// 跳转到list.jsp页面
request.getRequestDispatcher("/list.jsp").forward(request,response);
}
}
3)ArticleService
package com.dfbz.service;
import com.dfbz.dao.ArticleDao;
import com.dfbz.entity.Article;
import java.util.List;
/**
* 业务逻辑层
*/
public class ArticleService {
private ArticleDao articleDao=new ArticleDao();
/**
* 查询所有文章,按照发布时间倒叙排序(最晚发布的在最前面)
* @return
*/
public List<Article> findAll() {
List<Article> articleList= articleDao.findAll();
return articleList;
}
}
4)ArticleDao
package com.dfbz.dao;
import com.dfbz.entity.Article;
import com.dfbz.entity.User;
import com.dfbz.utils.DataSourceUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 数据层
*/
public class ArticleDao {
/**
* 查询所有文章,按照发布时间倒叙排序(最晚发布的在最前面)
* @return
*/
public List<Article> findAll() {
try {
// 获取数据库连接
Connection connection = DataSourceUtils.getConnection();
// 获取预定义SQL语句对象
PreparedStatement ps = connection.prepareStatement("select * from article order by publish_date desc;");
// 执行查询获取结果集
ResultSet rs = ps.executeQuery();
List<Article> articleList=new ArrayList<>();
while(rs.next()){
Integer id = rs.getInt("id");
String title = rs.getString("title");
String content = rs.getString("content");
Date publishDate = rs.getDate("publish_date");
String publishUser = rs.getString("publish_user");
Integer uId = rs.getInt("u_id");
// 添加到articleList集合中
articleList.add(new Article(id,title,content,publishDate,publishUser,uId));
}
return articleList;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
}
5)list.jsp页面
<c:forEach items="${articleList}" var="article">
<hr>
<div style="padding:20px;background-color: #eee;border-radius: 10px;">
<h3 style="text-align: center;">${article.title}</h3>
<p>${article.content}</p>
<h4 style="text-align: right;">发布人:${article.publishUser}</h4>
<h4 style="text-align: left;">发布日期:${article.publishDate}</h4>
</div>
</c:forEach>
3.1.4 文章添加
1)add.jsp页面
修改表单提交路径和提交方式
<form action="/add" method="post">
2)AddServlet:
package com.dfbz.controller;
import com.dfbz.entity.Article;
import com.dfbz.entity.User;
import com.dfbz.service.ArticleService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Date;
/**
* 控制器层
*/
@WebServlet("/add")
public class AddServlet extends HttpServlet {
// 专门负责处理article的业务逻辑
private ArticleService articleService=new ArticleService();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置编码
request.setCharacterEncoding("UTF-8");
// 获取前端提交的参数
String title = request.getParameter("title");
String content = request.getParameter("content");
Article article = new Article();
article.setTitle(title);
article.setContent(content);
// 从session中获取用户
HttpSession session = request.getSession();
User loginUser = (User) session.getAttribute("loginUser");
// 发布人
article.setPublishUser(loginUser.getName());
// 发布时间
article.setPublishDate(new Date());
// 发布人id
article.setuId(loginUser.getId());
// 将文章添加到数据库
articleService.add(article);
// 重定向到ListServlet重新查询文章数据
response.sendRedirect(request.getContextPath()+"/list");
}
}
3)ArticleService
/**
* 添加文章到数据库
* @param article
*/
public void add(Article article) {
articleDao.add(article);
}
4)ArticleDao
/**
* 添加文章
* @param article
*/
public void add(Article article) {
try {
// 获取数据库连接
Connection connection = DataSourceUtils.getConnection();
// 获取预定义SQL语句对象
PreparedStatement ps = connection.prepareStatement("insert into article values(null,?,?,?,?,?)");
ps.setString(1,article.getTitle());
ps.setString(2,article.getContent());
// 转成时间戳
ps.setTimestamp(3, new Timestamp(article.getPublishDate().getTime()));
ps.setString(4,article.getPublishUser());
ps.setInt(5,article.getuId());
// 执行SQL语句
ps.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
3.2 使用过滤器完成权限校验
如上图所示,我们来到list.jsp
页面时需要显示当前登录人的姓名,换句话说,如果你没有登录那么就不能访问list.jsp
页面!
但仅仅是不能访问list.jsp
页面就完了吗?肯定不是的,如果我们不让客户端访问list.jsp
页面,他一样可以通过访问/list
请求来跳转到list.jsp
页面,而且用户如果没有登录应该也不能访问add.jsp
页面,以及/add
请求等,因此我们应该先分析需要拦截什么资源,需要放行什么资源;
3.2.1 分析需求
通过分析,我们发现静态资源全部需要放行,jsp页面除了login.jsp
放行外全部拦截,在请求访问除了/login
请求需要放行外,其他请求全部需要登录才可以访问;
需要拦截的请求:/list
、/list.jsp
、/add
、/add.jsp
;
3.2.3 编写LoginFilter拦截器
- LoginFilter:
package com.dfbz.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
// 拦截受限资源
@WebFilter({"/list", "/list.jsp", "/add", "/add.jsp"})
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
// 转成子接口,使功能更加强大
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 获取会话
HttpSession session = request.getSession();
// 获取当前登录用户
Object loginUser = session.getAttribute("loginUser");
if(loginUser == null){
// 说明用户没有登录
response.sendRedirect(request.getContextPath()+"/login.jsp");
return;
}
// 用户登录了: 放行
chain.doFilter(request,response);
}
@Override
public void destroy() {
}
}
3.3 使用过虑器完成特殊字符过滤
拦截器除了做请求的拦截与放行外,还可以做一些通用的操作,比如,解决request编码问题!
3.3.1 过滤字符表
DROP TABLE IF EXISTS `words`;
CREATE TABLE `words` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`word` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '敏感词汇',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of words
-- ----------------------------
INSERT INTO `words` VALUES (1, '笨蛋');
INSERT INTO `words` VALUES (2, '傻蛋');
3.3.2 字符过滤器
package com.dfbz.filter;
import com.dfbz.utils.DataSourceUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 特殊字符过滤
* 拦截所有请求
*/
@WebFilter("/*")
public class TextFilter implements Filter {
private List<String> words = new ArrayList<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 加载敏感词
try {
// 获取数据库连接
Connection connection = DataSourceUtils.getConnection();
// 获取预定义SQL语句对象
PreparedStatement ps = connection.prepareStatement("select * from words;");
// 执行查询获取结果集
ResultSet rs = ps.executeQuery();
while (rs.next()) {
// 添加到词汇集合中
words.add(rs.getString("word"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
// 需要增强request的getParameter()方法
ServletRequest requestProxy = (ServletRequest) Proxy.newProxyInstance(
TextFilter.class.getClassLoader(), // 和目标对象(ServletRequest)一样的类加载器
request.getClass().getInterfaces(), // 目标对象(ServletRequest)实现的所以接口的字节码对象
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取执行的方法名
String methodName = method.getName();
if ("getParameter".equals(methodName)) {
//增强getParameter方法
// 原来的功能
String returnVal = (String) method.invoke(request, args);
for (String word : words) {
if (returnVal.contains(word)) {
// 如果包含有敏感词就把敏感词替换为***
returnVal = returnVal.replaceAll(word, "***");
}
}
// 相当于修改了request的getParameter()的返回值
return returnVal;
}
// 其他的方法直接保留原有的功能
return method.invoke(request, args);
}
}
);
// 放行请求,传递代理对象给具体的servlet
chain.doFilter(requestProxy, response);
}
@Override
public void destroy() {
}
}
再次添加文章: