目录导航
Servlet
Java EE指的是Java平台企业版(Java Platform Enterprise Edition),之前称为Java 2 Platform, Enterprise Edition(J2EE),2017 年的 9 月Oracle将Java EE 捐赠给 Eclipse 基金会,由于Oracle持有Java商标原因,Eclipse基金于2018年3月将Java EE更名为Jakarta EE。
Java EE和Servlet版本
| Java SE/JDK版本 | Java EE版本 | Servlet版本 | 发布时间 |
|---|---|---|---|
| / | / | Servlet 1.0 | (1997年6月) |
| JDK1.1 | / | Servlet 2.0 | / |
| / | / | Servlet 2.1 | (1998年11月) |
| JDK1.2 | J2EE 1.2 | Servlet 2.2 | (1999年12月12日) |
| JDK1.2 | J2EE 1.3 | Servlet 2.3 | (2001年9月24日) |
| JDK1.3 | J2EE 1.4 | Servlet 2.4 | (2003年11月11日) |
| JDK1.5 | Java EE 5 | Servlet 2.5 | (2006年5月11日) |
| JDK1.6 | Java EE 6 | Servlet 3.0 | (2009年12月10日) |
| / | Java EE 7 | Servlet 3.1 | (2013年5月28日) |
| / | Java EE 8 | Servlet 4.0 | (2017年8月31日) |
| / | Jakarta EE8 | Servlet 4.0 | (2019年8月26日) |
由上表可知Java EE并不是Java SE的一部分(JDK不自带),Java EE的版本也不完全是对应了JDK版本,我们通常最为关注的是Java EE对应的Servlet版本。不同的Servlet版本有着不一样的特性,Servlet容器(如GlassFish/Tomcat/Jboss)也会限制部署的Servlet版本。Java流行的Spring MVC(基于Servlet机制实现)、Struts2(基于Filter机制实现)等Web框架也是基于不同的Java EE版本封装了各自的框架。
Servlet 3.0 规范、Servlet 3.1 规范、Servlet 4.0 规范
Tomcat Servlet版本
| Tomcat版本 | Java EE版本 | Servlet版本 | JSP版本 | 发布时间 |
|---|---|---|---|---|
| Tomcat 5.0.0 + | J2EE 1.4 | Servlet 2.4 | JSP 2.0 | (2003年11月24日) |
| Tomcat 6.0.0 – Tomcat 6.0.44 | Java EE 5 | Servlet 2.5 | JSP 2.1 | (2006年5月11日 – 2007年9月11日) |
| Tomcat 7.0.0 – Tomcat 7.0.25 | Java EE 6 | Servlet 3.0 | JSP 2.2 | (2009年12月10日 – 2011年2月6日) |
| Tomcat 8.0.0 + | Java EE 7 | Servlet 3.1 | JSP 2.3 | (2013年5月28日) |
| Tomcat 9.0.0 + | Java EE 8 | Servlet 4.0 | JSP 2.3 | (2017年2月5日) |
| Tomcat 10.0.0 + | Jakarta EE8 | Servlet 4.0 | JSP 2.3 | / |
参考: Web Application Specifications
Servlet是在 Java Web容器中运行的小程序,通常我们用Servlet来处理一些较为复杂的服务器端的业务逻辑。Servlet是Java EE的核心,也是所有的MVC框架的实现的根本!
基于Web.xml配置
Servlet3.0 之前的版本都需要在web.xml 中配置servlet标签,servlet标签是由servlet和servlet-mapping标签组成的,两者之间通过在servlet和servlet-mapping标签中同样的servlet-name名称来实现关联的。
Servlet的定义
定义一个 Servlet 很简单,只需要继承javax.servlet.http.HttpServlet类并重写doXXX(如doGet、doPost)方法或者service方法就可以了,其中需要注意的是重写HttpServlet类的service方法可以获取到上述七种Http请求方法的请求。
javax.servlet.http.HttpServlet:
在写Servlet之前我们先了解下HttpServlet,javax.servlet.http.HttpServlet类继承于javax.servlet.GenericServlet,而GenericServlet又实现了javax.servlet.Servlet和javax.servlet.ServletConfig。javax.servlet.Servlet接口中只定义了servlet基础生命周期方法:init(初始化)、getServletConfig(配置)、service(服务)、destroy(销毁),而HttpServlet不仅实现了servlet的生命周期并通过封装service方法抽象出了doGet/doPost/doDelete/doHead/doPut/doOptions/doTrace方法用于处理来自客户端的不一样的请求方式,我们的Servlet只需要重写其中的请求方法或者重写service方法即可实现servlet请求处理。
javax.servlet.http.HttpServlet类:

TestServlet示例代码:
package com.anbai.sec.servlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Creator: yz
* Date: 2019/12/14
*/
// 如果使用注解方式请取消@WebServlet注释并注释掉web.xml中TestServlet相关配置
//@WebServlet(name = "TestServlet", urlPatterns = {"/TestServlet"})
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter out = response.getWriter();
out.println("Hello World~");
out.flush();
out.close();
}
}
请求TestServlet示例:

Servlet Web.xml配置
定义好了Servlet类以后我们需要在web.xml中配置servlet标签才能生效。
基于配置实现的Servlet:

Servlet 3.0 基于注解方式配置
基于注解的Servlet:
值得注意的是在 Servlet 3.0 之后( Tomcat7+)可以使用注解方式配置 Servlet 了,在任意的Java类添加javax.servlet.annotation.WebServlet注解即可。
基于注解的方式配置Servlet实质上是对基于web.xml方式配置的简化,极大的简化了Servlet的配置方式,但是也提升了对Servlet配置管理的难度,因为我们不得不去查找所有包含了@WebServlet注解的类来寻找Servlet的定义,而不再只是查看web.xml中的servlet标签配置。

Servlet 3.0 特性
- 新增动态注册
Servlet、Filter和Listener的API(addServlet、addFilter、addListener)。 - 新增
@WebServlet、@WebFilter、@WebInitParam、@WebListener、@MultipartConfig注解。 - 文件上传支持,
request.getParts()。 非阻塞 IO,添加异步 IO。- 可插拔性(
web-fragment.xml、ServletContainerInitializer)。
Request & Response
在B/S架构中最重要的就是浏览器和服务器端交互,Java EE将其封装为请求和响应对象,即 request(HttpServletRequest) 和 response(HttpServletResponse)。
HttpServletRequest对象用于处理来自客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP 中的所有信息都封装在这个对象中,通过HttpServletRequest对象可以获取到客户端请求的所有信息。
HttpServletResponse对象用于响应客户端的请求,通过HttpServletResponse对象可以处理服务器端对客户端请求响应。
HttpServletRequest常用方法
| 方法 | 说明 |
|---|---|
| getParameter(String name) | 获取请求中的参数,该参数是由name指定的 |
| getParameterValues(String name) | 返回请求中的参数值,该参数值是由name指定的 |
| getRealPath(String path) | 获取Web资源目录 |
| getAttribute(String name) | 返回name指定的属性值 |
| getAttributeNames() | 返回当前请求的所有属性的名字集合 |
| getCookies() | 返回客户端发送的Cookie |
| getSession() | 获取session回话对象 |
| getInputStream() | 获取请求主题的输入流 |
| getReader() | 获取请求主体的数据流 |
| getMethod() | 获取发送请求的方式,如GET、POST |
| getParameterNames() | 获取请求中所有参数的名称 |
| getRemoteAddr() | 获取客户端的IP地址 |
| getRemoteHost() | 获取客户端名称 |
| getServerPath() | 获取请求的文件的路径 |
HttpServletResponse常用方法
| 方法 | 说明 |
|---|---|
| getWriter() | 获取响应打印流对象 |
| getOutputStream() | 获取响应流对象 |
| addCookie(Cookie cookie) | 将指定的Cookie加入到当前的响应中 |
| addHeader(String name,String value) | 将指定的名字和值加入到响应的头信息中 |
| sendError(int sc) | 使用指定状态码发送一个错误到客户端 |
| sendRedirect(String location) | 发送一个临时的响应到客户端 |
| setDateHeader(String name,long date) | 将给出的名字和日期设置响应的头部 |
| setHeader(String name,String value) | 将给出的名字和值设置响应的头部 |
| setStatus(int sc) | 给当前响应设置状态码 |
| setContentType(String ContentType) | 设置响应的MIME类型 |
JSP基础
JSP(JavaServer Pages) 是与 PHP、ASP、ASP.NET 等类似的脚本语言,JSP是为了简化Servlet的处理流程而出现的替代品,早期的Java EE因为只能使用Servlet来处理客户端请求而显得非常的繁琐和不便,使用JSP可以快速的完成后端逻辑请求。
正因为在JSP中可以直接调用Java代码来实现后端逻辑的这一特性,黑客通常会编写带有恶意攻击的JSP文件(俗称WebShell)来实现对服务器资源的恶意请求和控制。
现代的MVC框架(如:Spring MVC 5.x)已经完全抛弃了JSP技术,采用了模板引擎(如:Freemark)或者RESTful的方式来实现与客户端的交互工作,或许某一天JSP技术也将会随着产品研发的迭代而彻底消失。
JSP 三大指令
<%@ page ... %>定义网页依赖属性,比如脚本语言、error页面、缓存需求等等<%@ include ... %>包含其他文件(静态包含)<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>引入标签库的定义
JSP 表达式(EL)
EL表达式(Expression Language)语言,常用于在jsp页面中获取请求中的值,如获取在Servlet中设置的Attribute:${名称}。使用EL表达式可以实现命令执行,我们将会在后续EL表达式章节中详细讲解。
JSP 标准标签库(JSTL)
JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。
JSTL支持通用的、结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签,SQL标签。 除了这些,它还提供了一个框架来使用集成JSTL的自定义标签。
JSP 九大对象
从本质上说 JSP 就是一个Servlet,JSP 引擎在调用 JSP 对应的 jspServlet 时,会传递或创建 9 个与 web 开发相关的对象供 jspServlet 使用。 JSP 技术的设计者为便于开发人员在编写 JSP 页面时获得这些 web 对象的引用,特意定义了 9 个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这 9 大对象的引用。
如下:
| 变量名 | 类型 | 作用 |
|---|---|---|
| pageContext | PageContext | 当前页面共享数据,还可以获取其他8个内置对象 |
| request | HttpServletRequest | 客户端请求对象,包含了所有客户端请求信息 |
| session | HttpSession | 请求会话 |
| application | ServletContext | 全局对象,所有用户间共享数据 |
| response | HttpServletResponse | 响应对象,主要用于服务器端设置响应信息 |
| page | Object | 当前Servlet对象,this |
| out | JspWriter | 输出对象,数据输出到页面上 |
| config | ServletConfig | Servlet的配置对象 |
| exception | Throwable | 异常对象 |
JSP、Servlet之间的关系
JSP、JSPX 文件是可以直接被 Java 容器直接解析的动态脚本, jsp 和其他脚本语言无异,不但可以用于页面数据展示,也可以用来处理后端业务逻辑。
从本质上说 JSP 就是一个Servlet ,因为 jsp 文件最终会被编译成 class 文件,而这个 class 文件实际上就是一个特殊的Servlet 。
JSP文件会被编译成一个java类文件,如index.jsp在Tomcat中Jasper编译后会生成index_jsp.java和index_jsp.class两个文件。而index_jsp.java 继承于HttpJspBase类,HttpJspBase是一个实现了HttpJspPage接口并继承了HttpServlet的标准的Servlet,__jspService方法其实是HttpJspPage接口方法,类似于Servlet中的service方法,这里的__jspService方法其实就是HttpJspBase的service方法调用。

Filter
javax.servlet.Filter是Servlet2.3新增的一个特性,主要用于过滤URL请求,通过Filter我们可以实现URL请求资源权限验证、用户登陆检测等功能。
Filter是一个接口,实现一个Filter只需要重写init、doFilter、destroy方法即可,其中过滤逻辑都在doFilter方法中实现。
Filter的配置类似于Servlet,由<filter>和<filter-mapping>两组标签组成,如果Servlet版本大于3.0同样可以使用注解的方式配置Filter。
基于注解实现的Filter示例:

Filter和Servlet的总结
对于基于Filter和Servlet实现的简单架构项目,代码审计的重心集中于找出所有的Filter分析其过滤规则,找出是否有做全局的安全过滤、敏感的URL地址是否有做权限校验并尝试绕过Filter过滤。第二点则是找出所有的Servlet,分析Servlet的业务是否存在安全问题,如果存在安全问题是否可以利用?是否有权限访问?利用时是否被Filter过滤等问题,切勿看到Servlet、JSP中的漏洞点就妄下定论,不要忘了Servlet前面很有可能存在一个全局安全过滤的Filter。
Filter和Servlet都是Java Web提供的API,简单的总结了下有如下共同点。
Filter和Servlet都需要在web.xml或注解(@WebFilter、@WebServlet)中配置,而且配置方式是非常的相似的。Filter和Servlet都可以处理来自Http请求的请求,两者都有request、response对象。Filter和Servlet基础概念不一样,Servlet定义是容器端小程序,用于直接处理后端业务逻辑,而Filter的思想则是实现对Java Web请求资源的拦截过滤。Filter和Servlet虽然概念上不太一样,但都可以处理Http请求,都可以用来实现MVC控制器(Struts2和Spring框架分别基于Filter和Servlet技术实现的)。- 一般来说
Filter通常配置在MVC、Servlet和JSP请求前面,常用于后端权限控制、统一的Http请求参数过滤(统一的XSS、SQL注入、Struts2命令执行等攻击检测处理)处理,其核心主要体现在请求过滤上,而Servlet更多的是用来处理后端业务请求上。
Cookie 和 Session 对象
Cookie 是最常用的Http会话跟踪机制,且所有Servlet容器都应该支持。当客户端不接受Cookie时,服务端可使用URL重写的方式作为会话跟踪方式。会话ID必须被编码为URL字符串中的一个路径参数,参数的名字必须是 jsessionid。
浏览器和服务端创建会话(Session)后,服务端将生成一个唯一的会话ID(sessionid)用于标识用户身份,然后会将这个会话ID通过Cookie的形式返回给浏览器,浏览器接受到Cookie后会在每次请求后端服务的时候带上服务端设置Cookie值,服务端通过读取浏览器的Cookie信息就可以获取到用于标识用户身份的会话ID,从而实现会话跟踪和用户身份识别。
因为Cookie中存储了用户身份信息,并且还存储于浏览器端,攻击者可以使用XSS漏洞获取到Cookie信息并盗取用户身份就行一些恶意的操作。
转载请注明出处及链接
