Ansible 变量与加密文件全解析:从基础定义到安全实践

Ansible 变量与加密文件全解析:从基础定义到安全实践

一、变量命名规则

  • 变量名只能包含 字母、数字、下划线
  • 必须以 字母开头(不能以数字或下划线开头)
  • 区分大小写(my_varMy_Var 是不同变量)
  • 避免使用 Ansible 预定义的特殊变量名(如 inventory_hostnamegroups 等)

二、变量优先级(受版本影响可能会有略微不同)

1. Global 范围(最高优先级)

作用于整个 Ansible 执行过程 ,覆盖所有 Play 和主机,通常用于 "全局强制配置"。

主要来源:

  • 命令行变量 :通过 -e/--extra-vars 传递,是优先级最高的变量(除非特殊配置),常用于临时覆盖其他变量。

    复制代码
    [student@master ansible]$ ansible-playbook abc.yml -e "env=prod app_port=8080"
  • Ansible 配置文件变量 :在 ansible.cfg 中定义的变量(如 remote_userinventory),属于全局默认配置。

  • 环境变量 :系统环境变量中以 ANSIBLE_ 开头的变量(如 ANSIBLE_REMOTE_USER),会覆盖 ansible.cfg 中的对应配置。

2. Play 范围(中优先级)

作用于单个 Play 内的所有主机,仅在当前 Play 生效,常用于 "Play 级别的统一配置"。

  • set_fact定义变量:也是将值赋值给变量。

  • Play 的 vars 块:在 Play 中直接定义的变量,优先级高于 Host 范围变量。

    示例:

    yaml 复制代码
    - name: aaaaaa
      hosts: node1
      vars:  # Play 级变量
        - aaa: 11
        - bbb: 22
      tasks:
        - name: abcd
          debug:
            var: aaa,bbb
    [student@master ansible]$ ansible-playbook aaa.yml 
    ok: [node1] => {
        "aaa,bbb": "(11, 22)"
    }
  • Play 的 vars_files 块 :通过外部 YAML/JSON 文件引入的变量(如 vars/prod.yml),与 vars 块优先级相同(按定义顺序,后定义的覆盖先定义的)。

    yaml 复制代码
    [student@master ansible]$ vim bbb.yml   
    aaa: 123
    bbb: 321 
    - name: aaa
      hosts: node5
      vars_files: /home/student/ansible/bbb.yml
      tasks:
        - name: abcd
          debug:
            var: aaa,bbb
    [student@master ansible]$ ansible-playbook aaa.yml 
    ok: [node5] => {
        "aaa,bbb": "(123, 321)"
    }
  • Role 的 vars 目录 :Role 内部 vars/main.yml 中定义的变量,优先级高于 Role 的 defaults,且属于 Play 范围(因为 Role 依附于 Play 执行)。

  • Role 的 defaults 目录 :Role 内部 defaults/main.yml 中定义的变量,是 Role 的 "默认值",优先级低于 Play 的 vars 和 Host 范围变量(注意:这是 Play 范围内的 "次优先级",容易混淆)。

3. Host 范围(最低优先级)

作用于特定主机或主机组 ,仅对目标主机生效,常用于 "主机个性化配置"。

主要来源:

  • Inventory 主机变量 :直接在 Inventory 文件中为单个主机定义的变量(如 web1 ansible_host=192.168.1.10 app_port=80)。
  • Inventory 组变量 :为 Inventory 中的主机组定义的变量(如在 group_vars/webservers.yml 中定义 web_root=/var/www/html),组内所有主机共享。
  • Facts 变量 :Ansible 自动收集的主机信息(如 ansible_os_familyansible_memtotal_mb),变量名以 ansible_ 开头。默认情况下,Facts 变量不能被普通变量覆盖 (需通过 gather_facts: no 关闭,或修改 ansible.cfg 中的 allow_world_readable_facts 配置)。
  • Register 变量 :通过 register 关键字捕获任务执行结果的变量(如捕获 command 模块的输出),仅在当前 Play 内的目标主机生效,属于 "临时 Host 变量"。
    示例:

注册和定义变量的各种方式

ansible中定义变量的方式有很多种,大致有:

(1) 将模块的执⾏结果注册为变量

(2) 直接定义字典类型的变量;

(3) role中⽂件内定义变量;

(4) 命令⾏传递变量;

(5) 借助with_items迭代将多个task的结果赋值给⼀个变量;

(6) inventory中的主机或主机组变量;

(7) 内置变量。

(8)事实变量

(1) 将模块的执⾏结果注册为变量

在 Ansible 中,register 是一个非常实用的选项,它能将当前任务(task)的执行结果完整捕获并赋值给一个自定义变量。这个变量可以包含任务的输出内容、状态信息、返回码等细节,供后续任务进行判断、提取数据或做进一步处理。

核心作用
  • 保存任务执行的详细结果(成功 / 失败状态、输出内容、返回码等)
  • 实现 "基于前序任务结果的条件执行"
  • 提取任务输出中的关键信息(如命令返回值、文件状态等)

基本语法

yaml 复制代码
- name: 任务名称
  模块名:
    模块参数
  register: 自定义变量名  # 将结果赋值给该变量

例:

bash 复制代码
---
- name: aaa
  hosts: node1
  tasks:
    - name: cat file
      shell:
        cmd: sudo cat /tmp/1aa
      register: abc

    - name: creste debug
      debug:
        var: abc
        
        
[student@master ansible]$ ansible-playbook aaa.yml 

PLAY [aaa] *********************************************************************************

TASK [Gathering Facts] *********************************************************************
ok: [node1]

TASK [cat file] ****************************************************************************
changed: [node1]

TASK [creste debug] ************************************************************************
ok: [node1] => {
    "abc": {
        "changed": true,
        "cmd": "sudo cat /tmp/1aa",
        "delta": "0:00:00.014112",
        "end": "2025-09-01 20:19:39.712909",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2025-09-01 20:19:39.698797",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "aaaaaaaa",
        "stdout_lines": [
            "aaaaaaaa"
        ]
    }
}

PLAY RECAP *********************************************************************************
node1                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

如果只想要任务执行的结果

将var后面的值改成abc.stdout

bash 复制代码
---
- name: aaa
  hosts: node1
  tasks:
    - name: cat file
      shell:
        cmd: sudo cat /tmp/1aa
      register: abc

    - name: creste debug
      debug:
        var: abc.stdout
        
[student@master ansible]$ ansible-playbook aaa.yml         
TASK [creste debug] ************************************************************************
ok: [node1] => {
    "abc.stdout": "aaaaaaaa"
}
(2) 直接定义字典类型的变量;

在 Ansible 中,字典(dictionary)类型变量用于存储键值对(key-value)形式的数据,非常适合组织关联信息(如配置参数、多属性对象等)。直接定义字典变量的方式灵活,可在 Playbook、变量文件、set_fact等场景中使用,语法遵循 YAML 格式。

一、基本语法(YAML 字典格式)

Ansible 中字典的基本格式为:

键值对

yaml 复制代码
字典名:
  键1: 值1
  键2: 值2
  键3: 值3  # 值可以是字符串、数字、列表、甚至嵌套字典

键(key)通常不需要引号(特殊字符除外),值(value)支持多种类型(字符串、数字、列表、布尔值、嵌套字典等)。

实列在上面Play 范围(中优先级)中有写

(3) role中⽂件内定义变量;

未学之后补

(4) 命令⾏传递变量;

在 Ansible 中,通过命令行传递变量 是一种灵活的方式,常用于临时覆盖配置、动态指定参数(如环境标识、版本号等)。命令行传递的变量优先级最高,会覆盖 Playbook、变量文件、Inventory 等其他位置定义的同名变量,非常适合临时调整任务行为。

核心语法

通过 -e--extra-vars 选项传递变量,基本格式:

bash 复制代码
ansible-playbook 剧本名.yml -e "变量名=值"  # 单个变量
ansible-playbook 剧本名.yml -e "变量1=值1 变量2=值2"  # 多个变量(空格分隔)

列:

yaml 复制代码
---
- name: aaa
  hosts: node1
  tasks:
    - name: debug
      debug:
        msg: aaa={{ccc}} bbb={{ddd}}

[student@master ansible]$ ansible-playbook -e "ccc=111 ddd=222" aaa.yml       

ok: [node1] => {
    "msg": "aaa=111 bbb=222"
}

(5) 借助with_items迭代将多个task的结果赋值给⼀个变量;

在 Ansible 中,当使用 with_items(或较新的 loop)进行迭代时,任务会对列表中的每个项执行一次。此时,通过 register 注册的变量会自动包含一个 results 列表,存储每个迭代项的执行结果 。我们可以通过这个 results 列表,将多个迭代任务的结果汇总到一个变量中,实现 "将多个 task 结果赋值给一个变量" 的效果。

核心原理
  • 当任务使用 with_items 循环时,register 注册的变量会生成一个 results 属性(列表类型)。
  • results 列表中的每个元素对应一次迭代的结果,包含当前迭代项(item)和该次任务的详细输出(如 stdoutrcstat 等)。
  • 可通过遍历 results 列表,提取每个迭代的结果,实现对多个任务结果的集中处理。

例:

bash 复制代码
---
- name: aaa
 hosts: node1
 tasks:
   - name: debug
     shell:
       cmd: echo {{ item }}
     with_items:
       - aaaa
       - bbbb
       - cccc
     register: cy

   - name: look cy
     debug:
       var: cy.results[{{ item }}].stdout
     with_items:
       - 0
       - 1
       - 2

[student@master ansible]$ ansible-playbook aaa.yml 

*******************************************************************************
changed: [node1] => (item=aaaa)
changed: [node1] => (item=bbbb)
changed: [node1] => (item=cccc)

TASK [look cy] *****************************************************************************
ok: [node1] => (item=0) => {
   "ansible_loop_var": "item",
   "cy.results[0].stdout": "aaaa",
   "item": 0
}
ok: [node1] => (item=1) => {
   "ansible_loop_var": "item",
   "cy.results[1].stdout": "bbbb",
   "item": 1
}
ok: [node1] => (item=2) => {
   "ansible_loop_var": "item",
   "cy.results[2].stdout": "cccc",
   "item": 2
}

(6) inventory中的主机或主机组变量;

在 Ansible 中,inventory( inventory 文件或动态 inventory )不仅用于定义主机和主机组,还可以直接为主机主机组定义变量。这些变量可以在 Playbook、模板、模块中直接引用,用于定制针对特定主机或主机组的操作。

1. 主机变量(Host Variables)

主机变量是针对单个主机 定义的变量,仅对该主机生效。

通常用于描述主机的个性化属性(如 IP、端口、特定配置等)。

定义方式(在 inventory 文件中):
ini 复制代码
node1 aaa=1111
node2 aaa=2222
node3 aaa=3333
node4
node5

在剧本中

yaml 复制代码
---
- name: aaaaaa
  hosts: node1,node2,node3
  tasks:
    - name: abcd
      debug:
        var: aaa

运行结果

bash 复制代码
[student@master ansible]$ ansible-playbook bbb.yml 

PLAY [aaaaaa] ******************************************************************************

TASK [Gathering Facts] *********************************************************************
ok: [node1]
ok: [node3]
ok: [node2]

TASK [abcd] ********************************************************************************
ok: [node1] => {
    "aaa": 1111
}
ok: [node2] => {
    "aaa": 2222
}
ok: [node3] => {
    "aaa": 3333
}

也可用组的形式

inventory

ini 复制代码
[t1]
node1
node2


[t2]
node3
node4


[t3]
node5


[t1:vars]
aaa=123456

[t2:vars]
aaa=654321

[t3:vars]
aaa=abcdef

剧本

yaml 复制代码
---
- name: aaaaaa
  hosts: all
  tasks:
    - name: abcd
      debug:
        var: aaa

运行结果

bash 复制代码
[student@master ansible]$ ansible-playbook bbb.yml 

ok: [node1] => {
    "aaa": 123456
}
ok: [node2] => {
    "aaa": 123456
}
ok: [node3] => {
    "aaa": 654321
}
ok: [node4] => {
    "aaa": 654321
}
ok: [node5] => {
    "aaa": "abcdef"
}

创建host和组的专属存放变量文件的目录

  1. host_vars 目录(主机专属变量)
    • 路径:/etc/ansible/host_vars/(全局)或 Playbook 所在目录下的 host_vars/(项目内)。
    • 文件名与主机名一致,用于定义特定主机的变量。
  2. group_vars 目录(组专属变量)
    • 路径:/etc/ansible/group_vars/(全局)或 Playbook 所在目录下的 group_vars/(项目内)。
    • 文件名与组名一致,用于定义特定组内所有主机共享的变量。
bash 复制代码
[student@master ansible]$ mkdir host_vars
[student@master ansible]$ mkdir group_vars

[student@master ansible]$ vim host_vars/node1
aaa: 12345678

[student@master ansible]$ vim host_vars/node1.yml 
aaa: abcdefg

[student@master ansible]$ vim group_vars/t1
bbb: 1234

剧本

yaml 复制代码
---
- name: aaaaaa
  hosts: node1
  tasks:
    - name: abcd
      debug:
        var: aaa,bbb

运行效果

bash 复制代码
[student@master ansible]$ ansible-playbook bbb.yml 

PLAY [aaaaaa] ******************************************************************************

TASK [Gathering Facts] *********************************************************************
ok: [node1]

TASK [abcd] ********************************************************************************
ok: [node1] => {
    "aaa,bbb": "(12345678, 1234)"
}

删除/etc/ansible/host_vars/node1 保留/etc/ansible/host_vars/node1.yml,再次执行playbook

bash 复制代码
[student@master ansible]$ ansible-playbook bbb.yml 

PLAY [aaaaaa] ******************************************************************************

TASK [Gathering Facts] *********************************************************************
ok: [node1]

TASK [abcd] ********************************************************************************
ok: [node1] => {
    "aaa,bbb": "('abcdefg', 1234)"
}

(7)事实变量与内置变量

1. 事实变量(Facts Variables)
  • 定义 :Ansible 在执行 Playbook 时,通过setup模块自动从目标主机收集的系统 / 环境信息,是 "关于目标主机的动态事实"。
  • 本质:是目标主机的 "属性快照",内容完全依赖目标主机的实际状态(如操作系统版本、IP 地址、硬件配置等),不同主机的事实变量可能完全不同。
  • 特点:默认自动收集(可通过gather_facts: no关闭),内容动态、主机相关。
2. 内置变量(Built-in Variables)
  • 定义 :Ansible 框架自带的、不依赖目标主机的元数据变量,用于获取 Ansible 运行时信息、Inventory 配置信息或 Play/Task 上下文。
  • 本质:是 "关于 Ansible 自身或执行上下文的固定 / 半固定信息",不依赖目标主机,仅与 Ansible 环境、Inventory 配置或当前 Play 逻辑相关。
  • 特点:无需主动收集,Playbook 中可直接引用;内容静态(如 Ansible 版本)或基于配置(如 Inventory 分组),与目标主机无关。
1. 常用事实变量(Facts)

通过ansible node1 -m setup 可以查询node1主机所有的事实变量

  • ansible_hostname:目标主机的主机名(如web-server-01)。
  • ansible_os_family:目标主机的操作系统家族(如RedHatDebianWindows),常用于条件判断(例:RedHat 系装yum包,Debian 系装apt包)。
  • ansible_enp1s0.ipv4.address:目标主机网卡enp1s0的 IPv4 地址(如192.168.122.10)。
  • ansible_default_ipv4.address:目标主机的默认网卡ipv4地址
  • ansible_memtotal_mb:目标主机的总内存(单位:MB,如16384)。
  • ansible_pkg_mgr:目标主机默认的包管理器(如dnfaptpip)。
  • ansible_fqdn: 目标主机完全合格域名(FQDN)
  • ansible_bios_version
  • ansible_devices.vda.size: vda硬盘大小
  • ansible_devices.vdb.size: vdb硬盘大小
  • ansible_lvm.vgs: 逻辑卷中卷组
2. 常用内置变量(Built-in)
  • ansible_version:当前 Ansible 的版本信息(字典类型,如ansible_version.full2.16.3),常用于确保 Playbook 兼容特定版本。

  • inventory_hostname:目标主机在 Inventory 文件中定义的 "主机名 / 别名"(如 Inventory 中写web01 ansible_host=192.168.1.100,则inventory_hostnameweb01)。

  • play_hosts:当前 Play 的目标主机列表(列表类型,如["web01", "web02"]),常用于批量操作(例:获取所有目标主机 IP)。

  • ansible_play_name:当前 Play 的名称(如 Play 定义name: 部署Web服务,则此变量值为部署Web服务)。

  • groups

  • group_names:当前主机所属的 Inventory 分组列表(如["web_servers", "prod"]),常用于按分组执行任务。

  • groups.all 所有清单主机

  • groups.ungrouped:未分组的主机

  • inventory_dir:Inventory 文件所在的目录路径(如/etc/ansible/inventory)。

set_fact定义变量

在 Ansible 中,set_fact 是用于主动定义自定义事实变量(custom facts) 的模块。这些变量会被添加到主机的事实(facts)集合中,可在后续任务、甚至跨 Play 中像其他内置 facts(如 ansible_hostname)一样直接引用。set_fact 更接近 "动态变量赋值" 的逻辑,尤其适合基于现有变量(如 facts、register 结果、其他变量)构造新变量。

yaml 复制代码
---
- name: aaa
  hosts: all
  tasks:
    - name: set_fact     #将目标主机的域名变量和ip地址变量写成1一个变量
      set_fact:
        dns_and_ip: "{{ ansible_fqdn }}:{{ ansible_enp1s0.ipv4.address }} "

    - name: debug1
      debug:
        msg: "{{ ansible_hostname }} is {{ dns_and_ip }}  "


[student@master ansible]$ ansible-playbook aaa.yml 
ok: [node1] => {
    "msg": "node1 is node1.example.com:192.168.122.10   "
}
ok: [node2] => {
    "msg": "node2 is node2.example.com:192.168.122.20   "
}
ok: [node3] => {
    "msg": "node3 is node3.example.com:192.168.122.30   "
}
ok: [node4] => {
    "msg": "node4 is node4.example.com:192.168.122.40   "
}
ok: [node5] => {
    "msg": "node5 is node5.example.com:192.168.122.50   "
}

ansible_vault使用详解

Ansible Vault 是 Ansible 自带的敏感信息加密工具,用于加密包含密码、API 密钥、数据库凭证等敏感数据的文件(如变量文件、inventory 清单、playbook 片段等),防止敏感信息以明文形式存储在代码或配置文件中。

ansible_vault核心操作命令

创建加密的文件:

要创建新的加密文件,使用命令

bash 复制代码
ansible-vault create [选项] 文件名

常用选项

  • --vault-id 标识@密码文件`:指定加密密码(多密码场景)。
  • --ask-vault-pass:手动输入密码(默认方式)。

bash 复制代码
[student@master ansible]$ ansible-vault create ccc.yml 
New Vault password:   #输入密码
Confirm New Vault password:   #再次输入密码

使用vim和cat查看文件就会显示加密信息

bash 复制代码
[student@master ansible]$ cat ccc.yml 
$ANSIBLE_VAULT;1.1;AES256
36363464306637663362313261363430636565626364663233663030396136343862386161323565
6562643230383161623263663235383239613137353736650a396166656163333763373739633561
.......


[student@master ansible]$ vim ccc.yml 
$ANSIBLE_VAULT;1.1;AES256
36363464306637663362313261363430636565626364663233663030396136343862386161323565
6562643230383161623263663235383239613137353736650a396166656163333763373739633561
......

零时查看加密文件:

查看加密信息就要使用命令

bash 复制代码
ansible-vault view [选项] 文件名

yaml 复制代码
[student@master ansible]$ ansible-vault view ccc.yml 
Vault password: #输入密码
--- 
- name: ccc
  hosts: node1
  tasks: 
    - name: debug1
      debug: 
        msg: echo 123456 

输入密码后即可查看内容

修改加密文件

修改使用命令

bash 复制代码
ansible-vault edit [选项] 文件名

例:

yaml 复制代码
[student@master ansible]$ ansible-vault edit ccc.yml 
Vault password:  #输入密码
---
- name: ccc
  hosts: node1
  tasks:
    - name: debug1
      debug:
        msg: echo 123456

解密加密文件

解密的命令

bash 复制代码
ansible-vault decrypt [选项] 文件名

常用选项

  • --output 新文件名:解密到新文件(保留原加密文件)。

bash 复制代码
[student@master ansible]$ ansible-vault decrypt ccc.yml 
Vault password: 
Decryption successful

之后就可以直接查看和修改ccc.yml

bash 复制代码
[student@master ansible]$ cat ccc.yml 
--- 
- name: ccc
  hosts: node1
  tasks: 
    - name: debug1
      debug: 
        msg: echo 123456 

对已有的明文文件加密:

对文件加密使用

bash 复制代码
ansible-vault encrypt [选项] 文件名1 文件名2 ...

bash 复制代码
[student@master ansible]$ ansible-vault encrypt ccc.yml 
New Vault password: 
Confirm New Vault password: 
Encryption successful

再次查看就会发现

bash 复制代码
[student@master ansible]$ cat ccc.yml 
$ANSIBLE_VAULT;1.1;AES256
61373961663061356430643531613730623135373936663732353335393638643739623635613332
3530646333376437326330323532616330643131376234650a346362613663656564313635316466
..........

重设密码:

重设密码使用命令:

bash 复制代码
ansible-vault rekey [选项] 文件名1 文件名2 ...

bash 复制代码
[student@master ansible]$ ansible-vault rekey ccc.yml 
Vault password:  #旧密码
New Vault password:  #新密码
Confirm New Vault password: #再出输入新密码 
Rekey successful

ansible-vault encrypt_string

执行加密剧本

如果直接执行加密剧本

bash 复制代码
[student@master ansible]$ ansible-playbook ccc.yml 
ERROR! Attempting to decrypt but no vault secrets found

这时候就需要命令

bash 复制代码
ansible-playbook 文件名 --vault-id @prompt
或者
ansible-playbook 文件名 --ask-vault-pass

bash 复制代码
[student@master ansible]$ ansible-playbook ccc.yml --ask-vault-pass
Vault password:  #输入密码

PLAY [ccc] *********************************************************************************

TASK [Gathering Facts] *********************************************************************
ok: [node1]

TASK [debug1] ******************************************************************************
ok: [node1] => {
    "msg": "echo 123456"
}

用装有密码的文件输入密码

加上选项 --vault-id 存放密码的文件

bash 复制代码
#创建存放密码的文件
[student@master ansible]$ echo 3edc4rfv > pass
#将文件设置为仅自己可读写提升安全性
[student@master ansible]$ chmod 600 pass 
#运行加密脚本
[student@master ansible]$ ansible-playbook ccc.yml --vault-id pass

PLAY [ccc] ********************************************************************************

TASK [Gathering Facts] ********************************************************************
ok: [node1]

TASK [debug1] *****************************************************************************
ok: [node1] => {
    "msg": "echo 123456"
}

执行使用了加密文件的剧本

创建一个加密的存放变量的文件

bash 复制代码
[student@master ansible]$ ansible-vault create var.yml
New Vault password: 
Confirm New Vault password:

创建密码文件

bash 复制代码
[student@master ansible]$ echo 123456 >  varpass
[student@master ansible]$ chmod 600 varpass 

编写剧本

yaml 复制代码
---
- name: ccc
  hosts: node1
  vars_files: /home/student/ansible/var.yml
  tasks:
    - name: debug1
      debug:
        msg: "{{aaa,bbb}}"

如果直接运行就会

bash 复制代码
[student@master ansible]$ ansible-playbook ccc.yml 
ERROR! Attempting to decrypt but no vault secrets found

需要带上密码文件的地址

bash 复制代码
[student@master ansible]$ ansible-playbook ccc.yml --vault-id varpass

PLAY [ccc] ********************************************************************************

TASK [Gathering Facts] ********************************************************************
ok: [node1]

TASK [debug1] *****************************************************************************
ok: [node1] => {
    "msg": "(112233, 332211)"
}
相关推荐
GIS之路2 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug5 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu121387 分钟前
React面向组件编程
开发语言·前端·javascript
五仁火烧14 分钟前
生产环境中配置了接口3000后,不能启动,改成8080后就可以
linux·网络·安全·vue
专业开发者19 分钟前
借助安全返场方案提升智慧建筑能效的新机遇
物联网·安全
持续升级打怪中29 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路32 分钟前
GDAL 实现矢量合并
前端
hxjhnct35 分钟前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全