(Java)过滤器Filter及“登录案例”
程序员文章站
2022-06-18 17:41:55
...
1. 过滤器Filter
1.1 Filter概念
-
过滤器: 过筛子,符合条件的过去,不符合条件不能过去.
-
生活比喻: 安检,检查安全的人与物才可以通过放行
-
程序: 客户端需要访问服务器的目标资源,在客户端和服务器资源之间设置过滤器, 符合要求放行
2. Filter的入门程序
-
需求:
- 浏览器要访问HelloServlet
- 途径过滤期MyFilter, 若MyFilter放行,可执行访问到HelloServlet; 若不放行,无法访问HelloServlet
-
执行图解:
-
实现步骤:
- 编写Servlet, 在web.xml配置Servlet访问路径
- 编写Filter,定义类, 实现接口Filter, 实现接口中抽象方法, 在web.xml配置Filter的访问过滤路径
代码: HelloServlet
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//控制台打印
System.out.println("HelloServlet 执行了");
//浏览器响应
response.getWriter().println("Hello Servlet");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
代码: MyFilter
public class MyFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("MyFilter 执行了");
//执行过滤后放行, 进行后续的请求访问
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
代码: 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">
<!--配置servlet-->
<filter>
<filter-name>my</filter-name>
<filter-class>com.itheima.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>my</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.itheima.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
3. Filter的生命周期
-
过滤器对象的创建,是Tomcat服务器启动
- init(FilterConfig config)过滤器对象被创建的时候调用,FilterConfig 对象tomcat引擎创建
-
过滤器执行过滤的方法,过滤被访问资源的时候,必须是被过滤器过滤器的资源
- doFilter(request,response)
-
过滤器对象销毁的方法,销毁之前调用,服务器关闭
- destroy()
package cn.itcast.web.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * 1 创建类 实现Filter接口 * 2 在web.xml中注册 * 3 在web.xml中映射拦截的路径 */ public class Filter1 implements Filter { String encoding; public Filter1() { System.out.println("======= 1 执行Filter1的构造方法... ..."); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("======= 2 执行Filter1的初始化 init 方法... ..."); encoding = filterConfig.getInitParameter("encoding"); System.out.println("编码集: " + encoding); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("======= 3 执行Filter1的执行过滤 doFilter 方法... ... 编码集:" + encoding); // 因为父类不能调用子类的方法,所以向下转型 HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String uri = httpServletRequest.getRequestURI(); System.out.println("执行过滤器1 ... ... 访问的路径: " + uri); // 允许访问目标资源 : 放行 filterChain.doFilter(servletRequest, servletResponse); //System.out.println("放行之后执行... ..."); } @Override public void destroy() { System.out.println("======= 4 执行Filter1的销毁 destroy 方法... ..."); } }
4. Filter的url-pattern配置
- 完全匹配
<!--
过滤资源,只有hello
绝对匹配 <url-pattern>/hello</url-pattern>
只能过滤指定的资源
-->
<url-pattern>/hello</url-pattern>
- 目录匹配
<!--
目录匹配,过滤器中最常见
/abc/* 过滤abc目录下的所有资源
一次过滤一片资源
过滤后台资源 /admin/*
-->
<url-pattern>/admin/*</url-pattern>
- 后缀名匹配
<!--
后缀名匹配,一般不使用
*.jsp 访问所有jsp文件
-->
<url-pattern>*.jsp</url-pattern>
5. 注解配置Filter
@WebFilter(urlPatterns="/过滤资源")
@WebFilter("/*")
public class ChinaFilter implements Filter {
//代码省略...
}
package cn.itcast.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
// 方式一
//@WebFilter(urlPatterns = {"/hello.jsp", "/helloServlet"})
// 方式二
// @WebFilter(urlPatterns = {"/hello.jsp"})
// 方式三
// @WebFilter(urlPatterns = "/hello.jsp")
// 方式四
@WebFilter("/hello.jsp")
public class Filter3 implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 因为父类不能调用子类的方法,所以向下转型
HttpServletRequest httpServletRequest = (HttpServletRequest) req;
String uri = httpServletRequest.getRequestURI();
System.out.println("执行过滤器3 ... ... 访问的路径: " + uri);
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
6. Filter 处理中文乱码
- 需求:
使用过滤器Filter, 处理所有请求的中文乱码
- 代码: ChinaServlet
@WebServlet("/china")
public class ChinaServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("ChinaServlet , 获取请求参数 username = " + username);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 代码: ChinaFilter
@WebFilter("/*")
public class ChinaFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//在过滤器中,设置request对象的编码表
req.setCharacterEncoding("utf-8");
//设置response缓冲区的编码表,通知浏览器的解码
resp.setContentType("text/html;charset=utf-8");
System.out.println("ChinaFilter执行了, 过滤中文,设置编码utf-8");
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
7. 过滤器链 FilterChain的执行过程
Filter中的过滤器链 FilterChain: 由Tomcat引擎创建对象
作用: 维护过滤器执行顺序
小结: Servlet中doGet方法参数 request, response对象, 由Tomcat引擎创建, 经过多个过滤器一层层传递
8. 多个过滤器的先后执行顺序
-
web.xml配置:
和配置文件的编写顺序决定运行的顺序,准确的说法是,根据mapping的顺序决定 (由上到下执行)
-
注解开发
注解开发没有配置文件
按照类名的自然顺序决定: A-B-C
如果存在配置文件,配置文件优先
9. 过滤器案例: 登录验证(权限校验)
- 问题描述:在登录的时候,若登录成功会跳转到登录成功的页面,该页面有一个网址。若用户没有经过登录直接访问该网址,应该被拦截下来,让用户去登录。
- 方法分析:
- 在登录成功网址前加个过滤器,判断是否登录,若登录则可以放行,若没有,则跳转到登录页面并告知用户需要登录
- 判断是否登录的方法:在登录Servlet中,若登录成功,将用户名放在session域中;在过滤器中从session域中取出用户名,若能取到,就证明已经登录了,放行。
代码
- 登录页面
1. 访问项目的资源。验证其是否登录
2. 如果登录了,则直接放行。
3. 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。
9.2 实现
- 登录页面(非常丑,示例一下)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<div style="color: red;size: 10px">${message}</div>
<form action="${pageContext.request.contextPath}/login" method="post">
用户名:<input type="text" name="username"><br>
密的码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
- 登录成功页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>欢迎页面</title>
</head>
<body>
<div style="font-size: 28px">欢迎登录</div>
</body>
</html>
- LoginServlet
package 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;
@WebServlet(urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取表单提取的数据
String username = request.getParameter("username");
String password = request.getParameter("password");
// 判断是否 登录成功
if (username.equals("tom") && password.equals("123")){
//登录从成功,用户名存储在session中
request.getSession().setAttribute("username",username);
// 重定向,需要些web应用名称
response.sendRedirect(request.getContextPath()+"/resource/welcome.jsp");
}else {
//登录失败,回到登录页面,告知用户
request.setAttribute("message","登录失败,用户名或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request,response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 过滤器
package filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(urlPatterns = "/resource/*")
public class loginFilter implements Filter {
/**
* 登录的过滤: 不能直接访问登录成功页面,必须登录后才行
* 所以要判断是否登录,若登录,正常放行
* 若没有登录直接输入了登录成功页面的网址,那么就跳转到登录页面
* 那么如何判断是否登录了呢?
* 从session域中取出username
* 为什么从session域中取呢,因为在登录Servlet中,登录成功后将用户名存储到了session中
* 如果为空,就拦截(页面跳转到登录界面)
* 如果不为空,走正常程序()
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// servletRequest转成HttpServletRequest子接口
HttpServletRequest request = (HttpServletRequest)servletRequest;
//servletResponse转成HttpServletResponse
HttpServletResponse response = (HttpServletResponse)servletResponse;
// 获取session域中的username
String username = (String)request.getSession().getAttribute("username");
if (username == null){
// 没有登录,退回到登录页面并告知用户
request.getSession().setAttribute("message","请先登录");
request.getRequestDispatcher("/login.jsp").forward(request,response);
}else {
// 检测到登录,放行
filterChain.doFilter(request,response);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
9.3 验证
- 没有登录时,访问登录成功页面: