在Java Web开发的世界里,Servlet、过滤器(Filter)和监听器(Listener)构成了绝大多数Web应用的基石。理解它们的工作原理与生命周期,是掌握Java服务端开发的关键一步。本文将通过通俗的讲解和实践演示,带你深入理解这三大核心组件是什么、如何工作,并探讨它们在常规开发与安全/渗透/逆向领域的一些特殊应用场景。
一、核心基石:Servlet详解
Servlet是一套运行在Web服务器上的Java程序,专门负责处理客户端的请求(Request)并生成响应(Response)。它是连接浏览器与服务器端业务逻辑(如数据库、微服务)的桥梁。
1. 开发环境准备
在开始编码前,需要准备以下环境:
- IDE:安装并配置好IntelliJ IDEA。
- 运行环境:安装JDK和Tomcat服务器。
- 项目配置:在IDEA中创建一个Java Enterprise项目,并正确配置Tomcat的路径。
2. 创建第一个Servlet
定义Servlet后,需要为其配置访问路由。主要有两种方式:
方法一:传统web.xml配置
- 创建Java类继承
javax.servlet.http.HttpServlet。
- 重写
doGet() 或 doPost() 方法。
- 在
WEB-INF/web.xml 文件中进行路由映射。
<servlet>
<servlet-name>MyFirstServlet</servlet-name>
<servlet-class>com.example.servlet.MyFirstServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyFirstServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
方法二:注解配置(Servlet 3.0+ 推荐)
在Servlet类上直接使用 @WebServlet 注解,更加简洁。
@WebServlet("/hello")
public class MyFirstServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
// 处理GET请求的逻辑
}
}
3. Servlet的生命周期
Servlet从创建到销毁经历几个关键阶段,通过重写对应方法可以干预这些过程:
- init():初始化。通常只在Servlet第一次被访问时执行一次,适合加载资源。
- service():服务。每次请求到达时调用,并根据请求类型(GET/POST)分派到对应的
doGet() 或 doPost() 方法。
- destroy():销毁。在Web应用卸载或服务器关闭时执行一次,适合释放资源。
在IDEA中,可以在Servlet类里使用快捷键 Alt + Insert 快速生成这些生命周期方法的骨架。
4. 接收请求与返回响应
这是Servlet的核心日常操作,主要依赖于两个对象:
二、请求守门人:过滤器(Filter)
过滤器(Filter)在请求到达目标Servlet之前,或响应返回客户端之后,对数据进行拦截和处理,犹如一道“安检门”。
1. 过滤器的应用场景
- 权限控制:检查用户登录状态。
- 统一编码:设置请求和响应的字符集,解决乱码问题。
- 敏感词过滤:清洗用户提交的文本。
- 日志记录:记录请求的IP、时间、URL等信息。
2. 创建与配置过滤器
创建一个类实现 javax.servlet.Filter 接口,实现其三个核心方法。
@WebFilter("/*") // 使用注解拦截所有请求
public class XssFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
// 初始化,服务器启动时执行一次
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 1. 请求到达Servlet前的逻辑(如参数清洗)
// ...
// 2. 放行,让请求继续传递
chain.doFilter(request, response);
// 3. 响应返回客户端前的逻辑
// ...
}
@Override
public void destroy() {
// 销毁,服务器关闭时执行一次
}
}
同样,也可以通过 web.xml 进行配置,配置方式与Servlet类似。
3. 过滤器在安全领域的角色
过滤器在红蓝对抗中是高频出现的组件,理解其机制对安全/渗透/逆向工作至关重要。
- 攻击方(红队)视角:常被用于植入内存马。攻击者通过漏洞动态注册一个恶意Filter,拦截特定请求执行命令。只要服务器不重启,这个后门就常驻内存,难以通过文件查杀发现。
- 防御方(蓝队)视角:用于实现访问控制和内存马清理。通过遍历和分析当前应用已注册的所有Filter,可以识别和移除恶意的内存马。
- 实践案例:实现一个简易的XSS过滤器,在
doFilter 方法中,对请求参数里的 <script> 标签进行替换或删除,防止恶意脚本注入。
三、事件观察员:监听器(Listener)
监听器(Listener)用于“监听”Web应用内部特定对象(如 ServletContext、HttpSession、ServletRequest)的创建、销毁及属性变更等事件,并在事件发生时自动执行代码。
1. 监听器的应用场景
- 统计在线人数:监听
HttpSession 的创建与销毁。
- 应用初始化:在
ServletContext 创建时(应用启动),加载全局配置或初始化连接池。
- 请求日志:监听
ServletRequest 的创建,记录每一次访问。
2. 监听器的分类与创建
根据监听对象,主要分为三类:
- 监听域对象生命周期:如
ServletContextListener、HttpSessionListener、ServletRequestListener。
- 监听域对象属性变化:如
ServletContextAttributeListener。
- 监听Session对象绑定事件:如
HttpSessionBindingListener(由被绑定的对象自身实现)。
创建方式同样支持 @WebListener 注解或 web.xml 配置。
案例演示:统计在线用户数
@WebListener
public class OnlineUserListener implements HttpSessionListener {
private int onlineCount = 0;
@Override
public void sessionCreated(HttpSessionEvent se) {
onlineCount++;
// 将计数存入ServletContext,供全局访问
se.getSession().getServletContext().setAttribute("onlineCount", onlineCount);
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
onlineCount--;
se.getSession().getServletContext().setAttribute("onlineCount", onlineCount);
}
}
3. 监听器与安全
监听器也是内存马的常见载体,在安全/渗透/逆向场景下需要重点关注。
- 代码审计:检查
ServletContextListener 的 contextInitialized() 方法,是了解应用启动时加载了哪些资源或潜在“后门”的关键。
- 红蓝对抗:攻击者可能动态注册一个
ServletRequestListener,使其在每次请求时都能执行恶意代码;防御方则需要具备检测和清除此类内存马的能力。
总结
- Servlet 是业务处理的核心,直接负责请求与响应。
- Filter 是请求/响应的预处理与后处理器,扮演着拦截与增强的角色。
- Listener 是应用内部事件的观察者,用于响应状态变化并执行自动化任务。
这三者协同工作,构成了Java Web应用从请求接收到响应返回的完整处理链条。无论是进行常规的Java业务开发,设计稳健的后端 & 架构,还是深入底层进行安全研究与防御,透彻理解它们都是不可或缺的基础。希望本文能帮助你建立起对这三个核心组件的清晰认知,并在实践中得心应手。
参考资料
[1] 第42天-JavaEE筑基:深入理解Servlet、过滤器与监听器, 微信公众号:mp.weixin.qq.com/s/8Uc0GxtuOkzl4j4tPuh5hw
版权声明:本文由 云栈社区 整理发布,版权归原作者所有。