某小程序sign签名参数逆向分析
文章目录
- 1. 写在前面
- 2. 接口分析
- 3. 分析还原
【🏠作者主页】:吴秋霖
【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作!
【🌟作者推荐】:对爬虫领域以及JS逆向分析感兴趣的朋友可以关注《爬虫JS逆向实战》《深耕爬虫领域》
未来作者会持续更新所用到、学到、看到的技术知识!包括但不限于:各类验证码突防、爬虫APP与JS逆向分析、RPA自动化、分布式爬虫、Python领域等相关文章
作者声明:文章仅供学习交流与参考!严禁用于任何商业与非法用途!否则由此产生的一切后果均与作者无关!如有侵权,请联系作者本人进行删除!
1. 写在前面
做爬虫脑子一定要灵光~不然怎么当大佬!正所谓大路不通走水路,水路不通走山路!相信很多工程师在面对一个端采集遇到瓶颈的时候什么M端、APP端、小程序端、Web端都几乎会挑一个或多个去摸排分析一下。风控这个东西很奇妙也很玄学,有时候还真有那么一些低风控的接口或者端存在
小程序端这么说吧,体量大点的风控基本都拉的很强且有的跟某信有因果!除非真的没有其他端的数据源了,一般少有人去(总之非可选的主流战场)
分析目标:
5Lmd5YS/572R57uc
2. 接口分析
随便通过关键词在搜索接口看看发包情况,直接Curl重放是403无效的,会出现签名错误。这个如下所示:

也就是说上面的sign是动态实时生成的,另外还有一个token参数也是有时效性的,将请求参数拿出来看看,如下所示:
data = {"method": "serverless.function.runtime.invoke","params": "{\"functionTarget\":\"DCloud-clientDB\",\"functionArgs\":{\"command\":{\"$db\":[{\"$method\":\"collection\",\"$param\":[\"a_novels\"]},{\"$method\":\"where\",\"$param\":[{\"novels_name\":{\"$regexp\":{\"source\":\"玄幻\",\"flags\":\"\"}}}]},{\"$method\":\"get\",\"$param\":[]}]},\"clientInfo\":{\"PLATFORM\":\"mp-weixin\",\"OS\":\"mac\",\"APPID\":\"__UNI__1B787A1\",\"DEVICEID\":\"17356293548006764541\",\"scene\":1089,\"deviceId\":\"17356293548006764541\",\"appId\":\"__UNI__1B787A1\",\"appName\":\"\",\"appVersion\":\"2.0.1\",\"appVersionCode\":\"201\",\"appLanguage\":\"zh-Hans\",\"uniCompilerVersion\":\"4.36\",\"uniRuntimeVersion\":\"4.36\",\"uniPlatform\":\"mp-weixin\",\"deviceBrand\":\"apple\",\"deviceModel\":\"MacBookPro15,2\",\"deviceType\":\"pc\",\"osName\":\"mac\",\"osVersion\":\"OS\",\"hostVersion\":\"3.8.7\",\"hostName\":\"WeChat\",\"locale\":\"zh-Hans\",\"LOCALE\":\"zh-Hans\"},\"uniIdToken\":\"\"}}","spaceId": "mp-f510ce55-6e44-40b0-b249-d278726b813d","timestamp": 1735785506387,"token": "63bc0ed6-f8a1-40ab-bb07-d11bf678ab21"
}
初看大概是MD5标准加密,拼接一下路径、参数加个时间戳啥的
3. 分析还原
在开始分析JS之前,可以通过反编译小程序的方式去分析也可以使用大佬开源的调试工具来辅助分析,有些Web端分析比较多的使用这个辅助还是蛮不错的。如下所示:

这里在页面直接搜索的话有时候资源多会比较慢,可以直接通过堆栈或者编译好的JS文件简单的进行静态分析,如下所示:

可以看到Ae就是生成签名的调用方法,而clientSecret是一段密文,后续它将参与加密用到,如下所示:

直接跳转到Ae方法处,可以看到Object.keys(e).sort()获取e对象里面的所有键进行一个排序,存在的话则使用&拼接,然后去掉所拼接字符串开头多余的一个&确保格式正确,如下所示:


接下来需要分析P方法,也就是最终的加密算法实现,进入当前方法直接来到了如下所示:
_createHmacHelper: function(e) {return function(t, n) {return new d.HMAC.init(e,n).finalize(t)}
}
这里推测d.HMAC是个库框架中的HMAC实现,而init(e, n)与finalize(t)是该HMAC对象的初始化和最终处理方法,注意入口处的时候是使用了密钥的,所以要分析是什么算法难度是不大的,肯定不是一个单纯的MD5,对应JS代码如下所示:
var k = A((function(e, t) {var n;e.exports = n = n || function(e, t) {var n = Object.create || function() {function e() {}return function(t) {var n;return e.prototype = t,n = new e,e.prototype = null,n}}(), r = {}, o = r.lib = {}, i = o.Base = {extend: function(e) {var t = n(this);return e && t.mixIn(e),t.hasOwnProperty("init") && this.init !== t.init || (t.init = function() {t.$super.init.apply(this, arguments)}),t.init.prototype = t,t.$super = this,t},create: function() {var e = this.extend();return e.init.apply(e, arguments),e},init: function() {},mixIn: function(e) {for (var t in e)e.hasOwnProperty(t) && (this[t] = e[t]);e.hasOwnProperty("toString") && (this.toString = e.toString)},clone: function() {return this.init.prototype.extend(this)}}, a = o.WordArray = i.extend({init: function(e, t) {e = this.words = e || [],this.sigBytes = null != t ? t : 4 * e.length},toString: function(e) {return (e || c).stringify(this)},concat: function(e) {var t = this.words, n = e.words, r = this.sigBytes, o = e.sigBytes;if (this.clamp(),r % 4)for (var i = 0; i < o; i++) {var a = n[i >>> 2] >>> 24 - i % 4 * 8 & 255;t[r + i >>> 2] |= a << 24 - (r + i) % 4 * 8}elsefor (i = 0; i < o; i += 4)t[r + i >>> 2] = n[i >>> 2];return this.sigBytes += o,this},clamp: function() {var t = this.words, n = this.sigBytes;t[n >>> 2] &= 4294967295 << 32 - n % 4 * 8,t.length = e.ceil(n / 4)},clone: function() {var e = i.clone.call(this);return e.words = this.words.slice(0),e},random: function(t) {for (var n, r = [], o = function(t) {t = t;var n = 987654321, r = 4294967295;return function() {var o = ((n = 36969 * (65535 & n) + (n >> 16) & r) << 16) + (t = 18e3 * (65535 & t) + (t >> 16) & r) & r;return o /= 4294967296,(o += .5) * (e.random() > .5 ? 1 : -1)}}, i = 0; i < t; i += 4) {var u = o(4294967296 * (n || e.random()));n = 987654071 * u(),r.push(4294967296 * u() | 0)}return new a.init(r,t)}}), u = r.enc = {}, c = u.Hex = {stringify: function(e) {for (var t = e.words, n = e.sigBytes, r = [], o = 0; o < n; o++) {var i = t[o >>> 2] >>> 24 - o % 4 * 8 & 255;r.push((i >>> 4).toString(16)),r.push((15 & i).toString(16))}return r.join("")},parse: function(e) {for (var t = e.length, n = [], r = 0; r < t; r += 2)n[r >>> 3] |= parseInt(e.substr(r, 2), 16) << 24 - r % 8 * 4;return new a.init(n,t / 2)}}, s = u.Latin1 = {stringify: function(e) {for (var t = e.words, n = e.sigBytes, r = [], o = 0; o < n; o++) {var i = t[o >>> 2] >>> 24 - o % 4 * 8 & 255;r.push(String.fromCharCode(i))}return r.join("")},parse: function(e) {for (var t = e.length, n = [], r = 0; r < t; r++)n[r >>> 2] |= (255 & e.charCodeAt(r)) << 24 - r % 4 * 8;return new a.init(n,t)}}, l = u.Utf8 = {stringify: function(e) {try {return decodeURIComponent(escape(s.stringify(e)))} catch (e) {throw new Error("Malformed UTF-8 data")}},parse: function(e) {return s.parse(unescape(encodeURIComponent(e)))}}, f = o.BufferedBlockAlgorithm = i.extend({reset: function() {this._data = new a.init,this._nDataBytes = 0},_append: function(e) {"string" == typeof e && (e = l.parse(e)),this._data.concat(e),this._nDataBytes += e.sigBytes},_process: function(t) {var n = this._data, r = n.words, o = n.sigBytes, i = this.blockSize, u = o / (4 * i), c = (u = t ? e.ceil(u) : e.max((0 | u) - this._minBufferSize, 0)) * i, s = e.min(4 * c, o);if (c) {for (var l = 0; l < c; l += i)this._doProcessBlock(r, l);var f = r.splice(0, c);n.sigBytes -= s}return new a.init(f,s)},clone: function() {var e = i.clone.call(this);return e._data = this._data.clone(),e},_minBufferSize: 0});o.Hasher = f.extend({cfg: i.extend(),init: function(e) {this.cfg = this.cfg.extend(e),this.reset()},reset: function() {f.reset.call(this),this._doReset()},update: function(e) {return this._append(e),this._process(),this},finalize: function(e) {return e && this._append(e),this._doFinalize()},blockSize: 16,_createHelper: function(e) {return function(t, n) {return new e.init(n).finalize(t)}},_createHmacHelper: function(e) {return function(t, n) {return new d.HMAC.init(e,n).finalize(t)}}});var d = r.algo = {};return r}(Math)
}
)), P = (A((function(e, t) {var n;e.exports = (n = k,function(e) {var t = n, r = t.lib, o = r.WordArray, i = r.Hasher, a = t.algo, u = [];!function() {for (var t = 0; t < 64; t++)u[t] = 4294967296 * e.abs(e.sin(t + 1)) | 0}();var c = a.MD5 = i.extend({_doReset: function() {this._hash = new o.init([1732584193, 4023233417, 2562383102, 271733878])},_doProcessBlock: function(e, t) {for (var n = 0; n < 16; n++) {var r = t + n, o = e[r];e[r] = 16711935 & (o << 8 | o >>> 24) | 4278255360 & (o << 24 | o >>> 8)}var i = this._hash.words, a = e[t + 0], c = e[t + 1], p = e[t + 2], h = e[t + 3], g = e[t + 4], v = e[t + 5], y = e[t + 6], m = e[t + 7], b = e[t + 8], w = e[t + 9], _ = e[t + 10], x = e[t + 11], S = e[t + 12], O = e[t + 13], A = e[t + 14], k = e[t + 15], P = i[0], T = i[1], j = i[2], E = i[3];P = s(P, T, j, E, a, 7, u[0]),E = s(E, P, T, j, c, 12, u[1]),j = s(j, E, P, T, p, 17, u[2]),T = s(T, j, E, P, h, 22, u[3]),P = s(P, T, j, E, g, 7, u[4]),E = s(E, P, T, j, v, 12, u[5]),j = s(j, E, P, T, y, 17, u[6]),T = s(T, j, E, P, m, 22, u[7]),P = s(P, T, j, E, b, 7, u[8]),E = s(E, P, T, j, w, 12, u[9]),j = s(j, E, P, T, _, 17, u[10]),T = s(T, j, E, P, x, 22, u[11]),P = s(P, T, j, E, S, 7, u[12]),E = s(E, P, T, j, O, 12, u[13]),j = s(j, E, P, T, A, 17, u[14]),P = l(P, T = s(T, j, E, P, k, 22, u[15]), j, E, c, 5, u[16]),E = l(E, P, T, j, y, 9, u[17]),j = l(j, E, P, T, x, 14, u[18]),T = l(T, j, E, P, a, 20, u[19]),P = l(P, T, j, E, v, 5, u[20]),E = l(E, P, T, j, _, 9, u[21]),j = l(j, E, P, T, k, 14, u[22]),T = l(T, j, E, P, g, 20, u[23]),P = l(P, T, j, E, w, 5, u[24]),E = l(E, P, T, j, A, 9, u[25]),j = l(j, E, P, T, h, 14, u[26]),T = l(T, j, E, P, b, 20, u[27]),P = l(P, T, j, E, O, 5, u[28]),E = l(E, P, T, j, p, 9, u[29]),j = l(j, E, P, T, m, 14, u[30]),P = f(P, T = l(T, j, E, P, S, 20, u[31]), j, E, v, 4, u[32]),E = f(E, P, T, j, b, 11, u[33]),j = f(j, E, P, T, x, 16, u[34]),T = f(T, j, E, P, A, 23, u[35]),P = f(P, T, j, E, c, 4, u[36]),E = f(E, P, T, j, g, 11, u[37]),j = f(j, E, P, T, m, 16, u[38]),T = f(T, j, E, P, _, 23, u[39]),P = f(P, T, j, E, O, 4, u[40]),E = f(E, P, T, j, a, 11, u[41]),j = f(j, E, P, T, h, 16, u[42]),T = f(T, j, E, P, y, 23, u[43]),P = f(P, T, j, E, w, 4, u[44]),E = f(E, P, T, j, S, 11, u[45]),j = f(j, E, P, T, k, 16, u[46]),P = d(P, T = f(T, j, E, P, p, 23, u[47]), j, E, a, 6, u[48]),E = d(E, P, T, j, m, 10, u[49]),j = d(j, E, P, T, A, 15, u[50]),T = d(T, j, E, P, v, 21, u[51]),P = d(P, T, j, E, S, 6, u[52]),E = d(E, P, T, j, h, 10, u[53]),j = d(j, E, P, T, _, 15, u[54]),T = d(T, j, E, P, c, 21, u[55]),P = d(P, T, j, E, b, 6, u[56]),E = d(E, P, T, j, k, 10, u[57]),j = d(j, E, P, T, y, 15, u[58]),T = d(T, j, E, P, O, 21, u[59]),P = d(P, T, j, E, g, 6, u[60]),E = d(E, P, T, j, x, 10, u[61]),j = d(j, E, P, T, p, 15, u[62]),T = d(T, j, E, P, w, 21, u[63]),i[0] = i[0] + P | 0,i[1] = i[1] + T | 0,i[2] = i[2] + j | 0,i[3] = i[3] + E | 0},_doFinalize: function() {var t = this._data, n = t.words, r = 8 * this._nDataBytes, o = 8 * t.sigBytes;n[o >>> 5] |= 128 << 24 - o % 32;var i = e.floor(r / 4294967296), a = r;n[15 + (o + 64 >>> 9 << 4)] = 16711935 & (i << 8 | i >>> 24) | 4278255360 & (i << 24 | i >>> 8),n[14 + (o + 64 >>> 9 << 4)] = 16711935 & (a << 8 | a >>> 24) | 4278255360 & (a << 24 | a >>> 8),t.sigBytes = 4 * (n.length + 1),this._process();for (var u = this._hash, c = u.words, s = 0; s < 4; s++) {var l = c[s];c[s] = 16711935 & (l << 8 | l >>> 24) | 4278255360 & (l << 24 | l >>> 8)}return u},clone: function() {var e = i.clone.call(this);return e._hash = this._hash.clone(),e}});function s(e, t, n, r, o, i, a) {var u = e + (t & n | ~t & r) + o + a;return (u << i | u >>> 32 - i) + t}function l(e, t, n, r, o, i, a) {var u = e + (t & r | n & ~r) + o + a;return (u << i | u >>> 32 - i) + t}function f(e, t, n, r, o, i, a) {var u = e + (t ^ n ^ r) + o + a;return (u << i | u >>> 32 - i) + t}function d(e, t, n, r, o, i, a) {var u = e + (n ^ (t | ~r)) + o + a;return (u << i | u >>> 32 - i) + t}t.MD5 = i._createHelper(c),t.HmacMD5 = i._createHmacHelper(c)}(Math),n.MD5)
}
)),
A((function(e, t) {var n;e.exports = (n = k,void function() {var e = n, t = e.lib.Base, r = e.enc.Utf8;e.algo.HMAC = t.extend({init: function(e, t) {e = this._hasher = new e.init,"string" == typeof t && (t = r.parse(t));var n = e.blockSize, o = 4 * n;t.sigBytes > o && (t = e.finalize(t)),t.clamp();for (var i = this._oKey = t.clone(), a = this._iKey = t.clone(), u = i.words, c = a.words, s = 0; s < n; s++)u[s] ^= 1549556828,c[s] ^= 909522486;i.sigBytes = a.sigBytes = o,this.reset()},reset: function() {var e = this._hasher;e.reset(),e.update(this._iKey)},update: function(e) {return this._hasher.update(e),this},finalize: function(e) {var t = this._hasher, n = t.finalize(e);return t.reset(),t.finalize(this._oKey.clone().concat(n))}})}())
}
)),
A((function(e, t) {e.exports = k.HmacMD5
}
通过对上面的JS代码进行静态分析可发现最终使用的加密为HmacMD5,结合有密钥参与加密,可以直接Python进行算法还原测试验证即可,算法实现如下所示:
def sign(e, t):sorted_keys = sorted(e.keys())n = ""for key in sorted_keys:if e[key]:n += f"&{key}={e[key]}"n = n[1:]signature = hmac.new(t.encode('utf-8'), n.encode('utf-8'), hashlib.md5).hexdigest()return signature
HMAC-MD5是基于MD5哈希算法的消息认证码,HMAC是一种利用密钥的哈希算法,用于生成一个“签名”,并且常用于消息的完整性验证和认证(如API请求签名)
最终简单的编写一个搜索代码查看一下是否可以正常获取到相关的数据,如下所示:

案列比较简单,适合正在或想要学习逆向新手小伙伴练练手
相关文章:
某小程序sign签名参数逆向分析
文章目录 1. 写在前面2. 接口分析3. 分析还原 【🏠作者主页】:吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python…...
智能风控/数据分析 聚合 分组 连接
data。head()查看前几行 data.head() 是一个在Python的Pandas库中常用的方法,用于查看DataFrame对象的前几行数据。默认情况下,head() 方法会返回DataFrame的前5行数据,但是你也可以通过传递一个整数参数来指定返回的…...
Unity3D PBR光照计算公式推导详解
前言 在Unity3D中,PBR(Physically Based Rendering,基于物理的渲染)光照模型是一种高级光照模型,它模拟了真实世界中光的传播和反射过程,从而提供了更加逼真的渲染效果。PBR光照模型的计算公式涉及多个物理…...
行为树详解(6)——黑板模式
【动作节点数据共享】 行为树中需要的参数可以来自游戏中的各个模块,如果仅需从多个模块获取少量参数,那么可以直接在代码中调用其他模块的单例继而层层调用获取数据。 如果获取的参数量很大,从架构上看,我们需要通过加一个中间…...
Vue.js与其他框架有哪些兼容性?
Vue.js的兼容性主要体现在几个方面,包括浏览器支持、运行环境适应性、与其他库和框架的集成能力等。以下是更详细的解释: 浏览器兼容性 现代浏览器:Vue.js广泛支持所有主流的现代浏览器,如Google Chrome, Firefox, Safari, Edge…...
Java 8 Stream 介绍
Java 8 Stream 介绍 1. 什么是Stream? Stream(流)是Java 8引入的全新概念,它是一个支持串行和并行聚合操作的元素序列。Stream API提供了一种声明式的方式来处理数据集合,可以让我们以一种类似SQL查询的方式处理数据…...
Java NIO、AIO分析
好的,下面将对Java中的**NIO(Non-blocking IO)和AIO(Asynchronous IO)**进行更深入的分析,重点探讨它们的特点和具体的应用场景。 一、Java NIO(Non-blocking IO)深入分析 1. 主要…...
pip下载包出现SSLError
报错: ERROR: Could not install packages due to an OSError: HTTPSConnectionPool(host‘files.pythonhosted.org’, port443): Max retries exceeded with url: /packages/8a/c2/ae7227e4b089c6a8210920db9d5ac59186b0a84eb1e6d96b9218916cdaf1/taming_transform…...
零成本的互联网创业创意有哪些?
在互联网时代,创业的门槛大大降低,即使没有大量的资金投入,也有许多机会可以实现创业梦想。以下将为您介绍一些零成本的互联网创业创意,帮助您在互联网的海洋中找到属于自己的宝藏。 一、内容创作与自媒体 (一&#…...
linux ubantu重启桌面
在 Ubuntu 系统中,重启桌面环境通常有几种方法,具体取决于你所使用的桌面环境(如 GNOME、KDE 等)。下面是几种常用的重启桌面的方法: 重启 GNOME 桌面环境 如果你使用的是 GNOME 桌面环境(Ubuntu 默认桌面…...
DeepSeek重新定义“Open“AI
“面对颠覆性技术,闭源所创造的护城河是暂时的。即使是OpenAI的闭源方法也无法阻止他人赶超。” ——梁文锋,DeepSeek CEO DeepSeek V3 是一个拥有6710亿参数的开源AI模型,正在提升AI效率的新标准。它在相对有限的预算下进行训练,…...
iOS - 自旋锁
在 Objective-C 运行时中大量使用自旋锁,主要有以下几个原因: 1. 性能考虑 上下文切换成本 // 自旋锁实现 static ALWAYS_INLINE void OSSpinLockLock(volatile OSSpinLock *lock) {do {while (lock->value ! 0) {__asm__ volatile ("pause&q…...
web应用网站如何启用http2请求
要启用 HTTP/2 协议,您需要确保您的 Web 服务器软件支持 HTTP/2,并进行相应的配置。以下是一些常见的 Web 服务器软件及其启用 HTTP/2 的方法: 1. Nginx 对于 Nginx,您需要确保使用的是 1.9.5 或更高版本,因为这些版本…...
python进阶06:MySQL
课后大总结 Day1 一、数据库命令总结 1.连接数据库 连接数据库进入mysql安装目录打开bin文件夹,输入cmd(此命令后无分号)mysql.exe -u root -ppassword命令后输入密码:root 设置密码set passwordpassword("root123"); 查看所有数据库show databases; …...
mac 使用zip2john破解zip压缩包密码
一、下载: git clone https://github.com/magnumripper/JohnTheRipper.git cd JohnTheRipper/src ./configure sudo make -s clean && sudo make -sj4 cd ../run二、使用: zip2john提取提取 ZIP 文件的哈希: ./zip2john protecte…...
若依中Feign调用的具体使用(若依微服务版自身已集成openfeign依赖,并在此基础上定义了自己的注解)
若依中Feign调用具体使用 注意:以下所有步骤实现的前提是需要在启动类上加入注解 EnableRyFeignClients 主要是为开启feign接口扫描 1.创建服务提供者(provider) 导入依赖(我在分析依赖时发现若依本身已经引入openfeign依赖,并在此基础上自定义了自己的EnableRyF…...
【算法题系列】LeetCode 5.最长回文子串|JavaScript 5种思路实现
题目描述 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 示例 1: 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。 示例 2: 输入: "cbbd" 输出: &q…...
基于ROS先验地图的机器人自主定位与导航SLAM
2021年学习,当时参加科大讯飞的智能车大赛, 【语音交互启动-teb算法路径规划A*算法自动避障路径最短优化yolo5目标检测视觉结果判断分类终点指定点位自动泊车语音播报。】 【讯飞学院】http://www.iflyros.com/home/ 一、全局路径规划中的地图 栅格地图&…...
nginx 1.6.3配置虚拟主机与rewrite-location匹配规则
1、 Nginx 虚拟主机配置(配置文件末尾以分号[;]结尾) (1) 准备测试目录站点 [rootWEB conf]# cd /application/nginx/conf/ [rootWEB conf]# mkdir extra (创建虚拟主机存放目录࿰…...
1130-host ... is not allowed to connect to this MySql serve
局域网内另外一台电脑使用navicat连接Mysql出现上述问题:不允许连接 解决方案: 1、输入命令:进入mysql mysql -u root -p 2、输入命令:展示所有数据库 show databases; 3、输入命令进入mysql数据库: use mysql; 4、…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...
数据库——redis
一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...
ThreadLocal 源码
ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物,因为每个访问一个线程局部变量的线程(通过其 get 或 set 方法)都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段,这些类希望将…...
