学习目标:
学习
在前后端分离成为主流的今天,JSP(Java Server Pages)似乎成了 "时代的眼泪"。但回溯 2000-2015 年的 Java Web 黄金期,它却是无数开发者入门动态网页的第一块敲门砖 —— 不用手动拼接 HTML 标签,直接在网页里写 Java 代码,就能轻松实现用户登录、数据查询等功能。
然而,这种 "便捷性" 的背后,藏着从底层原理衍生的安全漏洞。今天我们就从 JSP 的本质讲起,拆解它的运行流程、核心逻辑,以及那些至今仍威胁着遗留系统的风险点。
一、JSP 的本质:
很多人误以为 JSP 是独立的技术,其实它只是Servlet 的 "语法糖"。要理解 JSP,必须先搞懂 Servlet 的痛点 ——
Servlet 是纯 Java 类,要输出 HTML 页面,得用out.println()一行行拼接标签,比如:
java
// 纯Servlet代码,开发效率极低
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("");
out.println("
");out.println("欢迎," + username); // 拼接动态内容
out.println("");
out.println("");
而 JSP 允许开发者在 HTML 中直接嵌入 Java 代码,比如:
jsp
欢迎,<%= username %>
核心真相:JSP 文件不会被直接执行。服务器会先把它翻译成 Servlet 源文件(.java),再编译成字节码文件(.class),最终运行的还是 Servlet。我们写的 JSP 代码,只是让服务器帮我们自动完成了 "Java 代码→Servlet" 的转换工作。
二、JSP 完整运行流程:4 步从请求到响应
当你在浏览器输入http://xxx.com/user.jsp时,服务器(比如 Tomcat)会按以下 4 个步骤处理,每一步都藏着关键技术细节:
1. 请求拦截:服务器判断是否需要编译
服务器收到 JSP 请求后,会先去 "工作目录"(比如 Tomcat 的work/Catalina/localhost/项目名/)找对应的字节码文件(如user_jsp.class):
如果找到,且原 JSP 文件没有被修改过,直接跳过翻译和编译,执行字节码文件;
如果没找到(第一次请求),或原 JSP 文件被修改过,就进入 "翻译→编译" 阶段。
小知识:开发时修改 JSP 后不用重启服务器,就是因为服务器会检测文件修改时间,自动重新翻译编译。
2. 翻译阶段:JSP→Servlet 源文件(.java)
服务器的 JSP 引擎(比如 Tomcat 的 Jasper)会逐行解析 JSP 代码,把不同类型的内容转换成 Java 代码:
JSP 语法
作用
转换后的 Java 代码示例
<% 代码 %>
嵌入 Java 脚本(如循环、判断)
直接放在 Servlet 的_jspService()方法中
<%= 变量 %>
输出 Java 变量到 HTML
out.print(变量);
<%@ page ... %>
页面指令(如导入包、设置编码)
转换成类的注解或静态代码块
动态包含其他页面
request.getRequestDispatcher("xxx.jsp").include(request, response);
普通 HTML 标签
静态内容
用out.write("HTML标签");逐行输出
举个实际转换例子:原 JSP 代码:
jsp
<%@ page import="java.util.Date" %>
当前时间:<%= new Date() %>
<% if (new Date().getHours() < 12) { %>
早上好!
<% } %>
转换后的 Servlet 核心代码(简化版):
java
// 继承服务器提供的基础Servlet类
public class index_jsp extends org.apache.jasper.runtime.HttpJspBase {
// 处理请求的核心方法
public void _jspService(HttpServletRequest request, HttpServletResponse response) {
// 初始化输出流(JSP的out内置对象)
javax.servlet.jsp.JspWriter out = pageContext.getOut();
try {
response.setContentType("text/html");
out.write("\n");
out.write("
\n");out.write(" 当前时间:");
out.print(new Date()); // 对应<%= new Date() %>
out.write("\n");
if (new Date().getHours() < 12) { // 对应<% if (...) %>
out.write("
早上好!
\n");}
out.write(" \n");
out.write("");
} catch (Exception e) {
// 异常处理...
}
}
}
3. 编译阶段:Servlet 源文件→字节码(.class)
JSP 引擎调用 Java 编译器(javac),把生成的.java文件编译成.class字节码文件。这个字节码文件和普通 Java 类一样,会被服务器的类加载器加载到内存。
4. 执行阶段:生成响应返回浏览器
加载后的字节码文件会执行_jspService()方法(对应 Servlet 的service()方法),完成 3 件事:
通过request对象获取用户输入(如表单参数、URL 参数);
执行嵌入的 Java 代码(如查询数据库、处理业务逻辑);
通过out对象把动态内容(如时间、用户信息)嵌入 HTML,再通过response对象返回给浏览器。
三、JSP 核心逻辑:3 个简化开发的设计亮点
JSP 能成为 Java Web 的 "初代顶流",核心是它解决了 Servlet 的 3 个痛点,这些设计也是理解其漏洞的关键:
1. 内置对象:不用 new 就能用的 "工具集"
JSP 预定义了 9 个内置对象,开发者不用手动创建,直接在代码中使用,大幅减少重复代码。最常用的 5 个:
内置对象
类型
作用
request
HttpServletRequest
获取用户请求数据(如参数、Cookie)
response
HttpServletResponse
设置响应数据(如跳转、设置 Cookie)
session
HttpSession
存储用户会话信息(如登录状态)
out
JspWriter
向浏览器输出内容
pageContext
PageContext
获取其他内置对象,管理页面属性
比如要获取用户输入的 "用户名",直接写:
jsp
<% String username = request.getParameter("username"); %>
不用像 Servlet 那样手动声明HttpServletRequest对象。
2. 指令与动作元素:简化配置与页面复用
JSP 提供了专门的语法来处理配置和页面交互,不用写复杂的 Java 代码:
指令元素:以<%@开头,用于页面全局配置,比如<%@ page contentType="text/html;charset=UTF-8" %>(设置页面编码)、<%@ include file="header.jsp" %>(静态包含头部页面);
动作元素:以
3. 生命周期自动管理:
JSP 对应的 Servlet 生命周期完全由服务器托管,开发者不用关心类的加载、实例化和销毁:
初始化阶段:第一次被访问时,服务器调用jspInit()方法(可重写,用于加载配置、初始化资源);
请求处理阶段:每次请求触发_jspService()方法(核心,处理业务逻辑);
销毁阶段:服务器关闭或应用卸载时,调用jspDestroy()方法(可重写,用于释放资源,如关闭数据库连接)。
学习时间:
学习时间为学习时间
学习时间
筋肉人
为学习时间
future
内容为笔记【有时比较抽象,有时比较过于详细,请宽恕。作者可能写的是仅个人笔记,筋肉人future】
学习产出:
技术笔记 1遍
有错误请指出,作者会及时改正