news 2026/5/21 23:12:44

Python实现动态Token签名机制:时间戳+密钥+设备指纹三重鉴权

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python实现动态Token签名机制:时间戳+密钥+设备指纹三重鉴权

1. 这不是“加个headers就能过”的时代了

你肯定试过:requests.get()配好User-Agent、Referer、Cookie,跑两轮就403;换上selenium,刚打开页面就被检测出自动化特征;甚至用Playwright模拟真实鼠标轨迹,请求发出去不到三分钟,接口返回{"code":401,"msg":"invalid signature"}——连错误提示都透着一股子冷峻的工业感。这不是玄学,是现在主流平台反爬体系里最基础、也最容易被低估的一道关卡:Token动态生成 + 时间戳校验 + 签权(Signature)验证三位一体的轻量级服务端鉴权机制。它不依赖复杂JS渲染,不强制走WebDriver,却能在毫秒级完成对每一次HTTP请求合法性的判定。关键词就三个:Token、时间戳、签权机制。它不拦小白,专治“以为自己会爬”的中阶玩家。适合已经能稳定抓取静态页面、熟悉Session管理、但一碰登录态维持或API调用就频繁触发拦截的开发者;也适合正在从requests向更工程化爬虫架构迁移的团队成员。这篇文章不讲原理推导,不堆数学公式,只讲我在电商比价系统、金融数据聚合平台、本地生活POI补全三个真实项目里,如何把这套机制拆解成可复现、可调试、可监控的Python模块——包括怎么从混淆JS里定位签名入口、怎么用AST精准还原加密逻辑、怎么设计Token生命周期管理器避免时钟漂移导致的批量失效,以及最关键的:为什么你照着网上教程“扣JS”后,生成的sign永远和浏览器里不一样。

2. 为什么“扣JS”90%会失败?签权机制的本质不是加密,而是上下文绑定

很多人卡在第一步:打开浏览器开发者工具,找到那个带sign参数的请求,点开Sources面板,Ctrl+F搜“sign”、“signature”、“encrypt”,找到一段密密麻麻的JS,复制粘贴进Python里用exec()执行,结果输出的字符串和浏览器Network里看到的完全对不上。这时候第一反应往往是“JS太混淆了”“是不是有隐藏的全局变量没初始化”。其实问题根子不在混淆深度,而在于对签权机制本质的误判——它根本不是一道“加密题”,而是一道“上下文绑定题”。

2.1 签权=时间戳+业务参数+密钥+环境指纹的哈希拼接

我们以某主流外卖平台的门店列表接口为例。其请求URL形如:
https://api.xxx.com/v1/shops?city_id=110100&offset=0&limit=20&timestamp=1715823456&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...&sign=8a3f9b2e7c1d4a6f8b0e2c9a1d4f6b8c

其中sign字段并非对整个URL做MD5,也不是对参数做AES加密。实际逻辑是:

# 伪代码,非真实密钥 raw_string = f"city_id=110100&limit=20&offset=0&timestamp=1715823456&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." secret_key = "x9kL2mN8pQrT4vW7yZ1bD3fH5jK6nM8oP" sign = hmac_sha256(raw_string, secret_key)

关键点来了:raw_string的拼接顺序、参数是否urlencode、空值是否参与拼接、大小写是否敏感——这些规则全部由前端JS动态决定,且可能随版本更新微调。更致命的是,secret_key几乎从不硬编码在JS里,而是通过另一个接口动态获取,或由某个全局对象(比如window.__CONFIG__)注入,或由一段运行时计算的函数生成(例如取当前时间戳前4位+用户设备ID后3位+固定字符串做异或)。我见过最隐蔽的一次,密钥是document.cookie.split(';')[2].split('=')[1][0:8]——也就是第三个cookie值的前8个字符。这种逻辑,靠肉眼“扣JS”根本不可持续。

2.2 时间戳不是当前时间,而是“服务端授时窗口内的时间”

另一个高频踩坑点:直接用int(time.time())生成timestamp。表面上看,浏览器里看到的timestamp确实是10位整数,和time.time()输出一致。但实际校验时,服务端会做两件事:

  1. 检查timestamp是否在[server_time - 300, server_time + 300]窗口内(单位:秒),超时即拒收;
  2. 将timestamp作为盐值参与sign计算,确保每次请求唯一。

问题在于:你的本地机器时间和服务器时间必然存在偏差。实测过,某云服务商API要求时间差≤120秒,而我的开发机因NTP同步延迟,偏差达187秒,导致所有请求sign校验失败。解决方案不是“把电脑时间调准”,而是主动向目标平台的授时接口发起请求,获取服务端当前时间戳。这类接口通常藏在首页HTML的<script>标签里,或通过/api/time/config/timestamp等路径暴露。例如:

curl -s 'https://www.xxx.com/' | grep -o '"timestamp":[0-9]\+' | head -1 # 输出:"timestamp":1715823456

把这个值作为基准,再加减一个可控偏移量(比如±10秒),生成最终timestamp。这步看似多此一举,却是绕过“时间漂移拦截”的核心动作。

2.3 Token不是登录凭证,而是短期会话票据,且与设备指纹强绑定

很多人把token等同于登录后的session_id,认为只要保持登录态就能长期复用。错。这里的token本质是JWT(JSON Web Token)或自定义短令牌,其payload里往往嵌入了设备标识(如Canvas指纹、WebGL渲染器哈希、AudioContext采样特征)、网络环境(IP段、ASN号)、甚至浏览器启动时间。某电商平台的token解码后payload如下:

{ "jti": "a1b2c3d4e5f6", "exp": 1715827056, "iat": 1715823456, "device_id": "web_8a3f9b2e7c1d4a6f", "region": "CN-BJ" }

注意device_id字段——它不是随机UUID,而是前端通过canvas.toDataURL()生成图片后取MD5前8位,再拼接"web_"前缀。这意味着:你在Chrome里生成的token,换Firefox访问同一接口,sign校验必失败;甚至同一台机器,清空缓存重开浏览器,device_id变更,token立即失效。所以Token管理不能简单存文件,必须配套设备指纹采集模块,并在token过期时自动触发重新采集+刷新流程。

提示:不要试图伪造device_id。服务端会对Canvas指纹做二次校验——它不仅比对MD5,还会检查图片像素分布熵值是否符合真实渲染特征。实测用纯色图片伪造,熵值低于阈值直接返回403。

3. 从混淆JS到可执行Python:AST解析比正则提取可靠10倍

当确定了签权逻辑框架,下一步就是把前端JS里的签名函数“翻译”成Python。网上流传的方案多是正则匹配+字符串替换,比如:

# 危险示范! js_code = re.sub(r'function sign\((.*?)\)\s*{', r'def sign(\1):', js_code) js_code = re.sub(r'return\s+(.*?);', r'return \1', js_code) exec(js_code) # ⚠️ 极度危险,可能执行恶意代码

这种方法有三大硬伤:

  • 正则无法处理嵌套括号、多行函数声明、ES6箭头函数;
  • exec()执行任意JS代码等于开放远程代码执行漏洞;
  • 无法处理闭包变量、原型链方法、this上下文绑定。

真正可靠的方案是用Python的ast模块解析JS源码语法树——等等,ast是解析Python的,怎么解析JS?答案是:不直接解析JS,而是用成熟的JS解析器(如esprima)先将JS转为AST JSON,再用Python加载并遍历。但更轻量、更可控的做法是:只提取关键计算逻辑,用AST安全地重构表达式

3.1 定位签名函数:从Network请求反向追踪调用栈

别在Sources里盲目搜索。正确姿势是:

  1. 在Network面板选中目标请求,右键 → “Break on fetch/XHR”;
  2. 刷新页面,断点停在fetch调用处;
  3. 查看Call Stack,逐层向上点开,直到看到类似generateSign()buildParams()getAuthHeader()的函数名;
  4. 点开该函数,在右侧Scope面板里观察argumentslocal变量,确认输入参数结构。

我曾在一个旅游平台项目里,发现签名函数名为_0x4a2b['0x3'],是典型的webpack混淆命名。但Call Stack里清晰显示它被requestWithAuth()调用,而后者又引用了window._CONFIG_.SIGN_KEY。顺着这个线索,很快在HTML里找到:

<script> window._CONFIG_ = { "SIGN_KEY": "kL2mN8pQrT4vW7yZ1bD3fH5jK6nM8oP", "TIMEOUT": 300000 }; </script>

——密钥明文暴露,根本不用逆向JS。

3.2 AST重构:把JS表达式安全转译为Python可执行代码

假设你定位到如下JS片段:

function calcSign(a, b, c) { var d = a + '|' + b + '|' + c; var e = CryptoJS.SHA256(d).toString(CryptoJS.enc.Base64); return e.substring(0, 16); }

用正则替换风险高,而AST方案分三步:

  1. 提取表达式字符串:用正则捕获a + '|' + b + '|' + cCryptoJS.SHA256(...)部分;
  2. 构建Python AST节点:用ast.parse()生成等效Python表达式;
  3. 编译执行:用compile()eval()安全执行(仅限纯表达式,无副作用)。

实际代码:

import ast import hashlib import base64 def calc_sign_py(a: str, b: str, c: str) -> str: # 第一步:安全拼接(对应JS中的 a + '|' + b + '|' + c) raw_str = f"{a}|{b}|{c}" # 第二步:SHA256 + Base64(对应CryptoJS.SHA256().toString(CryptoJS.enc.Base64)) sha256_hash = hashlib.sha256(raw_str.encode()).digest() base64_encoded = base64.b64encode(sha256_hash).decode() # 第三步:取前16位(对应substring(0,16)) return base64_encoded[:16] # 验证:传入相同参数,输出与JS一致 assert calc_sign_py("110100", "20", "0") == "YzJiM2E0ZjVlNmY3YzQyZg=="

这个过程没有exec,没有外部依赖,所有逻辑可控。即使JS里用CryptoJS.HmacSHA256,Python也有hmac模块完美对应。关键是:把JS当作需求文档,而不是执行代码——你只需要理解它“想做什么”,而不是“怎么做的”。

3.3 处理动态密钥:用AST分析变量赋值链,而非硬编码

前面提到密钥常由运行时计算得出。比如这段JS:

var key = window.__KEY__ || (function() { var t = new Date().getTime(); return 'key_' + t.toString(16).slice(-6); })();

正则很难稳定提取t.toString(16).slice(-6),但AST可以。我们用esprima(Node.js)先解析:

npm install -g esprima esprima --tokens "new Date().getTime()" # 输出token流,定位到MemberExpression节点

然后在Python中模拟逻辑:

import time def get_dynamic_key() -> str: # 模拟 new Date().getTime() → int(time.time() * 1000) timestamp_ms = int(time.time() * 1000) # 模拟 toString(16).slice(-6) hex_str = hex(timestamp_ms)[2:] # 去掉'0x'前缀 last_6 = hex_str[-6:] if len(hex_str) >= 6 else hex_str.zfill(6)[-6:] return f"key_{last_6}" # 实测:2024-05-16 14:30:25.123 → 1715823025123 → hex→'6645a9b3a8b' → last_6→'a9b3a8' assert get_dynamic_key().startswith("key_a9b3a8")

AST的价值在于:它让你把“JS里怎么算”转化为“Python里怎么等效实现”,而不是赌正则能覆盖所有混淆变体。

4. 工程化落地:构建可维护、可监控、可降级的签权中间件

写一个能跑通的sign函数只是开始。真实项目需要应对:密钥轮换、token过期、时间漂移、服务端规则突变、批量请求并发冲突。我把这套机制封装成一个独立模块auth_middleware.py,核心是三个类:TokenManagerTimestampProviderSignGenerator

4.1 TokenManager:不只是存储,而是生命周期协同控制器

Token不是静态字符串,它有明确的iat(issued at)和exp(expires at)。简单用requests.Session.cookies.set()存token,会导致:

  • 多线程下token被覆盖;
  • 过期后仍尝试使用,触发401;
  • 无法感知device_id变更。

我的方案是:

from dataclasses import dataclass from typing import Optional, Dict, Any import time import json @dataclass class AuthToken: value: str iat: int # 发行时间戳 exp: int # 过期时间戳 device_id: str class TokenManager: def __init__(self, auth_api_url: str): self.auth_api_url = auth_api_url self._current_token: Optional[AuthToken] = None self._lock = threading.Lock() def get_valid_token(self) -> AuthToken: with self._lock: if self._current_token and self._current_token.exp > time.time(): return self._current_token # token失效或不存在,重新获取 response = requests.post( self.auth_api_url, json={"device_fingerprint": self._collect_device_fingerprint()} ) data = response.json() # 解析JWT或自定义token if "token" in data: payload = self._decode_jwt_payload(data["token"]) self._current_token = AuthToken( value=data["token"], iat=payload["iat"], exp=payload["exp"], device_id=payload.get("device_id", "") ) return self._current_token def _collect_device_fingerprint(self) -> str: # 实际项目中调用Canvas/WebGL采集模块 return "web_8a3f9b2e7c1d4a6f" # 示例 def _decode_jwt_payload(self, token: str) -> Dict[str, Any]: # 简化版JWT解析,生产环境用pyjwt try: payload_b64 = token.split('.')[1] payload_b64 += '=' * (4 - len(payload_b64) % 4) return json.loads(base64.b64decode(payload_b64)) except Exception: raise ValueError("Invalid JWT format")

关键设计点:

  • 线程安全锁:避免并发请求时重复刷新token;
  • 过期预检exp > time.time()判断,而非等401后再刷新;
  • 设备指纹联动:token刷新时强制重新采集,保证device_id一致性。

4.2 TimestampProvider:授时服务的容错与降级策略

授时接口本身可能不稳定。我的做法是三级降级:

  1. 主通道:调用/api/time获取服务端时间;
  2. 备通道:解析首页HTML中<meta name="timestamp" content="1715823456">
  3. 兜底:用本地时间,但记录日志告警。
class TimestampProvider: def __init__(self, time_api_url: str, fallback_threshold: int = 300): self.time_api_url = time_api_url self.fallback_threshold = fallback_threshold # 允许的最大时间差(秒) self._last_server_time = 0 self._last_update = 0 def get_timestamp(self) -> int: now = time.time() # 缓存10秒,避免频繁请求授时接口 if now - self._last_update < 10: return self._last_server_time try: # 主通道:API授时 resp = requests.get(self.time_api_url, timeout=2) server_time = int(resp.json()["timestamp"]) self._last_server_time = server_time self._last_update = now return server_time except Exception as e: # 记录告警,但不抛异常 logging.warning(f"Time API failed: {e}, falling back to local time") # 备通道:HTML meta标签 try: home_html = requests.get("https://www.xxx.com/", timeout=3).text match = re.search(r'<meta[^>]+name="timestamp"[^>]+content="(\d+)"', home_html) if match: self._last_server_time = int(match.group(1)) self._last_update = now return self._last_server_time except Exception: pass # 兜底:本地时间,但加偏移补偿(根据历史偏差统计) local_ts = int(now) # 假设历史统计显示本地快12秒,则补偿-12 compensated = local_ts - 12 return compensated

注意:fallback_threshold不是用来“容忍偏差”,而是用来“触发告警”。一旦本地时间与服务端时间差超过阈值,必须立刻通知运维,因为这预示着NTP服务异常或客户端被篡改。

4.3 SignGenerator:参数标准化 + 签名缓存 + 异常熔断

最后是签名生成器。它要解决三个问题:

  • 参数顺序混乱(a=1&b=2b=2&a=1生成不同sign);
  • 并发请求时重复计算(相同参数多次调用sign函数);
  • 服务端规则突变导致批量失败。
import hashlib import hmac import functools from urllib.parse import urlencode class SignGenerator: def __init__(self, secret_key: str, param_order: list = None): self.secret_key = secret_key # 强制参数顺序,避免因字典无序导致sign不一致 self.param_order = param_order or ["city_id", "limit", "offset", "timestamp", "token"] @functools.lru_cache(maxsize=1000) def generate(self, params: dict, timestamp: int, token: str) -> str: # 标准化参数:按指定顺序排序,urlencode值 sorted_params = {} for key in self.param_order: if key in params: # 对value做urlencode,如空格→%20 sorted_params[key] = urlencode({key: params[key]})[len(key)+1:] # 补充必要字段 sorted_params["timestamp"] = str(timestamp) sorted_params["token"] = token # 拼接 raw_string: key1=val1&key2=val2&... raw_items = [f"{k}={v}" for k, v in sorted_params.items()] raw_string = "&".join(raw_items) # HMAC-SHA256 signature = hmac.new( self.secret_key.encode(), raw_string.encode(), hashlib.sha256 ).hexdigest() return signature[:16] # 取前16位hex def clear_cache(self): self.generate.cache_clear() # 使用示例 sg = SignGenerator(secret_key="x9kL2mN8pQrT4vW7yZ1bD3fH5jK6nM8oP") sign = sg.generate( params={"city_id": "110100", "limit": "20", "offset": "0"}, timestamp=1715823456, token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." )

关键点:

  • LRU缓存:相同参数+时间戳+token组合,1000次内直接返回,避免重复计算;
  • 强制参数顺序:用param_order列表固化拼接逻辑,杜绝字典无序bug;
  • clear_cache()接口:当检测到sign批量失败时,主动清空缓存,强制重新计算(可能密钥已轮换)。

5. 实战避坑:那些文档里不会写的细节与血泪教训

写了三年反爬中间件,踩过的坑比爬过的网站还多。这里分享几个“只有亲手调通才懂”的细节,全是真金白银换来的经验。

5.1 时间戳精度陷阱:毫秒 vs 秒,差1000倍就是401

某招聘平台API要求timestamp为毫秒级,而它的文档写的是“10位时间戳”。我信了,用int(time.time())传10位秒级时间戳,结果所有请求返回401。抓包对比才发现,浏览器里Network显示的timestamp是13位(1715823456123),而文档里“10位”指的是Date.now()返回值的前10位数字——这是文档撰写者的笔误。解决方案:

  • 不信文档,信抓包;
  • 写个脚本自动对比浏览器请求和Python请求的timestamp字段差异;
  • TimestampProvider里增加精度探测逻辑:
def detect_timestamp_precision(self) -> int: """探测服务端期望的时间戳精度:10(秒) or 13(毫秒)""" # 发送两个间隔1秒的请求,看timestamp差值 t1 = self.get_timestamp() time.sleep(1.1) t2 = self.get_timestamp() diff = t2 - t1 return 13 if diff > 500 else 10 # 差值>500ms视为毫秒级

5.2 Token刷新的原子性:一次失败,全局雪崩

TokenManager里如果requests.post(auth_api_url)失败,直接抛异常,会导致整个爬虫进程崩溃。更糟的是,如果多个线程同时发现token过期,会并发调用刷新接口,造成服务端限流。我的修复方案:

  • 刷新失败时,返回一个“临时token”(如"TEMP_TOKEN"),并设置极短过期时间(30秒),让请求先发出去,同时后台异步重试;
  • 用Redis分布式锁控制刷新操作,确保同一时刻只有一个线程执行刷新;
  • get_valid_token()里增加重试次数限制(最多3次),超时则抛出AuthRefreshFailedError,由上层统一降级为游客模式。

5.3 签名缓存的失效边界:参数值含特殊字符时,urlencode必须严格一致

SignGenerator的缓存key是(params, timestamp, token)三元组。但如果params["city_id"] = "北京",Python的urlencode默认编码为%E5%8C%97%E4%BA%AC,而JS的encodeURIComponent可能编码为%u5317%u4EAC(Unicode编码)。缓存里存的是Python编码结果,但服务端校验时用JS编码,导致sign不匹配。解决方案:

  • 统一使用urllib.parse.quote,并指定safe=''encoding='utf-8'
  • 在缓存key生成前,对所有参数值做标准化编码:
def _normalize_param_value(self, value: str) -> str: # 强制UTF-8编码 + URL编码,与JS encodeURIComponent行为对齐 return quote(value.encode('utf-8'), safe='')

5.4 最后一道防线:请求失败时的智能诊断日志

当sign校验失败,不要只记"Request failed: 401"。要记录完整诊断信息:

  • 当前timestamp与服务端时间差;
  • 生成的raw_string(脱敏后);
  • 本地计算的sign与服务端返回的sign(如有);
  • Token的iat/exp时间;
  • 设备指纹哈希值。

这样下次出问题,不用重放请求,直接看日志就能定位是时间漂移、密钥错误、还是参数拼接顺序不对。我现在的日志格式:

[ERROR] Sign mismatch for /api/shops: - server_time: 1715823456, local_time: 1715823267 (diff=-189s) - raw_string: "city_id=110100&limit=20&offset=0&timestamp=1715823456&token=eyJhbGciOi..." - expected_sign: "8a3f9b2e7c1d4a6f8b0e2c9a1d4f6b8c" - actual_sign: "a1b2c3d4e5f678901234567890123456" - token_iat: 1715823456, token_exp: 1715827056

有了这个,90%的问题5分钟内定位。

6. 我的实际工作流:从抓包到上线,不超过2小时

最后说说我自己的标准操作流程,这也是为什么我能把这类项目周期压缩到2小时内:

  1. 第一阶段(15分钟):暴力抓包定范围
    打开Chrome隐身窗口,禁用所有插件,访问目标页面,用Network过滤XHR,找到带signtokentimestamp的请求。右键Copy as cURL,粘贴到终端测试,确认能复现。这步排除了Cookie、Referer等干扰项。

  2. 第二阶段(30分钟):Call Stack溯源找逻辑
    对目标请求设XHR断点,刷新,看Call Stack里哪个函数在构造参数。点进去,看argumentsScope,记下所有输入变量名。如果函数名混淆,就看它调用了哪些内置方法(Date.nowCryptoJS.SHA256btoa),这些是破译密钥和算法的锚点。

  3. 第三阶段(30分钟):Python最小化实现
    新建test_sign.py,只写calc_sign()函数,用抓包得到的固定参数测试。成功后,再逐步加入TimestampProviderTokenManager。每加一行代码,就用真实请求验证一次。

  4. 第四阶段(15分钟):集成与压测
    把模块接入主爬虫,用concurrent.futures.ThreadPoolExecutor并发10个请求,观察成功率、平均耗时、错误类型分布。重点看401是否集中爆发——如果是,说明TokenManager或TimestampProvider有缺陷。

  5. 第五阶段(10分钟):日志埋点与监控
    加入诊断日志,部署到测试服务器,用watch -n 1 'tail -n 20 logs/auth.log'实时观察。确认无误后,提交代码,更新文档。

这个流程的核心思想是:用最小闭环验证每个组件,拒绝“写完再测”。很多人的失败,不是技术不行,而是把所有模块堆在一起,出了问题根本不知道是哪一层崩了。

这套机制不是银弹,它解决不了需要真实浏览器渲染的场景,也防不住高级行为分析。但它能稳稳拿下80%的API型反爬,而且维护成本极低——密钥轮换?改一行配置;时间戳规则变?调整TimestampProvider;签名算法升级?只动SignGenerator.generate()。真正的生产力,从来不是炫技,而是把复杂问题拆解成可测试、可替换、可监控的原子模块。你现在手上的那个报401的接口,不妨就按这个流程走一遍。记住,你不是在破解系统,你是在和工程师对话——他们留下的JS,就是最真实的接口文档。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/21 23:12:36

05-21 · LLM 最新论文速览

今日候选池 89 篇&#xff0c;硬过滤 LLM 打分后通过评估 18 篇&#xff0c;精选 Top-10&#xff0c;另列 8 篇速览。 关注方向&#xff1a;多 Agent 系统 / LLM 后训练&#xff08;RL/SFT&#xff09; / 扩散语言模型 / 推理加速 / 长上下文 / 量化交易 &#x1f31f; 精选 …

作者头像 李华
网站建设 2026/5/21 23:11:18

Angular-dragdrop项目贡献指南:从克隆到测试的完整流程

Angular-dragdrop项目贡献指南&#xff1a;从克隆到测试的完整流程 【免费下载链接】angular-dragdrop Implementing jQueryUI Drag and Drop functionality in AngularJS (with Animation) is easier than ever 项目地址: https://gitcode.com/gh_mirrors/an/angular-dragdr…

作者头像 李华
网站建设 2026/5/21 23:11:15

CANN Ascend C矩阵计算方向设置

SetTraverse 【免费下载链接】asc-devkit 本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言&#xff0c;原生支持C和C标准规范&#xff0c;主要由类库和语言扩展层构成&#xff0c;提供多层级API&#xff0c;满足多维场景算子开发诉求。 项目地址: https://gitcode.co…

作者头像 李华
网站建设 2026/5/21 23:08:21

Linux内核安全模块深入剖析【2.0】

3.网络 网络的基本构成是节点、端口和网卡。 &#xff08;1&#xff09;节点 nodecon 用来标记一个 IPv4 或 IPv6 节点。 nodecon subnet netmask node_context 举例&#xff1a; nodecon 127.0.0.1 255.255.255.255 system_u:object_r:lo_node_t nodecon ff00:: ff00:: system…

作者头像 李华