git-secret:在 Git 存储库中加密和存储密钥(下)

news2024/11/17 3:49:08

在本篇文章中,将带你了解如何在 Docker 容器中设置git-secretgpg,通过 Makefile recipe 为不同的场景创建工作流。

Makefile Adjustment

git-secretgpg指令添加到 Makefile 中.make/01-00-application-setup.mk

# File: .make/01-00-application-setup.mk

#...

# gpg

DEFAULT_SECRET_GPG_KEY?=secret.gpg
DEFAULT_PUBLIC_GPG_KEYS?=.dev/gpg-keys/*

.PHONY: gpg
gpg: ## Run gpg commands. Specify the command e.g. via ARGS="--list-keys"
    $(EXECUTE_IN_APPLICATION_CONTAINER) gpg $(ARGS)

.PHONY: gpg-export-public-key
gpg-export-public-key: ## Export a gpg public key e.g. via EMAIL="john.doe@example.com" PATH=".dev/gpg-keys/john-public.gpg"
    @$(if $(PATH),,$(error PATH is undefined))
    @$(if $(EMAIL),,$(error EMAIL is undefined))
    "$(MAKE)" -s gpg ARGS="gpg --armor --export $(EMAIL) > $(PATH)"

.PHONY: gpg-export-private-key
gpg-export-private-key: ## Export a gpg private key e.g. via EMAIL="john.doe@example.com" PATH="secret.gpg"
    @$(if $(PATH),,$(error PATH is undefined))
    @$(if $(EMAIL),,$(error EMAIL is undefined))
    "$(MAKE)" -s gpg ARGS="--output $(PATH) --armor --export-secret-key $(EMAIL)"

.PHONY: gpg-import
gpg-import: ## Import a gpg key file e.g. via GPG_KEY_FILES="/path/to/file /path/to/file2"
    @$(if $(GPG_KEY_FILES),,$(error GPG_KEY_FILES is undefined))
    "$(MAKE)" -s gpg ARGS="--import --batch --yes --pinentry-mode loopback $(GPG_KEY_FILES)"

.PHONY: gpg-import-default-secret-key
gpg-import-default-secret-key: ## Import the default secret key
    "$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_SECRET_GPG_KEY)"

.PHONY: gpg-import-default-public-keys
gpg-import-default-public-keys: ## Import the default public keys
    "$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_PUBLIC_GPG_KEYS)" 

.PHONY: gpg-init
gpg-init: gpg-import-default-secret-key gpg-import-default-public-keys ## Initialize gpg in the container, i.e. import all public and private keys

# git-secret

.PHONY: git-secret
git-secret: ## Run git-secret commands. Specify the command e.g. via ARGS="hide"
    $(EXECUTE_IN_APPLICATION_CONTAINER) git-secret $(ARGS)

.PHONY: secret-init
secret-init: ## Initialize git-secret in the repository via `git-secret init`
    "$(MAKE)" -s git-secret ARGS="init"

.PHONY: secret-init-gpg-socket-config
secret-init-gpg-socket-config: ## Initialize the config files to change the gpg socket locations
    echo "%Assuan%" > .gitsecret/keys/S.gpg-agent
    echo "socket=/tmp/S.gpg-agent" >> .gitsecret/keys/S.gpg-agent
    echo "%Assuan%" > .gitsecret/keys/S.gpg-agent.ssh
    echo "socket=/tmp/S.gpg-agent.ssh" >> .gitsecret/keys/S.gpg-agent.ssh
    echo "extra-socket /tmp/S.gpg-agent.extra" > .gitsecret/keys/gpg-agent.conf
    echo "browser-socket /tmp/S.gpg-agent.browser" >> .gitsecret/keys/gpg-agent.conf

.PHONY: secret-encrypt
secret-encrypt: ## Decrypt secret files via `git-secret hide`
    "$(MAKE)" -s git-secret ARGS="hide"

.PHONY: secret-decrypt
secret-decrypt: ## Decrypt secret files via `git-secret reveal -f`
    "$(MAKE)" -s git-secret ARGS="reveal -f" 

.PHONY: secret-decrypt-with-password
secret-decrypt-with-password: ## Decrypt secret files using a password for gpg via `git-secret reveal -f -p $(GPG_PASSWORD)`
    @$(if $(GPG_PASSWORD),,$(error GPG_PASSWORD is undefined))
    "$(MAKE)" -s git-secret ARGS="reveal -f -p $(GPG_PASSWORD)" 

.PHONY: secret-add
secret-add: ## Add a file to git secret via `git-secret add $FILE`
    @$(if $(FILE),,$(error FILE is undefined))
    "$(MAKE)" -s git-secret ARGS="add $(FILE)"

.PHONY: secret-cat
secret-cat: ## Show the contents of file to git secret via `git-secret cat $FILE`
    @$(if $(FILE),,$(error FILE is undefined))
    "$(MAKE)" -s git-secret ARGS="cat $(FILE)"

.PHONY: secret-list
secret-list: ## List all files added to git secret `git-secret list`
    "$(MAKE)" -s git-secret ARGS="list"

.PHONY: secret-remove
secret-remove: ## Remove a file from git secret via `git-secret remove $FILE`
    @$(if $(FILE),,$(error FILE is undefined))
    "$(MAKE)" -s git-secret ARGS="remove $(FILE)"

.PHONY: secret-add-user
secret-add-user: ## Remove a user from git secret via `git-secret tell $EMAIL`
    @$(if $(EMAIL),,$(error EMAIL is undefined))
    "$(MAKE)" -s git-secret ARGS="tell $(EMAIL)"

.PHONY: secret-show-users
secret-show-users: ## Show all users that have access to git secret via `git-secret whoknows`
    "$(MAKE)" -s git-secret ARGS="whoknows"

.PHONY: secret-remove-user
secret-remove-user: ## Remove a user from git secret via `git-secret killperson $EMAIL`
    @$(if $(EMAIL),,$(error EMAIL is undefined))
    "$(MAKE)" -s git-secret ARGS="killperson $(EMAIL)"

.PHONY: secret-diff
secret-diff: ## Show the diff between the content of encrypted and decrypted files via `git-secret changes`
    "$(MAKE)" -s git-secret ARGS="changes"

工作流程

使用git-secret非常简单:

  • 初始化git-secret

  • 添加所有用户。

  • 添加所有机密文件并确保这些文件通过.gitignore被忽略。

  • 加密文件。

  • 如果团队其他成员对文件进行更改,则需要解密文件→更新文件→再次提交加密文件

  • 如果对解密的文件进行更改,修改完需要再次重新进行加密。

下面的“流程挑战”部分展示了一些在可能遇到的问题,“场景”部分将会展示一些常见场景的具体示例。

流程中的挑战

从流程的角度,一起来看看在过程中可能遇到的一些困难和挑战,以及如何处理解决。

更新机密

更新机密时,请确保先解密文件,从而避免使用可能仍存在本地的旧文件。可以通过检查最新的 main 分支并运行git secret reveal,来获得最新版本的机密文件。也可以使用post-mergeGit hook 自动执行此操作,不过要注意覆盖本地机密文件的风险哦。

代码审查和合并冲突

由于无法对加密文件进行很好的区分,因此当涉及机密时代码审查变得更加困难。这是可以尝试使用 GitLab
进行审查,首先检查.gitsecret/paths/mapping.cfg文件的差异,在 UI 中查看哪些文件已更改。

此外,可以根据以下步骤来查看:

  • 检查main分支。

  • 通过git secret reveal -f解密文件

  • 查看feature-branch.

  • 运行git secret changes来查看main的解密文件和feature-branch中加密文件之间的差异。

当多个团队成员需要同时修改不同分支上的机密文件时,情况会更加复杂一些,因为Git 无法智能处理增量更新。

本地 git-secret 和 gpg 设置

当团队的所有人员将git-secret安装在本地,并且使用他们自己的gpg密钥,这也意味着团队的成本会随之增加,原因如下:

  • 新加入开发团队的人员需要:

    • 本地安装git-secret(*)

    • 在本地安装和设置gpg(*)

    • 创建gpg密钥对

  • 必须由所有其他团队成员 (*) 添加公钥。

  • 必须通过添加密钥的用户git secret tell

  • 机密需要重新加密。

对于离开团队的人员:

  • 所有其他团队成员(*) 都需要删除公钥。

  • 通过git secret killperson删除密钥的用户。

  • 机密需要重新加密。

另外,需要确保git-secretgpg版本保持最新,避免遇到任何兼容性问题。作为替代方案,也可以通过 Docker 处理,而上述步骤中标注(*)
则可以省去,也就是不需要设置本地的git-secretgpg

为了更加便捷,将存储库中每个开发人员的公共gpg密钥放在.dev/gpg-keys/,而私钥命名为secret.gpg并放在代码库的根目录中。

在此设置中,secret.gpg还必须被添加到.gitignore文件中。

# File: .gitignore
#...
vendor/
secret.gpg

然后可以使用make目标简化导入:

# gpg

DEFAULT_SECRET_GPG_KEY?=secret.gpg
DEFAULT_PUBLIC_GPG_KEYS?=.dev/gpg-keys/*

.PHONY: gpg
gpg: ## Run gpg commands. Specify the command e.g. via ARGS="--list-keys"
    $(EXECUTE_IN_APPLICATION_CONTAINER) gpg $(ARGS)

.PHONY: gpg-import
gpg-import: ## Import a gpg key file e.g. via GPG_KEY_FILES="/path/to/file /path/to/file2"
    @$(if $(GPG_KEY_FILES),,$(error GPG_KEY_FILES is undefined))
    "$(MAKE)" -s gpg ARGS="--import --batch --yes --pinentry-mode loopback $(GPG_KEY_FILES)"

.PHONY: gpg-import-default-secret-key
gpg-import-default-secret-key: ## Import the default secret key
    "$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_SECRET_GPG_KEY)"

.PHONY: gpg-import-default-public-keys
gpg-import-default-public-keys: ## Import the default public keys
    "$(MAKE)" -s gpg-import GPG_KEY_FILES="$(DEFAULT_PUBLIC_GPG_KEYS)" 

.PHONY: gpg-init
gpg-init: gpg-import-default-secret-key gpg-import-default-public-keys ## Initialize gpg in the container, i.e. import all public and private keys

上述操作需要在容器启动后运行一次。

场景

先假设以下这些条件:

  • 已检查过 Git 存储库。git checkout part-6-git-secret-encrypt-repository-docker

  • 没有正在运行的 Docker 容器。make docker-down

  • 已删除现有git-secret文件夹、中的密钥.dev/gpg-keyssecret.gpg密钥和passwords.*文件。rm -rf .gitsecret/ .dev/gpg-keys/* secret.gpg passwords.*

gpg 密钥的初始设置

不幸的是,我没有找到通过makedocker创建和导出gpg密钥的方法。你需要交互式地运行这些命令,或者传递一个带换行的字符串给它。这两件事在makedocker中都复杂得可怕。因此,你需要登录到应用程序的容器中,并在那里直接运行这些命令。这不是很简单,但无论如何,这只需要在一个新的开发人员入职时做一次。

密钥导出到secret.gpg,公钥导出到gp.dev/gpg-keys/alice-public.gpg

# start the docker setup
make docker-up

# log into the container ('winpty' is only required on Windows)
winpty docker exec -ti dofroscra_local-application-1 bash

# export key pair
name="Alice Doe"
email="alice@example.com"
gpg --batch --gen-key < .dev/gpg-keys/alice-public.gpg



$ make docker-up
ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml up -d
Container dofroscra_local-application-1  Created
...
Container dofroscra_local-application-1  Started
$ docker ps
CONTAINER ID   IMAGE                                COMMAND                  CREATED          STATUS          PORTS                NAMES
...
95f740607586   dofroscra/application-local:latest   "/usr/sbin/sshd -D"      21 minutes ago   Up 21 minutes   0.0.0.0:2222->22/tcp dofroscra_local-application-1

$ winpty docker exec -ti dofroscra_local-application-1 bash
root:/var/www/app# name="Alice Doe"
root:/var/www/app# email="alice@example.com"
gpg --batch --gen-key < Key-Type: 1
> Key-Length: 2048
> Subkey-Type: 1
> Subkey-Length: 2048
> Name-Real: $name
> Name-Email: $email
> Expire-Date: 0
> %no-protection
> EOF
gpg: directory '/root/.gnupg' created
gpg: keybox '/root/.gnupg/pubring.kbx' created
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key BBBE654440E720C1 marked as ultimately trusted
gpg: directory '/root/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/root/.gnupg/openpgp-revocs.d/225C736E0E70AC222C072B70BBBE654440E720C1.rev'

root:/var/www/app# gpg --output secret.gpg --armor --export-secret-key $email
root:/var/www/app# head secret.gpg
-----BEGIN PGP PRIVATE KEY BLOCK-----

lQOYBGJD+bwBCADBGKySV5PINc5MmQB3PNvCG7Oa1VMBO8XJdivIOSw7ykv55PRP
3g3R+ERd1Ss5gd5KAxLc1tt6PHGSPTypUJjCng2plwD8Jy5A/cC6o2x8yubOslLa
x1EC9fpcxUYUNXZavtEr+ylOaTaRz6qwSabsAgkg2NZ0ey/QKmFOZvhL8NlK9lTI
GgZPTiqPCsr7hiNg0WRbT5h8nTmfpl/DdTgwfPsDn5Hn0TEMa79WsrPnnq16jsq0
Uusuw3tOmdSdYnT8j7m1cpgcSj0hRF1eh4GVE0o62GqeLTWW9mfpcuv7n6mWaCB8
DCH6H238gwUriq/aboegcuBktlvSY21q/MIXABEBAAEAB/wK/M2buX+vavRgDRgR
hjUrsJTXO3VGLYcIetYXRhLmHLxBriKtcBa8OxLKKL5AFEuNourOBdcmTPiEwuxH
5s39IQOTrK6B1UmUqXvFLasXghorv8o8KGRL4ABM4Bgn6o+KBAVLVIwvVIhQ4rlf

root:/var/www/app# gpg --armor --export $email > .dev/gpg-keys/alice-public.gpg
root:/var/www/app# head .dev/gpg-keys/alice-public.gpg
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQENBGJD+bwBCADBGKySV5PINc5MmQB3PNvCG7Oa1VMBO8XJdivIOSw7ykv55PRP
3g3R+ERd1Ss5gd5KAxLc1tt6PHGSPTypUJjCng2plwD8Jy5A/cC6o2x8yubOslLa
x1EC9fpcxUYUNXZavtEr+ylOaTaRz6qwSabsAgkg2NZ0ey/QKmFOZvhL8NlK9lTI
GgZPTiqPCsr7hiNg0WRbT5h8nTmfpl/DdTgwfPsDn5Hn0TEMa79WsrPnnq16jsq0
Uusuw3tOmdSdYnT8j7m1cpgcSj0hRF1eh4GVE0o62GqeLTWW9mfpcuv7n6mWaCB8
DCH6H238gwUriq/aboegcuBktlvSY21q/MIXABEBAAG0HUFsaWNlIERvZSA8YWxp
Y2VAZXhhbXBsZS5jb20+iQFOBBMBCgA4FiEEIlxzbg5wrCIsBytwu75lREDnIMEF
AmJD+bwCGy8FCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQu75lREDnIMEN4Af+

至此alice@example.com就有了一个新机密和私钥,将其导出到secret.gpg.dev/gpg-keys/alice- public.gpg。剩下的命令现在可以直接在application容器外的主机上运行。

git-secret 的初始设置

现在来将git-secret引入一个新的代码库,然后运行以下命令。

初始化git-secret

make secret-init$ make secret-init
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="init";
git-secret: init created: '/var/www/app/.gitsecret/'

应用gpg对共享目录进行修复:

$ make secret-init-gpg-socket-config$ make secret-init-gpg-socket-config
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent
echo "socket=/tmp/S.gpg-agent" >> .gitsecret/keys/S.gpg-agent
echo "%Assuan%" > .gitsecret/keys/S.gpg-agent.ssh
echo "socket=/tmp/S.gpg-agent.ssh" >> .gitsecret/keys/S.gpg-agent.ssh
echo "extra-socket /tmp/S.gpg-agent.extra" > .gitsecret/keys/gpg-agent.conf
echo "browser-socket /tmp/S.gpg-agent.browser" >> .gitsecret/keys/gpg-agent.conf

容器启动后初始化 gpg

重启容器后,需要初始化gpg也就是导入公钥.dev/gpg-keys/*和导入私钥Secret.gpg,不然就无法对文件进行加密和解密。

make gpg-init$ make gpg-init
"C:/Program Files/Git/mingw64/bin/make" -s gpg-import GPG_KEY_FILES="secret.gpg"
gpg: directory '/home/application/.gnupg' created
gpg: keybox '/home/application/.gnupg/pubring.kbx' created
gpg: /home/application/.gnupg/trustdb.gpg: trustdb created
gpg: key BBBE654440E720C1: public key "Alice Doe <alice@example.com>" imported
gpg: key BBBE654440E720C1: secret key imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1
"C:/Program Files/Git/mingw64/bin/make" -s gpg-import GPG_KEY_FILES=".dev/gpg-keys/*"
gpg: key BBBE654440E720C1: "Alice Doe <alice@example.com>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1

添加新成员

接下来一起看看如何将新成员加入到git-secret

make secret-add-user EMAIL="alice@example.com"$ make secret-add-user EMAIL="alice@example.com"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="tell alice@example.com"
git-secret: done. alice@example.com added as user(s) who know the secret.

验证是否通过:

make secret-show-users$ make secret-show-users
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="whoknows"
alice@example.com

添加和加密文件

来添加一个新的加密文件secret_password.txt,创建以下文件:

echo "my_new_secret_password" > secret_password.txt

将其添加到.gitignore

echo "secret_password.txt" >> .gitignore

将其添加到git-secret

make secret-add FILE="secret_password.txt"$ make secret-add FILE="secret_password.txt"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="add secret_password.txt"
git-secret: 1 item(s) added.

加密所有文件:

make secret-encrypt$ make secret-encrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="hide"
git-secret: done. 1 of 1 files are hidden.$ ls secret_password.txt.secret
secret_password.txt.secret

解密文件

首先移除secret_password.txt文件,请运行:

rm secret_password.txt$ rm secret_password.txt$ ls secret_password.txt
ls: cannot access 'secret_password.txt': No such file or directory

然后进行解密:

make secret-decrypt$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: done. 1 of 1 files are revealed.$ cat secret_password.txt
my_new_secret_password

注意:如果gpg密钥受密码保护(假设密码是123456),请运行以下命令:

make secret-decrypt-with-password GPG_PASSWORD=123456

此外,还可以将GPG_PASSWORD变量加入.make/.env文件作为本地默认值,这样就不用每次都指定该值,然后可以简单地运行以下命令而不传递GPG_PASSWORD

make secret-decrypt-with-password

删除文件

可以通过以下方式解密文件:移除之前添加的secret-password.txt

make secret-remove FILE="secret_password.txt"$ make secret-remove FILE="secret_password.txt"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="remove secret_password.txt"
git-secret: removed from index.
git-secret: ensure that files: [secret_password.txt] are now not ignored.

注意:这里既不会自动删除secret_password.txt文件,也不会自动删除secret_password.txt.secret文件

$ ls -l | grep secret_password.txt 
-rw-r--r-- 1 Pascal 197121 3 月 31 日 19 日 14:03 secret_password.txt 
-rw-r--r-- 1 Pascal 197121 358 3 月 31 日 14:02 secret_password.txt.secret

即使加密的 secret_password.txt 文件仍然存在,也不会被解密:

$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: done. 0 of 0 files are revealed.

移除团队成员

移除团队成员需要通过以下步骤:

make secret-remove-user EMAIL="alice@example.com"$ make secret-remove-user EMAIL="alice@example.com"
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="killperson alice@example.com"
git-secret: removed keys.
git-secret: now [alice@example.com] do not have an access to the repository.
git-secret: make sure to hide the existing secrets again.

如果团队中还有其他成员留下,需要确保再次加密机密文件:

make secret-encrypt

如果该组已移除全部成员,git-secret就会报错:

$ make secret-decrypt
"C:/Program Files/Git/mingw64/bin/make" -s git-secret ARGS="reveal -f"
git-secret: abort: no public keys for users found. run 'git secret tell email@address'.
make[1]: *** [.make/01-00-application-setup.mk:57: git-secret] Error 1
make: *** [.make/01-00-application-setup.mk:69: secret-decrypt] Error 2

恭喜你~现在你可以加密和解密机密文件,并存储在 Git 存储库中啦!

最后

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,有需要的小伙伴,可以【扫下方二维码】免费领取:

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

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

相关文章

C语言基础复习

目录 数组 一维数组 完全初始化int a[5]{1,2,3,4,5}; 不完全初始化int a[5]{1,2} 完全不初始化”&#xff0c;int a[5] 二维数组 完全初始化 不完全初始化 指针 变量的访问方式&#xff1a; 指针变量的定义&#xff1a; 指针变量的赋值&#xff1a; 指针变量的运算…

Spring-Security入门

简介 Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 ​ 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多&#xff0c;因为相比与Spring…

ChatGPT - InstructGPT 论文简读

发表于NLP会议:NeurlPS,EMNLP EMNLP: Empirical Methods in Natural Language Processing,自然语言处理中的经验方法NeurlPS: Neural Information Processing Systems,神经信息处理系统ChatGPT: Optimizing Language Models for Dialogue,优化对话的语言模型 ChatGPT:htt…

一文了解编程领域的模版

文章目录模版含义代码模版泛型模版引擎小结&#x1f34a;在编程领域&#xff0c;模板是一种代码片段&#xff0c;它可以被重复使用&#xff0c;并允许您在保持代码的基本结构不变的情况下&#xff0c;根据需要调整其中的内容。模板通常在构建大型程序或开发一类相关程序时非常有…

Arthas的学习与使用

一、简介 Arthas 是一款线上监控诊断产品&#xff0c;通过全局视角实时查看应用 load、内存、gc、线程的状态信息&#xff0c;并能在不修改应用代码的情况下&#xff0c;对业务问题进行诊断&#xff0c;包括查看方法调用的出入参、异常&#xff0c;监测方法执行耗时&#xff0c…

Maven知识点-反应堆

前言 在一个多模块的Maven项目中&#xff0c;反应堆&#xff08;Reactor&#xff09;是指所有模块组成的一个构建结构。对于单模块的项目&#xff0c;反应堆就是该模块本身&#xff1b;但是对于多模块项目来说&#xff0c;反应堆就包含了各模块之间继承和依赖的关系&#xff0…

一篇带你MySQL入门

文章目录1. MySQL概述1.1 数据库相关概念1.2 MySQL数据库1.2.1 版本1.2.2 下载1.2.3 数据模型2. SQL2.1 SQL通用语法2.2 SQL分类2.3 DDL2.3.1 数据库操作2.3.2 表操作2.4 图形化界面工具2.4.1 安装2.4.2 使用2.5 DML2.5.1 添加数据2.5.2 修改数据2.5.3 删除数据2.6 DQL2.6.1 基…

每天一道大厂SQL题【Day04】大数据排序统计

每天一道大厂SQL题【Day04】大数据排序统计 大家好&#xff0c;我是Maynor。相信大家和我一样&#xff0c;都有一个大厂梦&#xff0c;作为一名资深大数据选手&#xff0c;深知SQL重要性&#xff0c;接下来我准备用100天时间&#xff0c;基于大数据岗面试中的经典SQL题&#x…

酒店管理|基于Springboot+Vue前后端分离实现酒店管理系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

webpack5从入门到精通

前言 webpack是什么&#xff1f; 摘自官网的一段话&#xff1a;webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时&#xff0c;它会在内部从一个或多个入口点构建一个 依赖图(dependency graph)&#xff0c;然后将你项目中所需的每…

[oeasy]python0072_修改字体前景颜色_foreground_color_font

修改颜色 回忆上次内容 m 可以改变字体样式 0-9 之间设置的都是字体效果0 重置为默认1 变亮2 变暗3 斜体4 下划线5 慢闪6 快闪7 前景背景互换8 隐藏9 中划线 叠加效果 \33[1;3moeasy;分割 取消效果 21 取消 122 取消 223 取消 3一直到 290 是全部取消&#xff0c;回到默认 最…

静态链接库与动态链接库

静态链接库与动态链接库的区别 静态链接库&#xff1a;在项目中引用了库函数&#xff0c;编译时链接器会将引用的函数代码或变量&#xff0c;链接到可执行文件里&#xff0c;和可执行程序组装在一起 动态链接库&#xff1a;在编译阶段不参与链接&#xff0c;不会和可执行文件…

【Unity】流式播放远端音频:WAV格式音频篇(一)

先了解一下wav的格式&#xff1a; 参考1&#xff1a;【音频】WAV 格式详解_tyustli的博客-CSDN博客_wav文件格式详解wav 文件支持多种不同的比特率、采样率、多声道音频。WAV 文件格式是 Microsoft 的 RIFF 规范的一个子集&#xff0c;用于存储多媒体文件。RIFF&#xff08;res…

git-secret:在 Git 存储库中加密和存储密钥(上)

目前市面上已经存在许多较为成熟的密钥管理产品&#xff0c;比如 HashiCorp Vault&#xff0c;AWS Secrets Manager 以及 GCP Secret Manager。由于这些产品需要集成和维护等服务&#xff0c;因此在项目中引入会增加一定成本和开销。阅读本文&#xff0c;将带你了解如何在 Dock…

numpy数值差分

文章目录diffediff1ddiff diff是numpy中用于求差分的函数&#xff0c;函数定义为 diff(a, n1, axis-1, prepend<no value>, append<no value>)其中a为数组&#xff0c;n为差分的阶数&#xff0c;axis为求导对应的坐标轴&#xff0c;默认-1表示最后一个轴。 例如…

提分必练,中创教育PMP全真模拟题分享

湖南中创教育每日五题分享来啦&#xff0c;“日日行&#xff0c;不怕千万里&#xff1b;常常做&#xff0c;不怕千万事。”&#xff0c;每日五题我们练起来&#xff01; 1、一个建筑项目所在的地区即将进入台风季节&#xff0c;恶劣的天气会严重影响项目的进度。高层管理者要求…

Java poi之word文本替换

目录结构前言文档准备引入Maven依赖代码块替换结果验证孤勇者替换结果对比青鸟替换结果对比前言 应公司需求&#xff0c;需实现以下功能 word文本内容的替换&#xff1b;word文本内容的提取&#xff1b;word文档中图片的提取存放 此文章将使用Apache POI实现Word文档中文本内…

【C++】揭开“引用”的庐山真面目

目录 一、引用的概念 二、引用的应用 1.特性 2.使用场景 2.1 引用作为函数参数 2.2 引用作为函数返回值 三、引用的权限问题 四、引用和指针的区别 一、引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟…

【数据结构之二叉树简介·顺序存储·应用:堆·堆排序·TOPK问题】

​ &#x1f57a;作者&#xff1a; 迷茫的启明星 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f383;相关文章 【数据结构从0到1之树的初识】 &#x1f3c7;家人们&#xff0c;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64…

Kotlin SharedFlowStateFlow 热流到底有多热?

前言 协程系列文章&#xff1a; 一个小故事讲明白进程、线程、Kotlin 协程到底啥关系&#xff1f;少年&#xff0c;你可知 Kotlin 协程最初的样子&#xff1f;讲真&#xff0c;Kotlin 协程的挂起/恢复没那么神秘(故事篇)讲真&#xff0c;Kotlin 协程的挂起/恢复没那么神秘(原理…