本文档说明外部服务器如何安全地调用本项目的 API 接口(已配置 Basic Auth)。
⚠️ 重要:永远不要在代码中硬编码用户名和密码!
应该使用以下方式存储凭证:
.gitignore)Caddy 使用 bcrypt 哈希格式,htpasswd 文件格式为 username hash(空格分隔)。
# 步骤 1:生成密码哈希
docker run --rm caddy:2-alpine caddy hash-password --plaintext 'your_password'
# 输出示例:$2a$14$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 步骤 2:将用户名和哈希写入 htpasswd 文件(注意是空格分隔,不是冒号)
echo 'admin $2a$14$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' > htpasswd
# 或者一步完成:
HASH=$(docker run --rm caddy:2-alpine caddy hash-password --plaintext 'your_password')
echo "admin $HASH" > htpasswd
注意: Caddy 的格式是 username hash(空格分隔),不是 httpd 的 username:hash(冒号分隔)。
生成的 htpasswd 文件会通过 Caddyfile 的 import 导入(已通过 docker-compose 挂载到容器中)。
注意:htpasswd 文件已添加到 .gitignore,不会被提交到 Git。
// 从环境变量读取凭证
const username = process.env.BYREAL_API_USERNAME || ''
const password = process.env.BYREAL_API_PASSWORD || ''
// 生成 Basic Auth header
const credentials = Buffer.from(`${username}:${password}`).toString('base64')
const authHeader = `Basic ${credentials}`
// 发送请求(使用 HTTPS)
const response = await fetch('https://love.hdlife.me/api/my-lp', {
method: 'GET',
headers: {
Authorization: authHeader,
'Content-Type': 'application/json',
},
})
const data = await response.json()
import axios from 'axios'
const username = process.env.BYREAL_API_USERNAME || ''
const password = process.env.BYREAL_API_PASSWORD || ''
const response = await axios.get('https://love.hdlife.me/api/my-lp', {
auth: {
username,
password,
},
headers: {
'Content-Type': 'application/json',
},
})
const data = response.data
import ky from 'ky'
const username = process.env.BYREAL_API_USERNAME || ''
const password = process.env.BYREAL_API_PASSWORD || ''
const credentials = Buffer.from(`${username}:${password}`).toString('base64')
const response = await ky.get('https://love.hdlife.me/api/my-lp', {
headers: {
Authorization: `Basic ${credentials}`,
},
})
const data = await response.json()
import os
import requests
from requests.auth import HTTPBasicAuth
# 从环境变量读取
username = os.getenv('BYREAL_API_USERNAME', '')
password = os.getenv('BYREAL_API_PASSWORD', '')
# 方式 1: 使用 HTTPBasicAuth(推荐)
response = requests.get(
'https://love.hdlife.me/api/my-lp',
auth=HTTPBasicAuth(username, password),
headers={'Content-Type': 'application/json'}
)
data = response.json()
# 方式 2: 手动设置 header
import base64
credentials = base64.b64encode(f'{username}:{password}'.encode()).decode()
headers = {
'Authorization': f'Basic {credentials}',
'Content-Type': 'application/json'
}
response = requests.get('https://love.hdlife.me/api/my-lp', headers=headers)
# 从环境变量读取
curl -u "${BYREAL_API_USERNAME}:${BYREAL_API_PASSWORD}" \
https://love.hdlife.me/api/my-lp
# 或者直接指定(不推荐,密码会出现在命令历史中)
curl -u "admin:your_password" \
https://love.hdlife.me/api/my-lp
package main
import (
"encoding/base64"
"fmt"
"io"
"net/http"
"os"
)
func main() {
username := os.Getenv("BYREAL_API_USERNAME")
password := os.Getenv("BYREAL_API_PASSWORD")
credentials := base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
req, _ := http.NewRequest("GET", "https://love.hdlife.me/api/my-lp", nil)
req.Header.Set("Authorization", "Basic "+credentials)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.util.Base64;
public class ApiClient {
public static void main(String[] args) {
String username = System.getenv("BYREAL_API_USERNAME");
String password = System.getenv("BYREAL_API_PASSWORD");
String credentials = Base64.getEncoder().encodeToString(
(username + ":" + password).getBytes()
);
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://love.hdlife.me/api/my-lp"))
.header("Authorization", "Basic " + credentials)
.header("Content-Type", "application/json")
.GET()
.build();
try {
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
} catch (Exception e) {
e.printStackTrace();
}
}
}
如果外部服务器也在 Docker 中运行,在 docker-compose.yml 中配置:
services:
your-service:
image: your-image
environment:
- BYREAL_API_USERNAME=${BYREAL_API_USERNAME}
- BYREAL_API_PASSWORD=${BYREAL_API_PASSWORD}
# 或者从 .env 文件读取
env_file:
- .env
在服务器上执行:
# 生成密码哈希并写入 htpasswd 文件
HASH=$(docker run --rm caddy:2-alpine caddy hash-password --plaintext 'your_password')
echo "admin $HASH" > htpasswd
# 查看生成的文件
cat htpasswd
在服务器上创建 .env 文件:
# Solana 配置
SOL_ENDPOINT=your_solana_endpoint
SOL_SECRET_KEY=your_secret_key
docker compose down
docker compose up -d
访问 https://love.hdlife.me,应该会自动跳转到 HTTPS 并要求 Basic Auth。
.gitignore:确保 htpasswd 文件不会被提交使用 curl 测试 Basic Auth 和 HTTPS:
# 测试(会提示输入密码)
curl -u admin https://love.hdlife.me/api/my-lp
# 或者从环境变量读取
curl -u "${BYREAL_API_USERNAME}:${BYREAL_API_PASSWORD}" \
https://love.hdlife.me/api/my-lp
如果返回 401 Unauthorized,说明凭证错误;如果返回 200 或其他业务状态码,说明认证成功。
A: 联系项目管理员获取用户名和密码。密码存储在服务器的 htpasswd 文件中。
A: 在服务器上重新生成 htpasswd 文件:
# 生成新密码哈希并写入 htpasswd 文件
HASH=$(docker run --rm caddy:2-alpine caddy hash-password --plaintext 'new_password')
echo "admin $HASH" > htpasswd
# 重启服务
docker compose restart caddy
A:
# 查看 Caddy 容器日志
docker logs byreal-caddy
# 查看访问日志(如果在 Caddyfile 中配置了日志文件)
docker exec byreal-caddy cat /var/log/caddy/access.log
A: Caddy 会自动管理 Let's Encrypt 证书的续期,无需手动操作。
Caddyfile - Caddy HTTPS + Basic Auth 配置htpasswd - Basic Auth 密码文件(不提交到 Git)docker-compose.yml - Docker 服务配置.env - 环境变量配置(不提交到 Git)