深入研究websocket直播中signature这个参数怎么来的,模拟自己生成一个

上一节课我们已经找到了生成signature这个字段的代码位置,就是这个B函数,嗯......听起来好像有点奇怪,但是它确实叫B啊,笑死。不管了,看一下里面的逻辑是啥。

注意e参数的内容是:

{
    "app_name": "douyin_web",
    "version_code": "180800",
    "webcast_sdk_version": "1.0.14-beta.0",
    "update_version_code": "1.0.14-beta.0",
    "compress": "gzip",
    "device_platform": "web",
    "cookie_enabled": true,
    "screen_width": 1512,
    "screen_height": 982,
    "browser_language": "zh-CN",
    "browser_platform": "MacIntel",
    "browser_name": "Mozilla",
    "browser_version": "5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
    "browser_online": true,
    "tz_name": "Asia/Shanghai",
    "cursor": "t-1718942296076_r-1_d-1_u-1_h-7382800685396382772",
    "internal_ext": "internal_src:dim|wss_push_room_id:7382777844734167858|wss_push_did:7347516590731134502|first_req_ms:1718942295989|fetch_time:1718942296076|seq:1|wss_info:0-1718942296076-0-0|wrds_v:7382800932146251064",
    "host": "https://live.douyin.com",
    "aid": "6383",
    "live_id": 1,
    "did_rule": 3,
    "endpoint": "live_pc",
    "support_wrds": 1,
    "user_unique_id": "7347516590731134502",
    "im_path": "/webcast/im/fetch/",
    "identity": "audience",
    "need_persist_msg_count": "15",
    "insert_task_id": "",
    "live_reason": "",
    "room_id": "7382777844734167858",
    "heartbeatDuration": "0"
}

注意t是很多参数的e里面的websocket_key数组,它里面是:

[
            {
                param_name: 'live_id',
                param_type: 'string',
            },
            {
                param_name: 'aid',
                param_type: 'string',
            },
            {
                param_name: 'version_code',
                param_type: 'string',
            },
            {
                param_name: 'webcast_sdk_version',
                param_type: 'string',
            },
            {
                param_name: 'room_id',
                param_type: 'string',
            },
            {
                param_name: 'sub_room_id',
                param_type: 'string',
            },
            {
                param_name: 'sub_channel_id',
                param_type: 'string',
            },
            {
                param_name: 'did_rule',
                param_type: 'string',
            },
            {
                param_name: 'user_unique_id',
                param_type: 'string',
            },
            {
                param_name: 'device_platform',
                param_type: 'string',
            },
            {
                param_name: 'device_type',
                param_type: 'string',
            },
            {
                param_name: 'ac',
                param_type: 'string',
            },
            {
                param_name: 'identity',
                param_type: 'string',
            },
        ]

有了这两个参数传递过来,那我们就可以安心研究B里面的逻辑了。

看一下这个for循环吧,它的逻辑就是取出e里面的参数(将t里面的参数名称),然后拼接到o这个字符串上,然后再把o传递给V()这个函数:

V()这个函数里面又做了什么事情呢?V()其实会返回一个函数,这个函数可以传递两个参数,

接下来继续看返回的这个函数里面代码逻辑: 

其实这里继续深入研究,会发现是把e参数转为Bytes数组了:

这个转换函数也可以自己写一个:

        const o =
            ',live_id=1,aid=6383,version_code=180800,webcast_sdk_version=1.0.14-beta.0,room_id=7382777844734167858,sub_room_id=,sub_channel_id=,did_rule=3,user_unique_id=7347516590731134502,device_platform=web,device_type=,ac=,identity=audience'
        const substr = o.substring(1)
        console.log('subStr----', substr)

        // 将字符串转Bytes数组
        const stringToBytes = (str) => {
            var array = new Uint8Array(str.length)
            for (var i = 0, l = str.length; i < l; i++) {
                array[i] = str.charCodeAt(i)
            }
            return array
        }

        console.log('字符串转为Bytes数组', stringToBytes(substr))

转换完之后,又使用wordsToBytes函数将结果转成了另外一个形式:

然后再调用bytesToHex函数:

const bytesToHex = function (e) {
    for (var t = [], r = 0; r < e.length; r++)
        t.push((e[r] >>> 4).toString(16)), t.push((15 & e[r]).toString(16))
    return t.join('')
}

转换之后的结果格式为:a5faced0e2965a966b9fde2044e3ff1e

然后再调用frontierSign函数将上面这串字符串转为signature的值:

但是这个frontierSign是啥呢?这是一个webmssdk.es5.js包里面的函数,所以需要将这个webmssdk.es5.js包下载到本地,然后集成到window对象上,就可以调用这个函数了,我这里写了一个demo:可以看到已经生成了值

demo代码如下:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <script src="./vFun.js"></script>
        <script src="./webmssdk.es5.js"></script>
    </head>
    <body>
        <div>测试代码</div>
    </body>
    <script>
        const config = [
            {
                param_name: 'live_id',
                param_type: 'string',
            },
            {
                param_name: 'aid',
                param_type: 'string',
            },
            {
                param_name: 'version_code',
                param_type: 'string',
            },
            {
                param_name: 'webcast_sdk_version',
                param_type: 'string',
            },
            {
                param_name: 'room_id',
                param_type: 'string',
            },
            {
                param_name: 'sub_room_id',
                param_type: 'string',
            },
            {
                param_name: 'sub_channel_id',
                param_type: 'string',
            },
            {
                param_name: 'did_rule',
                param_type: 'string',
            },
            {
                param_name: 'user_unique_id',
                param_type: 'string',
            },
            {
                param_name: 'device_platform',
                param_type: 'string',
            },
            {
                param_name: 'device_type',
                param_type: 'string',
            },
            {
                param_name: 'ac',
                param_type: 'string',
            },
            {
                param_name: 'identity',
                param_type: 'string',
            },
        ]

        // 使用for便利试试
        for (let { param_name: i } of config) {
            console.log('i----', i)
        }
        const o =
            ',live_id=1,aid=6383,version_code=180800,webcast_sdk_version=1.0.14-beta.0,room_id=7382772251994655488,sub_room_id=,sub_channel_id=,did_rule=3,user_unique_id=7347516590731134502,device_platform=web,device_type=,ac=,identity=audience'
        const substr = o.substring(1)
        console.log('subStr----', substr)
        // s函数就是stringToBytes
        const sResult = sFunc(substr)
        //  V()函数就是
        console.log('s函数stringToBytes结果', sResult)
        // 有了s的返回结果,再调用i.wordsToBytes
        // var r = i.wordsToBytes(s(e, t));
        const r = wordsToBytes(sResult)
        console.log('r----', r)
        // 最后调用bytesToHex;
        // return t && t.asBytes ? r : t && t.asString ? a.bytesToString(r) : i.bytesToHex(r)
        const bytesRes = bytesToHex(r)
        console.log('bytesRes----', bytesRes)
        const frontierSignRes = window.byted_acrawler.frontierSign({
            'X-MS-STUB': bytesRes,
        })
        console.log('frontierSignRes----', frontierSignRes)
    </script>
</html>

vFun.js的代码如下:

var sFunc = function (e, t) {
    // 判断e是不是string类型,是的话,把t赋值给e,然后
    e.constructor == String
        ? stringToBytes(e)
        : oFunc(e)
        ? (e = Array.prototype.slice.call(e, 0))
        : Array.isArray(e) || e.constructor === Uint8Array || (e = e.toString())
    for (
        var r = bytesToWords(e),
            l = 8 * e.length,
            c = 1732584193,
            u = -271733879,
            p = -1732584194,
            d = 271733878,
            h = 0;
        h < r.length;
        h++
    )
        r[h] =
            (((r[h] << 8) | (r[h] >>> 24)) & 16711935) |
            (((r[h] << 24) | (r[h] >>> 8)) & 4278255360)
    ;(r[l >>> 5] |= 128 << l % 32), (r[(((l + 64) >>> 9) << 4) + 14] = l)
    for (var m = sff, f = sgg, g = shh, _ = sii, h = 0; h < r.length; h += 16) {
        var v = c,
            C = u,
            y = p,
            T = d
        ;(c = m(c, u, p, d, r[h + 0], 7, -680876936)),
            (d = m(d, c, u, p, r[h + 1], 12, -389564586)),
            (p = m(p, d, c, u, r[h + 2], 17, 606105819)),
            (u = m(u, p, d, c, r[h + 3], 22, -1044525330)),
            (c = m(c, u, p, d, r[h + 4], 7, -176418897)),
            (d = m(d, c, u, p, r[h + 5], 12, 1200080426)),
            (p = m(p, d, c, u, r[h + 6], 17, -1473231341)),
            (u = m(u, p, d, c, r[h + 7], 22, -45705983)),
            (c = m(c, u, p, d, r[h + 8], 7, 1770035416)),
            (d = m(d, c, u, p, r[h + 9], 12, -1958414417)),
            (p = m(p, d, c, u, r[h + 10], 17, -42063)),
            (u = m(u, p, d, c, r[h + 11], 22, -1990404162)),
            (c = m(c, u, p, d, r[h + 12], 7, 1804603682)),
            (d = m(d, c, u, p, r[h + 13], 12, -40341101)),
            (p = m(p, d, c, u, r[h + 14], 17, -1502002290)),
            (u = m(u, p, d, c, r[h + 15], 22, 1236535329)),
            (c = f(c, u, p, d, r[h + 1], 5, -165796510)),
            (d = f(d, c, u, p, r[h + 6], 9, -1069501632)),
            (p = f(p, d, c, u, r[h + 11], 14, 643717713)),
            (u = f(u, p, d, c, r[h + 0], 20, -373897302)),
            (c = f(c, u, p, d, r[h + 5], 5, -701558691)),
            (d = f(d, c, u, p, r[h + 10], 9, 38016083)),
            (p = f(p, d, c, u, r[h + 15], 14, -660478335)),
            (u = f(u, p, d, c, r[h + 4], 20, -405537848)),
            (c = f(c, u, p, d, r[h + 9], 5, 568446438)),
            (d = f(d, c, u, p, r[h + 14], 9, -1019803690)),
            (p = f(p, d, c, u, r[h + 3], 14, -187363961)),
            (u = f(u, p, d, c, r[h + 8], 20, 1163531501)),
            (c = f(c, u, p, d, r[h + 13], 5, -1444681467)),
            (d = f(d, c, u, p, r[h + 2], 9, -51403784)),
            (p = f(p, d, c, u, r[h + 7], 14, 1735328473)),
            (u = f(u, p, d, c, r[h + 12], 20, -1926607734)),
            (c = g(c, u, p, d, r[h + 5], 4, -378558)),
            (d = g(d, c, u, p, r[h + 8], 11, -2022574463)),
            (p = g(p, d, c, u, r[h + 11], 16, 1839030562)),
            (u = g(u, p, d, c, r[h + 14], 23, -35309556)),
            (c = g(c, u, p, d, r[h + 1], 4, -1530992060)),
            (d = g(d, c, u, p, r[h + 4], 11, 1272893353)),
            (p = g(p, d, c, u, r[h + 7], 16, -155497632)),
            (u = g(u, p, d, c, r[h + 10], 23, -1094730640)),
            (c = g(c, u, p, d, r[h + 13], 4, 681279174)),
            (d = g(d, c, u, p, r[h + 0], 11, -358537222)),
            (p = g(p, d, c, u, r[h + 3], 16, -722521979)),
            (u = g(u, p, d, c, r[h + 6], 23, 76029189)),
            (c = g(c, u, p, d, r[h + 9], 4, -640364487)),
            (d = g(d, c, u, p, r[h + 12], 11, -421815835)),
            (p = g(p, d, c, u, r[h + 15], 16, 530742520)),
            (u = g(u, p, d, c, r[h + 2], 23, -995338651)),
            (c = _(c, u, p, d, r[h + 0], 6, -198630844)),
            (d = _(d, c, u, p, r[h + 7], 10, 1126891415)),
            (p = _(p, d, c, u, r[h + 14], 15, -1416354905)),
            (u = _(u, p, d, c, r[h + 5], 21, -57434055)),
            (c = _(c, u, p, d, r[h + 12], 6, 1700485571)),
            (d = _(d, c, u, p, r[h + 3], 10, -1894986606)),
            (p = _(p, d, c, u, r[h + 10], 15, -1051523)),
            (u = _(u, p, d, c, r[h + 1], 21, -2054922799)),
            (c = _(c, u, p, d, r[h + 8], 6, 1873313359)),
            (d = _(d, c, u, p, r[h + 15], 10, -30611744)),
            (p = _(p, d, c, u, r[h + 6], 15, -1560198380)),
            (u = _(u, p, d, c, r[h + 13], 21, 1309151649)),
            (c = _(c, u, p, d, r[h + 4], 6, -145523070)),
            (d = _(d, c, u, p, r[h + 11], 10, -1120210379)),
            (p = _(p, d, c, u, r[h + 2], 15, 718787259)),
            (u = _(u, p, d, c, r[h + 9], 21, -343485551)),
            (c = (c + v) >>> 0),
            (u = (u + C) >>> 0),
            (p = (p + y) >>> 0),
            (d = (d + T) >>> 0)
    }
    return endian([c, u, p, d])
}

// (s._blocksize = 16)
// (s._digestsize = 16)

const sff = function (e, t, r, i, n, o, a) {
    var s = e + ((t & r) | (~t & i)) + (n >>> 0) + a
    return ((s << o) | (s >>> (32 - o))) + t
}

const sgg = function (e, t, r, i, n, o, a) {
    var s = e + ((t & i) | (r & ~i)) + (n >>> 0) + a
    return ((s << o) | (s >>> (32 - o))) + t
}

const shh = function (e, t, r, i, n, o, a) {
    var s = e + (t ^ r ^ i) + (n >>> 0) + a
    return ((s << o) | (s >>> (32 - o))) + t
}

const sii = function (e, t, r, i, n, o, a) {
    var s = e + (r ^ (t | ~i)) + (n >>> 0) + a
    return ((s << o) | (s >>> (32 - o))) + t
}

const stringToBytes = function (str) {
    var array = new Uint8Array(str.length)
    for (var i = 0, l = str.length; i < l; i++) {
        array[i] = str.charCodeAt(i)
    }
    return array
}

const oFunc = function (e) {
    return (
        null != e &&
        (t(e) ||
            ('function' == typeof e.readFloatLE &&
                'function' == typeof e.slice &&
                t(e.slice(0, 0))) ||
            !!e._isBuffer)
    )
}

const bytesToWords = function (e) {
    for (var t = [], r = 0, i = 0; r < e.length; r++, i += 8)
        t[i >>> 5] |= e[r] << (24 - (i % 32))
    return t
}

const wordsToBytes = function (e) {
    for (var t = [], r = 0; r < 32 * e.length; r += 8)
        t.push((e[r >>> 5] >>> (24 - (r % 32))) & 255)
    return t
}

const endian = function (e) {
    if (e.constructor == Number)
        return (16711935 & rotl(e, 8)) | (4278255360 & rotl(e, 24))
    for (var t = 0; t < e.length; t++) e[t] = endian(e[t])
    return e
}

const rotl = function (e, t) {
    return (e << t) | (e >>> (32 - t))
}

const bytesToHex = function (e) {
    for (var t = [], r = 0; r < e.length; r++)
        t.push((e[r] >>> 4).toString(16)), t.push((15 & e[r]).toString(16))
    return t.join('')
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/729955.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java基础之练习(2)

需求: 键盘录入一个字符串,使用程序实现在控制台遍历该字符串 package String;import java.util.Scanner;public class StringDemo5 {public static void main(String[] args) {//录入一个字符串Scanner sc new Scanner(System.in);System.out.println("请输入一个字符串…

vue3 antdv RadioButton默认值选择问题处理

1、先上官方文档&#xff1a; Ant Design Vue — An enterprise-class UI components based on Ant Design and Vue.js 官方代码&#xff1a; <template><div><div><a-radio-group v-model:value"value1"><a-radio-button value"a…

网络编程5----初识http

1.1 请求和响应的格式 http协议和前边学过的传输层、网络层协议不同&#xff0c;它是“一问一答”形式的&#xff0c;所以要分为请求和响应两部分看待&#xff0c;同时&#xff0c;请求和响应的格式是不同的&#xff0c;我们来具体介绍一下。 1.1.1 请求 在介绍请求之前&…

助力数据跨境,最新政策解读与应用实践分享

6月13日&#xff0c;VERYCLOUD睿鸿股份联合深数所企业数据合规服务南山工作站、亚马逊云科技&#xff0c;在深圳南山区共同主办了一场关于《数据出境合规之路——法规解析与实践探索》研讨会。 VERYCLOUD睿鸿股份是南山大数据产业协会的副会长单位。南山大数据产业协会作为深圳…

C++代码编写风格:Header-Only与声明实现分离的选择

C代码编写风格&#xff1a;Header-Only与声明实现分离的选择 最近看到一些小伙伴问到了几个比较有趣的问题&#xff0c;这里总结一下&#xff0c;这些都是实际面试中出现过的问题&#xff0c;看看你知道多少&#xff0c;考察一下底子。 面试问题1&#xff1a;你通常编写代码的风…

实战!如何从零搭建10万级 QPS 大流量、高并发优惠券系统--图文解析

实战&#xff01;如何从零搭建10万级 QPS 大流量、高并发优惠券系统–图文解析 原文链接&#xff1a;https://juejin.cn/post/7087824893831544845 原文作者&#xff1a;字节跳动技术团队 需求背景 需要设计、开发一个能够支持十万级 QPS 的优惠券系统 什么是QPS? Queri…

集采商城,智能费控4.0的核心载体

纵观企业费控模式进化史&#xff0c;从1.0手工报销模式到2.0线上报销模式&#xff0c;再到3.0移动报销模式&#xff0c;企业对费用管控的模式与技术虽然在不断升级迭代&#xff0c;但始终没有将消费端纳入费控体系&#xff0c;难以形成完整链路和闭环。 智能费控4.0创新提出场景…

SpringBoot+Vue物流快递仓库管理系统

物流快递仓库管理是一项非常繁琐复杂的工作&#xff0c;每天要处理大量的单据数据&#xff0c;包括入库、出库、退库、调库等多项货物操作流程。因此&#xff0c;为提高库管工作的质量和效率&#xff0c;就必须根据仓库管理的特点开发库存物流信息系统。 本文立足于物流信息系…

基于大数据的计算机就业数据可视化分析项目

使用Python作为编程语言&#xff0c;配合MySQL数据库以及Hadoop和Spark等大数据处理工具&#xff0c;实现了数据的抓取、清洗、分析到可视化展示的整个流程。系统采用Scrapy爬虫框架从拉勾网招聘平台高效抓取计算机行业的就业数据。随后&#xff0c;通过Pandas库对数据进行了深…

三人同行免单模式:社交电商的新趋势

在当今社交电商日益繁荣的背景下&#xff0c;三人同行免单模式作为一种创新的购物激励机制&#xff0c;正逐渐受到消费者和品牌的青睐。该模式通过消费者之间的互动和分享&#xff0c;促进产品销售和品牌推广&#xff0c;实现消费者与品牌的双赢。 模式概述 三人同行免单模式的…

Bilibili开源发布轻量级 Index 系列语言模型:2.8T 训练数据,支持角色扮演

Bilibili首次发布 Index 系列模型中的轻量版本&#xff1a;Index-1.9B 系列 本次开源的 Index-1.9B系列包含以下模型&#xff1a; Index-1.9Bbase:基座模型&#xff0c;具有 19 亿 非词嵌入参数量&#xff0c;在 2.8T 中英文为主的语料上预训练&#xff0c;多个评测基准上与…

养猫的上班族还不买智能猫砂盆吗?自费测评好用合集来啦!

到底谁还在蹲点等下班然后冲回家给猫铲屎的&#xff1f;原来是以前的我啊&#xff0c;任劳任怨给猫铲屎&#xff0c;上完自己的班回家还要给猫上班&#xff0c;累死累活的真的受不了&#xff01;最后实在受不了&#xff0c;在网上看了各种智能猫砂盆的测评后果断购入&#xff0…

mysql中的lead函数和over函数

文章目录 mysql中的lead函数和over函数作用需求场景实现方法group by分组和使用over函数分组有什么区别&#xff1f; mysql中的lead函数和over函数 作用 over函数是用来分组加排序的&#xff0c;然后等over函数分好组排好序之后&#xff0c;再使用lead函数去找当前分组内的下…

JMeter详解

一、线程组 作用:线程组就是控制Imeter用于执行测试的一组用户 位置:右键点击测试计划’-->添加 -->线程(用户)--> 线程组 特点: 模拟多人操作线程组可以添加多个&#xff0c;多个线程组可以并行或串行取样器(请求)和逻辑控制器必须依赖线程组才能使用线程组下可以…

自动化测试:Autorunner的使用

自动化测试&#xff1a;Autorunner的使用 一、实验目的 1、掌握自动化测试脚本的概念。 2、初步掌握Autorunner的使用 二、Autorunner的简单使用 autoRunner使用方法 新建项目 a) 在项目管理器空白区域,右键鼠标,选择新建项目 b) 输入项目名后,点击[确定]. 在初次打开aut…

手机怎么自动切换ip地址

在数字化时代&#xff0c;网络IP地址不仅是设备在网络世界的标识&#xff0c;也是确保用户网络安全和数据隐私的关键因素。对于手机用户来说&#xff0c;在某些情境下可能需要自动切换IP地址&#xff0c;本文将为您介绍手机怎么自动切换IP地址。 随着网络技术的发展&#xff0c…

python项目(课设)——飞机大战小游戏项目源码(pygame)

主程序 import pygame from plane_sprites import * class PlaneGame: """ 游戏类 """ def __init__(self): print("游戏初始化") # 初始化字体模块 pygame.font.init() # 创建游戏…

CocosCreator 微信小游戏上架流程准备工作

前言 事前准备非常重要&#xff0c;因为有creator的助力&#xff0c;实际上开发小游戏往往很快&#xff0c;但是如果准备不足&#xff0c;上架及审核过程非常慢&#xff0c;往往游戏做好了&#xff0c;还得各种排队等审核&#xff0c;大多数开发者又不是腾讯白名单之内&#x…

在WordPress上添加亚马逊联盟链接的三种方法

在互联网快速发展的今天&#xff0c;很多人都希望通过网络来增加收入&#xff0c;而加入亚马逊联盟计划&#xff08;Amazon Associates&#xff09;无疑是一个不错的选择。如果你有一个WordPress网站&#xff0c;那么在文章中添加亚马逊联盟链接是个很好的变现方式。今天&#…

什么样的企业适合运用裂变拉新工具?深入解析

在当今数字化快速发展的时代&#xff0c;裂变拉新工具已成为许多企业吸引新用户、扩大市场影响力的重要手段。然而&#xff0c;并非所有企业都适合运用这种工具。林叔将探讨哪些类型的企业更适合运用裂变拉新工具&#xff0c;并分析其背后的原因。 首先&#xff0c;拥有高度用…