接上一遍Filter基础知识,本文在两个应用场景下使用FIlter过滤器,加深理解应用。
本文目录
Filter案例
练习场景下使用过滤器,利用过滤器完成一些通用的操作。
一.案例1_登录验证
我们先正常的将登录流程走一遍,再对案例进行分析、实现。
1.0 正常的登录流程
用户登录界面:
登录成功:
比较简单的逻辑:
1.1 需求
- 访问用户登录后的资源。验证其是否登录
- 如果登录了,则直接放行。
- 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。
案例分析:
过滤器核心代码:LoginFilter.java
需要注意的是:
- 拦截请求过程中需要判断用户请求是否包含访问登录相关资源路径,要注意排除掉
css/js/图片/验证码
等其它资源,否则请求这些资源也会被拦截掉,页面的样式就会丢失。
/**
* 登录验证的过滤器
*/
@WebFilter("/*")
public class LoginFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//0.强制转换,因为ServletRequest接口的子接口HttpServletRequest才有获取请求路径的方法
HttpServletRequest request = (HttpServletRequest) req;
//1.获取资源请求路径
String uri = request.getRequestURI();
//2.判断是否包含登录相关资源路径,要注意排除掉 css/js/图片/验证码等资源
if("/login.jsp".contains(uri) || "/LoginServlet".contains(uri) || "/css/".contains(uri) || "/js/".contains(uri) || "/fonts/".contains(uri) ){
//包含,用户就是想登录。放行
chain.doFilter(req, resp);
}else{
//不包含,需要验证用户是否登录
//3.从获取session中获取user
Object user = request.getSession().getAttribute("user");
if(user != null){
//登录了。放行
chain.doFilter(req, resp);
}else{
//没有登录。跳转登录页面
request.setAttribute("login_msg","您尚未登录,请登录!");
request.getRequestDispatcher("/login.jsp").forward(request,resp);
}
}
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
1.2 代码实现
1.2.1 目录结构
登录案例可以参考另一篇博文:【JavaWeb】综合案例:用户登录.
1.2.2 数据库准备
1.2.3 页面login.jsp和index.jsp
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<form action="/LoginServlet" method="post">
用户账号:<input type="text" placeholder="请输入账号" name="username"><br>
用户密码:<input type="text" placeholder="请输入密码" name="password"><br>
<input type="submit" value="登录">
</form>
<!--提示信息-->
<p>${login_msg}</p>
</body>
</html>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户主页</title>
<!--导入css文件-->
<link rel="stylesheet" type="text/css" href="css/style.css" />
</head>
<body>
<p>登录成功,用户名${user.username}欢迎您!</p>
<form action="/UpdateServlet" method="post">
<input type="hidden" name="id" value="${user.id}">
<label for="introduce">自我介绍:</label>
<input type="text" name="introduce" id="introduce" value="${user.introduce}">
<input type="submit" value="修改自我介绍">
</form>
</body>
</html>
style.css
label{
color: red;
}
1.2.4 dao层
UserDao.java
/**
* 操作数据库的类
*/
public class UserDao {
//声明JDBCTemplate对象共用
private JdbcTemplate jdbcTemplate = new JdbcTemplate(JDBCUtils.getDataSource());
/**
* 登录方法
*
* @param loginUser
* @return user包含用户全部数据,没有查询到,放回null
*/
public User login(User loginUser) {
try {
//1.编写sql语句
String sql = "select * from info where username=? and password=?";
//2.调用JDBCTemplate对象的查询方法
User user = jdbcTemplate.queryForObject(sql //sql语句
, new BeanPropertyRowMapper<User>(User.class) //RowMapper接口实现类,将数据自动封装成指定对象
, loginUser.getUsername() //sql参数
, loginUser.getPassword()); // sql参数
return user;
} catch (Exception e) {
e.printStackTrace();//以后会将异常记录到日志文件中
return null;
}
}
/**
* 修改用户自我介绍
*
* @param updateUser
* @return
*/
public User update(User updateUser) {
//1.编写sql语句
String sql = "update info set introduce = ? where id = ? ";
//2.调用JDBCTemplate对象的方法
jdbcTemplate.update(sql,updateUser.getIntroduce(),updateUser.getId());
//3.再次查询
return jdbcTemplate.queryForObject("select * from info where id = ?" //sql语句
, new BeanPropertyRowMapper<User>(User.class) //RowMapper接口实现类,将数据自动封装成指定对象
, updateUser.getId()); // sql参数
}
}
1.2.5 domain层
User.java
/**
* 用户实体类
*/
public class User implements Serializable {
private int id;
private String username;
private String password;
private String introduce;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getIntroduce() {
return introduce;
}
public void setIntroduce(String introduce) {
this.introduce = introduce;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", introduce='" + introduce + '\'' +
'}';
}
}
1.2.6 util层
JDBCUtils.java
/**
* JDBC工具类,使用Druid连接池
*/
public class JDBCUtils {
private static DataSource ds;
static {
try {
//1.加载配置文件
Properties prop = new Properties();
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
prop.load(is);
//2.初始化数据库连接池对象
ds = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接池对象
*
* @return
*/
public static DataSource getDataSource() {
return ds;
}
/**
* 获取数据库连接对象
*
* @return
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
1.2.7 web层
过滤器:LoginFilter.java
Servlet资源:
LoginServlet.java
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置解码格式
req.setCharacterEncoding("utf-8");
//2.获取请求体参数username和password
String username = req.getParameter("username");
String password = req.getParameter("password");
//3.将username和password封装成User对象
User loginuser = new User();
loginuser.setUsername(username);
loginuser.setPassword(password);
//4.调用UserDao的login方法
UserDao dao = new UserDao();
User user = dao.login(loginuser);
//5.判断user是否为null
if (user==null){
//登录失败跳转到登录页面
req.setAttribute("login_msg","登录失败!用户名或密码错误。");
req.getRequestDispatcher("/login.jsp").forward(req,resp);
}else {
//登录成功,将用户信息存在Session中,跳转index.jsp
HttpSession session = req.getSession();
session.setAttribute("user",user);
resp.sendRedirect("/index.jsp");
}
}
}
UpdateServlet.java
@WebServlet(name = "UpdateServlet", urlPatterns = "/UpdateServlet")
public class UpdateServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1.设置解码格式
request.setCharacterEncoding("utf-8");
//2.获取请求体参数username和password
String id = request.getParameter("id");
String introduce = request.getParameter("introduce");
//3.将id和introduce封装成User对象
User Update_user = new User();
Update_user.setId(Integer.parseInt(id));
Update_user.setIntroduce(introduce);
//4.调用UserDao的update方法
UserDao dao = new UserDao();
User user = dao.update(Update_user);
//5.显示修改后用户的自我介绍
request.setAttribute("user", user);
request.getRequestDispatcher("/index.jsp").forward(request, response);
}
}
1.3 测试
1.用户未登录,直接访问index.jsp:将会跳转到登录界面login.jsp,并给出提示信息
未登录,直接访问LoginServlet、updateSerlvet资源同样如此。
2.当前用户已经登录,即session域中存在user的键值对,再直接访问index.jsp、LoginServlet、updateSerlvet资源就无须执行登录业务逻辑了:
登录成功,此时重开一个新的标签页,直接访问index.jsp:
通过F12查看浏览器控制台,网络抓包,可以看到直接进入了用户主页,不会再访问LoginServlet资源。
二.案例2_敏感词汇过滤
使用上面登录案例,来实现敏感词汇的过滤。用户修改自我介绍的时候,如果包含了《敏感词汇.txt》里面的敏感词汇,则会将词汇替换为 ***
。
敏感词汇.txt
笨蛋
坏蛋
先来看下效果:
修改自我介绍:
点击修改自我介绍,将会回显用户修改后的自我介绍:
比较简单的例子,旨在能理解明白过滤器进行敏感词汇过滤的逻辑,下面就来实现这个效果。
2.1 需求分析
需求:
- 1.对【用户登录案例】修改的自我介绍数据进行敏感词汇过滤
- 2.敏感词汇参考《敏感词汇.txt》
- 3.如果是敏感词汇,替换为
***
过滤分析图
难点分析
- 对req对象进行增强。使用动态代理,增强获取参数相关方法,对请求参数中敏感词汇进行过滤;
- 放行。传递代理对象
代码模式不熟悉的小伙伴,可以先阅读下另一篇的博文:代理模式:动态代理、静态代理.
2.2 代码实现
2.2.1 目录结构
大部分代码与上面登录案例一致。
2.2.2 核心代码
核心代码就是:敏感词汇过滤器SensitiveWordsFilter.java
- 使用缓冲字符输入流获取 《敏感词汇.txt》的词汇内容
- 增强方法逻辑中,使用 “
***
” 替换掉请求参数里面的敏感词汇,并返回替换后的值
/**
* 敏感词汇过滤器
*/
@WebFilter("/*")
public class SensitiveWordsFilter implements Filter {
//敏感词汇集合
private List<String> list = new ArrayList<String>();
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//1.创建代理对象,增强getParameter方法
ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强getParameter方法
//判断是否是getParameter方法
if(method.getName().equals("getParameter")){
//增强返回值
//获取返回值
String value = (String) method.invoke(req,args);
if(value != null){
for (String str : list) {
if(value.contains(str)){
value = value.replaceAll(str,"***");
}
}
}
return value;
}
//考虑到可能使用其它获取请求参数的方法,那就都需要进行增强,思路是一样的,这里不展开了。
//判断方法名是否是 getParameterMap
//判断方法名是否是 getParameterValue
return method.invoke(req,args);
}
});
//2.放行
chain.doFilter(proxy_req, resp);
}
public void init(FilterConfig config) throws ServletException {
try{
//1.获取文件真实路径
ServletContext servletContext = config.getServletContext();
String realPath = servletContext.getRealPath("/WEB-INF/classes/敏感词汇.txt");
//2.读取文件
BufferedReader br = new BufferedReader(new FileReader(realPath));
//3.将文件的每一行数据添加到list中
String line = null;
while((line = br.readLine())!=null){
list.add(line);
}
//释放流
br.close();
System.out.println(list);
}catch (Exception e){
e.printStackTrace();
}
}
public void destroy() {
}
}
测试与前面演示效果一致,不赘述了。
到这两个过滤器的小案例就完成了,理解为主,当前还可以进行更多过滤器的延伸,相信小伙伴们都可以信手拈来~