网络安全必学 SQL 注入
1.1 .Sql 注入攻击原理
SQL 注入漏洞可以说是在企业运营中会遇到的最具破坏性的漏洞之一,它也是目前被利用得最多的漏洞。要学会如何防御 SQL 注入,首先我们要学习它的原理。
针对 SQL 注入的攻击行为可描述为通过在用户可控参数中注入 SQL 语法,破坏原有 SQL 结构,达到编写程序时意料之外结果的攻击行为。其成因可以归结为以下两个原因叠加造成的:
程序编写者在处理应用程序和数据库交互时,使用字符串拼接的方式构造 SQL 语句。未对用户可控参数进行足够的过滤便将参数内容拼接进入到 SQL 语句中。注入攻击的本质,是把用户输入的数据当做代码执行。这里有两个关键条件:用户能够控制输入。原本程序要执行的代码,拼接了用户输入的数据。
1.2 .Sql 审计方法
手动找的话,可以直接找到 sqlmapper.xml 文件或者直接搜索 select、update、delete、insert “String sql=”等关键词,定位 SQL xml 配置文件。
如果 sql 语句中有出现 $ 进行参数拼接,则存在 SQL 注入风险。
当找到某个变量关键词有 SQL 注入风险时,可以再根据调用链找到该存在注入风险的业务逻辑代码,查看参数来源是否安全、是否有配置 SQL 危险参数过滤的过滤器,最终确认是否存在 SQL 注入。以下给出可能造成 sql 注入攻击的关键字,审计时可根据实际情况进项查找
常见 SQL 语句关键词
【一一帮助安全学习,所有资源一一】①网络安全学习路线②20 份渗透测试电子书③安全攻防 357 页笔记④50 份安全攻防面试指南⑤安全红队渗透工具包⑥网络安全必备书籍⑦100 个漏洞实战案例⑧安全大厂内部教程
1.3Sql 注入漏洞危害
1 、 攻击者可以做到
-
业务运营的所有数据被攻击
-
对当前数据库用户拥有的所有表数据进行增、删、改、查等操作
-
若当前数据库用户拥有 file_priv 权限,攻击者可通过植入木马的方式进一步控制 DB 所在服务器
-
若当前数据库用户为高权限用户,攻击者甚至可以直接执行服务器命令从而通过该漏洞直接威胁整个内网系统
2、可能对业务造成的影响
① 用户信息被篡改
② 攻击者偷取代码和用户数据恶意获取
线上代码被非法篡改,并造成为恶意攻击者输送流量或其他利益的影响
1.4 Sql 注入漏洞代码示例
Java 代码动态构建 SQL
Statement stmt = null;ResultSet rs = null;try{String userName = ctx.getAuthenticatedUserName(); //this is a constantString sqlString = "SELECT * FROM t_item WHERE owner='" + userName + "' AND itemName='" + request.getParameter("itemName") + "'";stmt = connection.createStatement();rs = stmt.executeQuery(sqlString);// ... result set handling}catch (SQLException se){// ... logging and error handling}
复制代码
这里将查询字符串常量与用户输入进行拼接来动态构建 SQL 查询命令。仅当 itemName 不包含单引号时,这条查询语句的行为才会是正确的。如果一个攻击者以用户名 wiley 发起一个请求,并使用以下条目名称参数进行查询:
name' OR 'a' = 'a
复制代码
那么这个查询将变成:
SELECT * FROM t_item WHERE owner = 'wiley' AND itemname = 'name' OR 'a'='a';
复制代码
此处,额外的 OR 'a'='a'条件导致整个 WHERE 子句的值总为真。那么,这个查询便等价于如下非常简单的查询:
SELECT * FROM t_item
复制代码
这个简化的查询使得攻击者能够绕过原有的条件限制:这个查询会返回 items 表中所有储存的条目,而不管它们的所有者是谁,而原本应该只返回属于当前已认证用户的条目。
在存储过程中动态构建 SQL
Java 代码:
CallableStatement = nullResultSet results = null;try{String userName = ctx.getAuthenticatedUserName(); //this is a constantString itemName = request.getParameter("itemName");cs = connection.prepareCall("{call sp_queryItem(?,?)}");cs.setString(1, userName);cs.setString(2, itemName);results = cs.executeQuery();// ... result set handling}catch (SQLException se){// ... logging and error handling}SQL Server存储过程:CREATE PROCEDURE sp_queryItem@userName varchar(50),@itemName varchar(50)ASBEGINDECLARE @sql nvarchar(500);SET @sql = 'SELECT * FROM t_itemWHERE owner = ''' + @userName + '''AND itemName = ''' + @itemName + '''';EXEC(@sql);ENDGO
复制代码
在存储过程中,通过拼接参数值来构建查询字符串,和在应用程序代码中拼接参数一样,同样是有 SQL 注入风险的。
Hibernate 动态构建 SQL/HQL
原生 SQL 查询:
String userName = ctx.getAuthenticatedUserName(); //this is a constantString itemName = request.getParameter("itemName");Query sqlQuery = session.createSQLQuery("select * from t_item where owner = '" + userName + "' and itemName = '" + itemName + "'");List<Item> rs = (List<Item>) sqlQuery.list();
复制代码
HQL 查询:
String userName = ctx.getAuthenticatedUserName(); //this is a constantString itemName = request.getParameter("itemName");Query hqlQuery = session.createQuery("from Item as item where item.owner = '" + userName + "' and item.itemName = '" + itemName + "'");List<Item> hrs = (List<Item>) hqlQuery.list();
复制代码
即使是使用 Hibernate,如果在动态构建 SQL/HQL 查询时包含了不可信输入,同样也会面临 SQL/HQL 注入的问题。
HQL 代码中,session.createQuery 使用 HQL 语句将查询到的数据存到到 list 集合中,需要时在拿出来使用。而参数中 itemName 是通过 request.getParameter 直接获取。攻击者若在此处写入恶意语句,程序将恶意语句查询出来的数据存放在 list 集合中,再通过某处调用成功将数据显示在前台。
Mybatis 注入分析
Mybatis 框架下易产生 SQL 注入漏洞的情况主要分为以下三种:
1)模糊查询 like
例如对人员姓名检索进行模糊查询,如果考虑安全编码规范问题,其对应的 SQL 语句如下:
Select * from user where name like '%#{name}%'
复制代码
但由于这样写程序会报错,研发人员将 SQL 查询语句修改如下:
Select * from user where name like '%${name}%'
复制代码
在这种情况下我们发现程序不再报错,但是此时产生了 SQL 语句拼接问题,如果 java 代码层面没有对用户输入的内容做处理势必会产生 SQL 注入漏洞。
2)in 之后的参数
例如对人员姓名进行同条件多值检索的时候,如当用户输入 001,002,003...时,如果考虑安全编码规范问题,其对应的 SQL 语句如下:
Select * from name where id in (#{id})
复制代码
但由于这样写程序会报错,研发人员将 SQL 查询语句修改如下:
Select * from name where id in (${id})
复制代码
修改 SQL 语句之后,程序停止报错,但是却引入了 SQL 语句拼接的问题,如果没有对用户输入的内容做过滤,势必会产生 SQL 注入漏洞。
3)order by 之后(重点和区分点)
当根据姓名、id 序号等信息用户进行排序的时候,如果考虑安全编码规范问题,其对应的 SQL 语句如下:
Select * from user where name = 'qihoo' order by #{id} desc
复制代码
但由于发布时间 id 不是用户输入的参数,无法使用预编译。研发人员将 SQL 查询语句修改如下:
Select * from user where name = 'qihoo' order by ${id} desc
复制代码
修改之后,程序未通过预编译,但是产生了 SQL 语句拼接问题,极有可能引发 SQL 注入漏洞。
1.5 .实战案例-OFCMS SQL 注入漏洞分析
本文中使用 ofcms 进行 SQL 注入漏洞讲解,此 CMS 算是对新手学习代码审计比较友好的 CMS。
上述为安装成功页面,如何安装 CMS 本章不在赘述。
后台页面:http://localhost:8080/ofcms-admin/admin/index.html

漏洞点:
ofcms-admin/src/main/java/com/ofsoft/cms/admin/controller/system/SystemGeneratrController.javacreate方法|/*** 创建表*/public void create() {try {String sql = getPara("sql");Db.update(sql);rendSuccessJson();} catch (Exception e) {e.printStackTrace();rendFailedJson(ErrorCode.get("9999"), e.getMessage());}}
复制代码
上述代码中使用 getpara 获取 sql 的参数值,并 update,跟进一下 getpara 和 update 方法。
跳转至 jfinal-3.2.jar/com/jfinal/core/controller.class
public String getPara(String name) {return this.request.getParameter(name);}
复制代码
上述代码无特殊用意,就是获取参数值,继续跟进 Db.update 方法。
跳转至 jfinal-3.2.jar/com/jfinal/plugin/activerecord/Db.class
public static int update(String sql) {return MAIN.update(sql);}
复制代码
发现调用 MAIN.update , 继续跟进。
跳转至 jfinal-3.2.jar/com/jfinal/plugin/activerecord/DbPro.class
public int update(String sql) {return this.update(sql, DbKit.NULL_PARA_ARRAY);}
复制代码
继续跟进到最后,发现华点。
public int update(String sql, Object... paras) {Connection conn = null;int var4;try {conn = this.config.getConnection();//连接var4 = this.update(this.config, conn, sql, paras);//调用update更新} catch (Exception var8) {throw new ActiveRecordException(var8);} finally {this.config.close(conn);}return var4;}
复制代码
重点:Object...
Object 是所有类的基类,而 Object... 是不确定方法参数情况下的一种多态表现形式(可以传递多个参数)。
再继续跟进 update ,同文件代码
int update(Config config, Connection conn, String sql, Object... paras) throws SQLException {PreparedStatement pst = conn.prepareStatement(sql);config.dialect.fillStatement(pst, paras);int result = pst.executeUpdate();DbKit.close(pst);return result;}
复制代码
上述代码执行 SQL 语句,并返回结果。
至此,整个功能流程结束,在我们跟进的过程中,代码中无任何过滤语句,获取参数值,调用 update 方法更新,更新成功后返回结果。
漏洞验证

漏洞点打上断点,网页中输入 poc 进行验证
update of_cms_topic set topic_url=updatexml(1,concat(0x7e,(user())),0) where topic_id = 1
复制代码


根据如上截图可看出我们传入的 SQL 语句是被完整的接收,并未做任何过滤直接带入数据库执行,所以此处直接写入漏洞代码爆出当前数据库账户为 root。
上述为 sqlmap 工具跑出来的注入点。
1.6 漏洞修复方法
添加全局过滤器,过滤特殊字符

SQLFilter.java 中:

PreparedStatement 参数化
如果使用参数化查询,则在 SQL 语句中使用占位符表示需在运行时确定的参数值。参数化查询使得 SQL 查询的语义逻辑被预先定义,而实际的查询参数值则等到程序运行时再确定。参数化查询使得数据库能够区分 SQL 语句中语义逻辑和数据参数,以确保用户输入无法改变预期的 SQL 查询语义逻辑。
在 Java 中,可以使用 java.sql.PreparedStatement 来对数据库发起参数化查询。在这个正确示例中,如果一个攻击者将 itemName 输入为 name' OR 'a' = 'a,这个参数化查询将免受攻击,而是会查找一个 itemName 匹配 name' OR 'a' = 'a 这个字符串的条目。
PreparedStatement stmt = nullResultSet rs = nulltry{String userName = ctx.getAuthenticatedUserName(); //this is a constantString itemName = request.getParameter("itemName");// ...Ensure that the length of userName and itemName is legitimate// ...String sqlString = "SELECT * FROM t_item WHERE owner=? AND itemName=?";stmt = connection.prepareStatement(sqlString);stmt.setString(1, userName);stmt.setString(2, itemName);rs = stmt.executeQuery();// ... result set handling}catch (SQLException se){// ... logging and error handling}
复制代码
存储过程参数化
这个存储过程使用参数化查询,而未包含不安全的动态 SQL 构建。数据库编译此存储过程时,会生成一个 SELECT 查询的执行计划,只允许原始的 SQL 语义被执行。任何参数值,即使是被注入的 SQL 语句也不会被执行,因为它们不是执行计划的一部分。
CallableStatement = nullResultSet results = null;try{String userName = ctx.getAuthenticatedUserName(); //this is a constantString itemName = request.getParameter("itemName");// ... Ensure that the length of userName and itemName is legitimate// ...cs = connection.prepareCall("{call sp_queryItem(?,?)}");cs.setString(1, userName);cs.setString(2, itemName);results = cs.executeQuery();// ... result set handling}catch (SQLException se){// ... logging and error handling}
复制代码
Hibernate 参数化查询
Hibernate 支持 SQL/HQL 参数化查询。为了防止 SQL 注入以及改善性能,以上这些示例使用了参数化绑定 的方式来设置查询参数。
String userName = ctx.getAuthenticatedUserName(); //this is a constantString itemName = request.getParameter("itemName");Query hqlQuery = session.createQuery("from Item as item where item.owner = ? and item.itemName = ?");hqlQuery.setString(1, userName);hqlQuery.setString(2, itemName);List<Item> rs = (List<Item>) hqlQuery.list();
复制代码
HQL 基于名称的参数化查询
String userName = ctx.getAuthenticatedUserName(); //this is a constantString itemName = request.getParameter("itemName");Query hqlQuery = session.createQuery("from Item as item where item.owner = :owner and item.itemName = :itemName");hqlQuery.setString("owner", userName);hqlQuery.setString("itemName", itemName);List<Item> rs = (List<Item>) hqlQuery.list();
复制代码
原生参数化查询
String userName = ctx.getAuthenticatedUserName(); //this is a constantString itemName = request.getParameter("itemName");Query sqlQuery = session.createSQLQuery("select * from t_item where owner = ? and itemName = ?");sqlQuery.setString(0, owner);sqlQuery.setString(1, itemName);List<Item> rs = (List<Item>) sqlQuery.list();
复制代码
MyBatis 框架的修复方案
尽量使用 #描述参数,如果一定要使用 $,则需要自己过滤用户输入
模糊查询 like SQL 注入修复建议
按照新闻标题对新闻进行模糊查询,可将 SQL 查询语句设计如下:
select * from news where name like concat(‘%’,#{name }, ‘%’)
复制代码
采用预编译机制,避免了 SQL 语句拼接的问题,从根源上防止了 SQL 注入漏洞的产生。
in 之后的参数 SQL 注入修复建议
在对新闻进行同条件多值查询的时候,可使用 Mybatis 自带循环指令解决 SQL 语句动态拼接的问题:
select * from news where id in<foreach collection="ids" item="item" open="("separator="," close=")">#{item} </foreach>
复制代码
order by SQL 注入修复建议
在 Java 层面做映射预编译机制只能处理查询参数,其他地方还需要研发人员根据具体情况来解决。如前面提到的排序情景:
Select * from news where title =‘淘宝’ order by #{time} asc,
复制代码
这里 time 不是查询参数,无法使用预编译机制,只能这样拼接:
Select * from news where title =‘淘宝’ order by ${time} asc
复制代码
针对这种情况研发人员可以在 java 层面做映射来进行解决。如当存在发布时间 time 和点击量 click 两种排序选择时,我们可以限制用户只能输入 1 和 2。
当用户输入 1 时,我们在代码层面将其映射为 time,当用户输入 2 时,将其映射为 click。而当用户输入 1 和 2 之外的其他内容时,我们可以将其转换为默认排序选择 time(或者 click)。

相关文章:
网络安全必学 SQL 注入
1.1 .Sql 注入攻击原理 SQL 注入漏洞可以说是在企业运营中会遇到的最具破坏性的漏洞之一,它也是目前被利用得最多的漏洞。要学会如何防御 SQL 注入,首先我们要学习它的原理。 针对 SQL 注入的攻击行为可描述为通过在用户可控参数中注入 SQL 语法&#x…...
Docker基础知识详解
✅作者简介:热爱Java后端开发的一名学习者,大家可以跟我一起讨论各种问题喔。 🍎个人主页:Hhzzy99 🍊个人信条:坚持就是胜利! 💞当前专栏:文章 🥭本文内容&am…...
腾讯、阿里入选首批“双柜台证券”,港股市场迎盛夏升温?
6月5日,香港交易所发布公告,将于6月19日在香港证券市场推出“港币-人民币双柜台模式”,当日确定有21只证券指定为双柜台证券。同时,港交所还表示,在双柜台模式推出前,更多证券或会被接纳并加入双…...
CentOS7 使用Docker 安装MySQL
CentOS7 使用Docker 安装MySQL Docker的相关知识本篇不会再概述,有疑惑的同学请自行查找相关知识。本篇只是介绍如何在CentOS7下使用Docker安装相应的镜像。 可登陆Docker官网 https://docs.docker.com 之后可以跟着官方的步骤进行安装。 clipboard.png 具体安装过…...
注解和反射复习
注解 注解:给程序和人看的,被程序读取,jdk5.0引用 内置注解 override:修饰方法,方法声明和重写父类方法, Deprecated:修饰,不推荐使用 suppressWarnings用来抑制编译时的警告,必须添加一个或多个参数s…...
RocketMQ的demo代码
下面是一个使用Java实现的RocketMQ示例代码,用于发送和消费消息: 首先,您需要下载并安装RocketMQ,并启动NameServer和Broker。 接下来,您可以使用以下示例代码来发送和消费消息: Producer.java文件&…...
C++ 连接、操作postgreSQL(基于libpq库)
C++ 连接postgreSQL(基于libpq库) 1.环境2.数据库操作2.1. c++ 连接数据库2.2. c++ 删除数据库属性表内容2.3. c++ 插入数据库属性表内容2.4 c++ 关闭数据库1.环境 使用libpq库来链接postgresql数据库,主要用到的头文件是这个: #include "libpq-fe.h"2.数据库操…...
Node.js技术简介及其在Web开发中的应用
Node.js是一个基于Chrome V8引擎的JavaScript运行时环境,使得JavaScript能够在服务器端运行。Node.js采用事件驱动、非阻塞I/O模型,能够处理大量并发请求,非常适合处理I/O密集型的应用程序。本文将介绍Node.js的特点、优势以及在Web开发中的应…...
时间序列分析:原理与MATLAB实现
2023年9月数学建模国赛期间提供ABCDE题思路加Matlab代码,专栏链接(赛前一个月恢复源码199,欢迎大家订阅):http://t.csdn.cn/Um9Zd 目录 1. 时间序列分析简介 2. 自回归模型(AR) 2.1. 参数估计 2.2. MATLAB实现...
mysql排序之if(isnull(字段名),0,1),字段名 或者 if(isnull(字段名),1,0),字段名
mysql排序之if(isnull(字段名),0,1),字段名 或者 if(isnull(字段名),1,0),字段名 默认情况下,MySQL将null算作最小值。如果想要手动指定null的顺序,可以这样处理: 将null强制放在最前 //null, null, 1,2,3,4(默认就是这样&#…...
华为OD机试真题 Java 实现【递增字符串】【2023Q1 200分】,附详细解题思路
一、题目描述 定义字符串完全由“A’和B"组成,当然也可以全是"A"或全是"B。如果字符串从前往后都是以字典序排列的,那么我们称之为严格递增字符串。 给出一个字符串5,允许修改字符串中的任意字符,即可以将任何的"A"修改成"B,也可以将…...
合并文件解决HiveServer2内存溢出方案
一、文件过多导致HiveServer2内存溢出 1.1查看表文件个数 desc formatted yanyu.tmp• 表文件数量为6522102 1.2查看表文件信息 hadoop fs -ls warehouse/yanyu.db/tmp• 分区为string 类型的time字段,分了2001个区。 1.3.查看某个分区下的文件个数为10000个 …...
韧性数据安全体系缘起与三个目标 |CEO专栏
今年4月,美创科技在数据安全领域的新探索——“韧性”数据安全防护体系框架正式发布亮相。 为帮您更深入了解“韧性数据安全”,我们特别推出专栏“构建适应性进化的韧性数据安全体系”,CEO柳遵梁亲自执笔,进行系列解读分享。 首期…...
华为OD机试真题 Java 实现【火车进站】【牛客练习题】
一、题目描述 给定一个正整数N代表火车数量,0<N<10,接下来输入火车入站的序列,一共N辆火车,每辆火车以数字1-9编号,火车站只有一个方向进出,同时停靠在火车站的列车中,只有后进站的出站了,先进站的才能出站。 要求输出所有火车出站的方案,以字典序排序输出。 …...
c#快速入门(下)
欢迎来到Cefler的博客😁 🕌博客主页:那个传说中的man的主页 🏠个人专栏:题目解析 🌎推荐文章:题目大解析2 目录 👉🏻Inline和lambda委托和lambda 👉…...
基于深度学习的目标姿态检测方法_kaic
目录 摘要 第1章 引言 1.1 研究背景和意义 1.2 国内外研究现状 1.3 主要内容 第2章 单目相机的目标姿态检测技术 2.1单目相机的工作原理 2.2目标姿态检测 2.3已有的目标姿态检测方法及其局限性 2.4本章总结 第3章 构建数据集 3.1 数据集来源 3.2数据集标注 3.3数据集分析 3.4本…...
Pycharm设置Python每个文件开头自定义模板(带上声明字符编码、作者名、时间等)
Pycharm设置地址: 在File---settings---Editor---File and Code Templates---Python script 脚本里添加: 模板声明设置参考: # ---encoding:utf-8--- # Time : ${DATE} ${HOUR}:${MINUTE} # Author : 作者名 # Email :你的邮箱 # Sit…...
Gem相关操作命令
Gem相关操作命令 gem -v # 查看 gem 版本gem source # 查看 gem 配置源 gem source -l # 查看 gem 配置源目录 gem sources -a url # 添加 gem 配置源(url 需换成网址) gem sources --add url # 添加 gem 配置源(url 需换成网址)…...
软件测试2023年行情怎么样?仔细讲解!
目录 前言: 普通功能测试人员不建议跳槽 还有一个要求就是要对业务的极致理解 那么产业互联网趋势会导致什么呢? 现在跳槽涨薪需要掌握到什么样的技术呢? 给大家一些跳槽建议 前言: 软件测试是为了发现程序中的错误而执行程序的…...
【1130. 叶值的最小代价生成树】
来源:力扣(LeetCode) 描述: 给你一个正整数数组 arr,考虑所有满足以下条件的二叉树: 每个节点都有 0 个或是 2 个子节点。数组 arr 中的值与树的中序遍历中每个叶节点的值一一对应。每个非叶节点的值等于…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
鸿蒙HarmonyOS 5军旗小游戏实现指南
1. 项目概述 本军旗小游戏基于鸿蒙HarmonyOS 5开发,采用DevEco Studio实现,包含完整的游戏逻辑和UI界面。 2. 项目结构 /src/main/java/com/example/militarychess/├── MainAbilitySlice.java // 主界面├── GameView.java // 游戏核…...
接口 RESTful 中的超媒体:REST 架构的灵魂驱动
在 RESTful 架构中,** 超媒体(Hypermedia)** 是一个核心概念,它体现了 REST 的 “表述性状态转移(Representational State Transfer)” 的本质,也是区分 “真 RESTful API” 与 “伪 RESTful AP…...
【Redis】Redis从入门到实战:全面指南
Redis从入门到实战:全面指南 一、Redis简介 Redis(Remote Dictionary Server)是一个开源的、基于内存的键值存储系统,它可以用作数据库、缓存和消息代理。由Salvatore Sanfilippo于2009年开发,因其高性能、丰富的数据结构和广泛的语言支持而广受欢迎。 Redis核心特点:…...
