DeOldify API安全接入指南:Nginx反向代理+Token鉴权配置方案
DeOldify API安全接入指南:Nginx反向代理+Token鉴权配置方案
1. 为什么需要API安全防护?
如果你已经部署了DeOldify图像上色服务,可能会发现一个问题:你的API接口是直接暴露在公网上的。这意味着任何人都可以访问你的服务,不仅可能导致:
- 服务被滥用:大量请求消耗你的计算资源
- 数据泄露风险:上传的图片可能包含敏感信息
- 费用失控:如果按使用量计费,可能产生意外费用
- 服务不稳定:恶意请求可能导致服务崩溃
想象一下,你开了一家自助餐厅,所有人都可以免费进来吃饭,而且没有人数限制。很快,餐厅就会被挤爆,食材被消耗殆尽,正常顾客反而吃不上饭。API服务也是同样的道理。
今天,我就来分享一套完整的API安全接入方案,用Nginx反向代理加上Token鉴权,给你的DeOldify服务加上一道安全门。
2. 整体架构设计
在开始配置之前,我们先看看整个安全架构是什么样的:
┌─────────────────────────────────────────────────────────────┐
│ 外部用户访问 │
│ │
│ https://api.yourdomain.com/colorize?token=your_token │
│ │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Nginx反向代理服务器 │
│ │
│ 1. 验证Token有效性 │
│ 2. 限制请求频率 │
│ 3. 记录访问日志 │
│ 4. 转发合法请求到后端服务 │
│ │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ DeOldify服务 (7860端口) │
│ │
│ 只接收来自Nginx的请求 │
│ 不直接暴露给公网 │
│ │
└─────────────────────────────────────────────────────────────┘
这个架构有几个关键好处:
- 安全性:API不再直接暴露,所有请求都经过Nginx过滤
- 可控性:可以精确控制谁可以访问、访问频率是多少
- 可扩展性:未来可以轻松添加更多安全策略
- 监控性:所有访问都有日志记录,便于分析和审计
3. Nginx反向代理配置
3.1 安装Nginx
如果你还没有安装Nginx,先来安装一下:
# Ubuntu/Debian系统
sudo apt update
sudo apt install nginx -y
# CentOS/RHEL系统
sudo yum install epel-release -y
sudo yum install nginx -y
# 启动Nginx
sudo systemctl start nginx
sudo systemctl enable nginx
# 检查状态
sudo systemctl status nginx
3.2 基础反向代理配置
我们先创建一个基础的Nginx配置文件,把请求转发到DeOldify服务:
# 创建配置文件
sudo nano /etc/nginx/sites-available/deoldify-api
把下面的配置内容复制进去:
server {
listen 80;
server_name api.yourdomain.com; # 改成你的域名或IP
# 访问日志
access_log /var/log/nginx/deoldify_access.log;
error_log /var/log/nginx/deoldify_error.log;
# 限制请求体大小(50MB)
client_max_body_size 50M;
# 超时设置
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# 健康检查接口(公开访问)
location /health {
proxy_pass http://localhost:7860/health;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 添加CORS头
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
}
# 图片上色接口(需要Token验证)
location /colorize {
# 这里先配置转发,后面会加上Token验证
proxy_pass http://localhost:7860/colorize;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 添加CORS头
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
}
# URL上色接口(需要Token验证)
location /colorize_url {
proxy_pass http://localhost:7860/colorize_url;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 添加CORS头
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
}
# 默认返回404
location / {
return 404;
}
}
保存文件后,启用这个配置:
# 创建符号链接
sudo ln -s /etc/nginx/sites-available/deoldify-api /etc/nginx/sites-enabled/
# 测试配置是否正确
sudo nginx -t
# 重新加载Nginx配置
sudo systemctl reload nginx
现在,你的DeOldify服务已经通过Nginx代理了。可以通过以下方式访问:
# 健康检查(公开)
curl http://api.yourdomain.com/health
# 图片上色(目前还不需要Token)
curl -X POST http://api.yourdomain.com/colorize \
-F "image=@/path/to/image.jpg"
4. Token鉴权系统实现
4.1 为什么选择Token鉴权?
Token鉴权有几个明显的优势:
- 简单易用:客户端只需要在请求中带上Token
- 无状态:服务器不需要保存会话信息
- 灵活控制:可以为不同用户生成不同Token,设置不同权限
- 易于撤销:只需要删除或禁用Token即可
4.2 创建Token管理脚本
我们来创建一个简单的Token管理系统:
# 创建Token管理目录
sudo mkdir -p /etc/deoldify-auth
sudo nano /etc/deoldify-auth/token_manager.sh
把下面的脚本内容复制进去:
#!/bin/bash
# Token管理脚本
# 用法:
# 生成Token: ./token_manager.sh generate [用户名] [有效期天数]
# 验证Token: ./token_manager.sh verify [Token]
# 列出Token: ./token_manager.sh list
# 禁用Token: ./token_manager.sh disable [Token]
# 启用Token: ./token_manager.sh enable [Token]
TOKEN_DB="/etc/deoldify-auth/tokens.db"
LOG_FILE="/var/log/deoldify-auth.log"
# 初始化数据库
init_db() {
if [ ! -f "$TOKEN_DB" ]; then
echo "[]" > "$TOKEN_DB"
chmod 600 "$TOKEN_DB"
log "初始化Token数据库"
fi
}
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
# 生成随机Token
generate_token() {
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1
}
# 生成新Token
generate() {
local username="$1"
local expire_days="${2:-30}"
if [ -z "$username" ]; then
echo "错误: 需要提供用户名"
echo "用法: $0 generate [用户名] [有效期天数]"
exit 1
fi
init_db
local token=$(generate_token)
local created=$(date +%s)
local expire_seconds=$((expire_days * 24 * 60 * 60))
local expire=$((created + expire_seconds))
# 读取现有Token
local tokens=$(cat "$TOKEN_DB")
# 添加新Token
local new_token=$(jq -n \
--arg token "$token" \
--arg username "$username" \
--arg created "$created" \
--arg expire "$expire" \
--arg enabled "true" \
'{
token: $token,
username: $username,
created: $created|tonumber,
expire: $expire|tonumber,
enabled: $enabled,
last_used: null,
usage_count: 0
}')
# 更新数据库
echo "$tokens" | jq ". += [$new_token]" > "$TOKEN_DB".tmp && mv "$TOKEN_DB".tmp "$TOKEN_DB"
echo "Token生成成功:"
echo "================"
echo "用户名: $username"
echo "Token: $token"
echo "有效期: $expire_days 天"
echo "创建时间: $(date -d @$created)"
echo "过期时间: $(date -d @$expire)"
echo "================"
echo "重要: 请妥善保存此Token,它只会显示一次!"
log "为用户 $username 生成Token,有效期 $expire_days 天"
}
# 验证Token
verify() {
local token="$1"
if [ -z "$token" ]; then
echo "错误: 需要提供Token"
echo "用法: $0 verify [Token]"
exit 1
fi
init_db
local tokens=$(cat "$TOKEN_DB")
local now=$(date +%s)
# 查找Token
local token_info=$(echo "$tokens" | jq -r ".[] | select(.token == \"$token\")")
if [ -z "$token_info" ]; then
echo "验证失败: Token不存在"
return 1
fi
local enabled=$(echo "$token_info" | jq -r '.enabled')
local expire=$(echo "$token_info" | jq -r '.expire')
local username=$(echo "$token_info" | jq -r '.username')
if [ "$enabled" != "true" ]; then
echo "验证失败: Token已被禁用"
return 1
fi
if [ "$now" -gt "$expire" ]; then
echo "验证失败: Token已过期"
return 1
fi
# 更新使用信息
local usage_count=$(echo "$token_info" | jq -r '.usage_count + 1')
local updated_tokens=$(echo "$tokens" | jq \
"map(if .token == \"$token\" then .last_used = $now | .usage_count = $usage_count else . end)")
echo "$updated_tokens" > "$TOKEN_DB".tmp && mv "$TOKEN_DB".tmp "$TOKEN_DB"
echo "验证成功:"
echo "用户名: $username"
echo "创建时间: $(date -d @$(echo "$token_info" | jq -r '.created'))"
echo "过期时间: $(date -d @$expire)"
echo "使用次数: $usage_count"
echo "最后使用: $(date)"
log "Token验证成功: $username"
return 0
}
# 列出所有Token
list() {
init_db
local tokens=$(cat "$TOKEN_DB")
local now=$(date +%s)
echo "Token列表:"
echo "================"
echo "$tokens" | jq -r '.[] |
"用户名: \(.username)
Token: \(.token)
状态: \(if .enabled == "true" then "启用" else "禁用" end)
创建时间: \(.created | tonumber | strftime("%Y-%m-%d %H:%M:%S"))
过期时间: \(.expire | tonumber | strftime("%Y-%m-%d %H:%M:%S"))
使用次数: \(.usage_count)
最后使用: \(if .last_used then (.last_used | tonumber | strftime("%Y-%m-%d %H:%M:%S")) else "从未使用" end)
---------------"'
}
# 禁用Token
disable() {
local token="$1"
if [ -z "$token" ]; then
echo "错误: 需要提供Token"
echo "用法: $0 disable [Token]"
exit 1
fi
init_db
local tokens=$(cat "$TOKEN_DB")
local updated_tokens=$(echo "$tokens" | jq \
"map(if .token == \"$token\" then .enabled = \"false\" else . end)")
echo "$updated_tokens" > "$TOKEN_DB".tmp && mv "$TOKEN_DB".tmp "$TOKEN_DB"
echo "Token已禁用"
log "禁用Token: $token"
}
# 启用Token
enable() {
local token="$1"
if [ -z "$token" ]; then
echo "错误: 需要提供Token"
echo "用法: $0 enable [Token]"
exit 1
fi
init_db
local tokens=$(cat "$TOKEN_DB")
local updated_tokens=$(echo "$tokens" | jq \
"map(if .token == \"$token\" then .enabled = \"true\" else . end)")
echo "$updated_tokens" > "$TOKEN_DB".tmp && mv "$TOKEN_DB".tmp "$TOKEN_DB"
echo "Token已启用"
log "启用Token: $token"
}
# 主函数
main() {
local action="$1"
case "$action" in
"generate")
generate "$2" "$3"
;;
"verify")
verify "$2"
;;
"list")
list
;;
"disable")
disable "$2"
;;
"enable")
enable "$2"
;;
*)
echo "用法: $0 {generate|verify|list|disable|enable}"
echo ""
echo "命令说明:"
echo " generate [用户名] [有效期天数] 生成新Token"
echo " verify [Token] 验证Token"
echo " list 列出所有Token"
echo " disable [Token] 禁用Token"
echo " enable [Token] 启用Token"
exit 1
;;
esac
}
# 安装依赖
if ! command -v jq &> /dev/null; then
echo "正在安装jq..."
if command -v apt &> /dev/null; then
sudo apt install -y jq
elif command -v yum &> /dev/null; then
sudo yum install -y jq
else
echo "错误: 无法安装jq,请手动安装"
exit 1
fi
fi
main "$@"
给脚本添加执行权限:
sudo chmod +x /etc/deoldify-auth/token_manager.sh
4.3 使用Token管理脚本
现在我们可以使用这个脚本来管理Token了:
# 生成一个新Token(有效期30天)
sudo /etc/deoldify-auth/token_manager.sh generate "user1" 30
# 输出示例:
# Token生成成功:
# ================
# 用户名: user1
# Token: aBcDeFgHiJkLmNoPqRsTuVwXyZ123456
# 有效期: 30 天
# 创建时间: 2024年 01月 15日 星期一 10:30:00 CST
# 过期时间: 2024年 02月 14日 星期一 10:30:00 CST
# ================
# 重要: 请妥善保存此Token,它只会显示一次!
# 验证Token
sudo /etc/deoldify-auth/token_manager.sh verify "aBcDeFgHiJkLmNoPqRsTuVwXyZ123456"
# 列出所有Token
sudo /etc/deoldify-auth/token_manager.sh list
# 禁用Token
sudo /etc/deoldify-auth/token_manager.sh disable "aBcDeFgHiJkLmNoPqRsTuVwXyZ123456"
# 启用Token
sudo /etc/deoldify-auth/token_manager.sh enable "aBcDeFgHiJkLmNoPqRsTuVwXyZ123456"
5. Nginx集成Token验证
5.1 创建Token验证脚本
我们需要创建一个Nginx可以调用的Token验证脚本:
sudo nano /etc/deoldify-auth/validate_token.py
把下面的Python脚本复制进去:
#!/usr/bin/env python3
"""
Token验证脚本 - 供Nginx auth_request模块调用
"""
import json
import sys
import os
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
import urllib.parse
TOKEN_DB = "/etc/deoldify-auth/tokens.db"
def load_tokens():
"""加载Token数据库"""
try:
with open(TOKEN_DB, 'r') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return []
def validate_token(token):
"""验证Token有效性"""
if not token:
return False, "未提供Token"
tokens = load_tokens()
now = int(time.time())
for t in tokens:
if t.get('token') == token:
# 检查是否启用
if t.get('enabled') != 'true':
return False, "Token已被禁用"
# 检查是否过期
expire_time = t.get('expire', 0)
if now > expire_time:
return False, "Token已过期"
# 更新使用信息
t['last_used'] = now
t['usage_count'] = t.get('usage_count', 0) + 1
# 保存更新
try:
with open(TOKEN_DB, 'w') as f:
json.dump(tokens, f, indent=2)
except:
pass # 即使保存失败也允许访问
return True, t.get('username', 'unknown')
return False, "无效的Token"
class TokenValidatorHandler(BaseHTTPRequestHandler):
"""HTTP请求处理器"""
def do_GET(self):
"""处理GET请求"""
# 解析查询参数
query = urllib.parse.urlparse(self.path).query
params = urllib.parse.parse_qs(query)
# 获取Token
token = params.get('token', [''])[0]
if not token:
# 尝试从Authorization头获取
auth_header = self.headers.get('Authorization', '')
if auth_header.startswith('Bearer '):
token = auth_header[7:]
# 验证Token
is_valid, message = validate_token(token)
if is_valid:
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.send_header('X-Username', message) # 传递用户名
self.end_headers()
response = {'valid': True, 'username': message}
else:
self.send_response(401)
self.send_header('Content-type', 'application/json')
self.end_headers()
response = {'valid': False, 'error': message}
self.wfile.write(json.dumps(response).encode())
def log_message(self, format, *args):
"""禁用默认日志"""
pass
def run_server(port=9090):
"""启动验证服务器"""
server_address = ('', port)
httpd = HTTPServer(server_address, TokenValidatorHandler)
print(f"Token验证服务器启动在端口 {port}")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\n服务器关闭")
httpd.server_close()
if __name__ == '__main__':
# 如果作为独立脚本运行,启动HTTP服务器
if len(sys.argv) > 1 and sys.argv[1] == 'validate':
# 命令行验证模式
token = sys.argv[2] if len(sys.argv) > 2 else ''
is_valid, message = validate_token(token)
if is_valid:
print(f"有效Token - 用户: {message}")
sys.exit(0)
else:
print(f"无效Token: {message}")
sys.exit(1)
else:
# 服务器模式
run_server()
给脚本添加执行权限:
sudo chmod +x /etc/deoldify-auth/validate_token.py
5.2 配置系统服务
创建一个systemd服务来运行Token验证服务器:
sudo nano /etc/systemd/system/deoldify-auth.service
添加以下内容:
[Unit]
Description=DeOldify Token Validation Service
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/etc/deoldify-auth
ExecStart=/usr/bin/python3 /etc/deoldify-auth/validate_token.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
启动服务:
# 重新加载systemd配置
sudo systemctl daemon-reload
# 启动服务
sudo systemctl start deoldify-auth
# 设置开机自启
sudo systemctl enable deoldify-auth
# 检查状态
sudo systemctl status deoldify-auth
5.3 更新Nginx配置集成Token验证
现在更新Nginx配置,集成Token验证:
sudo nano /etc/nginx/sites-available/deoldify-api
更新配置,在需要验证的location块中添加auth_request:
server {
listen 80;
server_name api.yourdomain.com;
access_log /var/log/nginx/deoldify_access.log;
error_log /var/log/nginx/deoldify_error.log;
client_max_body_size 50M;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# Token验证服务地址
auth_request /validate;
# Token验证接口(内部使用)
location = /validate {
internal;
proxy_pass http://localhost:9090;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
# 传递Token参数
if ($arg_token) {
proxy_set_header Authorization "Bearer $arg_token";
}
}
# 验证失败时的错误页面
error_page 401 = @error401;
location @error401 {
return 401 '{"error": "Unauthorized", "message": "无效或过期的Token"}';
add_header Content-Type application/json;
}
# 健康检查接口(公开访问)
location /health {
# 不需要Token验证
auth_request off;
proxy_pass http://localhost:7860/health;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
}
# 图片上色接口(需要Token验证)
location /colorize {
# 需要Token验证
auth_request on;
# 传递用户名到后端
auth_request_set $username $upstream_http_x_username;
proxy_set_header X-Username $username;
proxy_pass http://localhost:7860/colorize;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
}
# URL上色接口(需要Token验证)
location /colorize_url {
auth_request on;
auth_request_set $username $upstream_http_x_username;
proxy_set_header X-Username $username;
proxy_pass http://localhost:7860/colorize_url;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
}
# 默认返回404
location / {
return 404;
}
}
重新加载Nginx配置:
# 测试配置
sudo nginx -t
# 重新加载
sudo systemctl reload nginx
6. 添加限流和防护
6.1 配置请求限流
为了防止API被滥用,我们需要添加限流配置:
sudo nano /etc/nginx/conf.d/rate_limit.conf
添加以下限流配置:
# 定义限流区域
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=5r/s;
# 限制连接数
limit_conn_zone $binary_remote_addr zone=addr:10m;
然后更新DeOldify API配置:
sudo nano /etc/nginx/sites-available/deoldify-api
在server块中添加限流配置:
server {
listen 80;
server_name api.yourdomain.com;
# 全局连接限制
limit_conn addr 10;
# ... 其他配置保持不变 ...
# Token验证接口(更严格的限流)
location = /validate {
limit_req zone=auth_limit burst=5 nodelay;
limit_conn addr 5;
internal;
proxy_pass http://localhost:9090;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
if ($arg_token) {
proxy_set_header Authorization "Bearer $arg_token";
}
}
# API接口(标准限流)
location ~ ^/(colorize|colorize_url) {
limit_req zone=api_limit burst=20 nodelay;
limit_conn addr 5;
# ... 原有配置保持不变 ...
}
}
6.2 添加WAF防护
安装和配置ModSecurity来提供Web应用防火墙功能:
# 安装ModSecurity
sudo apt install libapache2-mod-security2 -y
# 下载OWASP核心规则集
sudo apt install git -y
cd /tmp
git clone https://github.com/coreruleset/coreruleset.git
sudo cp -r coreruleset /etc/modsecurity/
sudo cp /etc/modsecurity/coreruleset/crs-setup.conf.example /etc/modsecurity/coreruleset/crs-setup.conf
# 配置ModSecurity
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
sudo nano /etc/modsecurity/modsecurity.conf
修改以下配置:
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecDataDir /tmp/
安装Nginx的ModSecurity模块:
# 对于Ubuntu/Debian
sudo apt install libnginx-mod-http-modsecurity -y
# 配置Nginx使用ModSecurity
sudo nano /etc/nginx/modsecurity.conf
添加:
modsecurity on;
modsecurity_rules_file /etc/modsecurity/modsecurity.conf;
modsecurity_rules_file /etc/modsecurity/coreruleset/crs-setup.conf;
modsecurity_rules_file /etc/modsecurity/coreruleset/rules/*.conf;
在Nginx站点配置中启用:
server {
listen 80;
server_name api.yourdomain.com;
# 启用ModSecurity
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity.conf;
# ... 其他配置保持不变 ...
}
7. 客户端使用示例
7.1 Python客户端示例
更新之前的Python代码,添加Token支持:
import requests
import base64
from PIL import Image
from io import BytesIO
class DeOldifyClient:
"""DeOldify API客户端"""
def __init__(self, base_url, api_token):
"""
初始化客户端
Args:
base_url: API基础地址,如 "http://api.yourdomain.com"
api_token: API访问Token
"""
self.base_url = base_url.rstrip('/')
self.api_token = api_token
self.session = requests.Session()
# 设置默认headers
self.session.headers.update({
'Authorization': f'Bearer {api_token}'
})
def health_check(self):
"""健康检查"""
try:
response = self.session.get(f"{self.base_url}/health")
return response.json()
except Exception as e:
return {'error': str(e)}
def colorize_image(self, image_path, output_path=None):
"""
给图片上色
Args:
image_path: 图片文件路径
output_path: 输出文件路径,如果为None则自动生成
Returns:
上色后的图片路径
"""
try:
# 读取图片
with open(image_path, 'rb') as f:
files = {'image': f}
# 使用Token参数
params = {'token': self.api_token}
response = self.session.post(
f"{self.base_url}/colorize",
files=files,
params=params
)
# 检查响应
if response.status_code == 401:
raise Exception("Token无效或已过期")
elif response.status_code != 200:
raise Exception(f"API请求失败: {response.status_code}")
# 解析结果
result = response.json()
if result.get('success'):
# 解码base64图片数据
img_data = base64.b64decode(result['output_img_base64'])
img = Image.open(BytesIO(img_data))
# 保存图片
if output_path is None:
# 自动生成输出路径
import os
name, ext = os.path.splitext(image_path)
output_path = f"{name}_colored{ext}"
img.save(output_path)
print(f"✓ 上色完成: {output_path}")
return output_path
else:
raise Exception(f"上色失败: {result}")
except Exception as e:
print(f"✗ 错误: {e}")
return None
def colorize_from_url(self, image_url, output_path):
"""
从URL上色图片
Args:
image_url: 图片URL地址
output_path: 输出文件路径
Returns:
bool: 是否成功
"""
try:
# 准备请求数据
data = {"url": image_url}
# 使用Token参数
params = {'token': self.api_token}
response = self.session.post(
f"{self.base_url}/colorize_url",
json=data,
params=params
)
# 检查响应
if response.status_code == 401:
raise Exception("Token无效或已过期")
elif response.status_code != 200:
raise Exception(f"API请求失败: {response.status_code}")
# 解析结果
result = response.json()
if result.get('success'):
# 解码并保存
img_data = base64.b64decode(result['output_img_base64'])
img = Image.open(BytesIO(img_data))
img.save(output_path)
print(f"✓ 上色完成: {output_path}")
return True
else:
raise Exception(f"上色失败: {result}")
except Exception as e:
print(f"✗ 错误: {e}")
return False
def batch_colorize(self, input_folder, output_folder):
"""
批量处理文件夹中的图片
Args:
input_folder: 输入文件夹路径
output_folder: 输出文件夹路径
Returns:
dict: 处理结果统计
"""
import os
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 支持的图片格式
valid_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp']
results = {
'total': 0,
'success': 0,
'failed': 0,
'errors': []
}
# 遍历输入文件夹
for filename in os.listdir(input_folder):
# 检查文件扩展名
ext = os.path.splitext(filename)[1].lower()
if ext not in valid_extensions:
continue
input_path = os.path.join(input_folder, filename)
output_path = os.path.join(output_folder, f"colored_{filename}")
results['total'] += 1
print(f"正在处理 ({results['total']}): {filename}")
try:
success = self.colorize_image(input_path, output_path)
if success:
results['success'] += 1
else:
results['failed'] += 1
results['errors'].append(f"{filename}: 上色失败")
except Exception as e:
results['failed'] += 1
results['errors'].append(f"{filename}: {str(e)}")
# 打印统计信息
print(f"\n处理完成:")
print(f" 总计: {results['total']}")
print(f" 成功: {results['success']}")
print(f" 失败: {results['failed']}")
if results['errors']:
print(f"\n错误列表:")
for error in results['errors']:
print(f" - {error}")
return results
# 使用示例
if __name__ == "__main__":
# 配置信息
API_URL = "http://api.yourdomain.com" # 你的API地址
API_TOKEN = "your_token_here" # 你的Token
# 创建客户端
client = DeOldifyClient(API_URL, API_TOKEN)
# 测试连接
print("测试API连接...")
health = client.health_check()
print(f"服务状态: {health}")
# 单张图片上色
print("\n处理单张图片...")
result = client.colorize_image("old_photo.jpg", "old_photo_colored.jpg")
# 批量处理
print("\n批量处理图片...")
stats = client.batch_colorize("./input_photos", "./output_photos")
# 从URL处理
print("\n从URL处理图片...")
client.colorize_from_url(
"https://example.com/old_photo.jpg",
"downloaded_colored.jpg"
)
7.2 命令行使用示例
# 使用curl调用API(通过查询参数传递Token)
curl -X POST "http://api.yourdomain.com/colorize?token=your_token_here" \
-F "image=@/path/to/image.jpg"
# 或者通过Authorization头传递Token
curl -X POST http://api.yourdomain.com/colorize \
-H "Authorization: Bearer your_token_here" \
-F "image=@/path/to/image.jpg"
# 从URL上色
curl -X POST "http://api.yourdomain.com/colorize_url?token=your_token_here" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/image.jpg"}'
7.3 JavaScript/Node.js客户端示例
// Node.js客户端示例
const axios = require('axios');
const fs = require('fs');
const FormData = require('form-data');
class DeOldifyClient {
constructor(baseUrl, apiToken) {
this.baseUrl = baseUrl;
this.apiToken = apiToken;
this.client = axios.create({
baseURL: baseUrl,
headers: {
'Authorization': `Bearer ${apiToken}`
}
});
}
// 健康检查
async healthCheck() {
try {
const response = await this.client.get('/health');
return response.data;
} catch (error) {
return { error: error.message };
}
}
// 图片上色
async colorizeImage(imagePath, outputPath) {
try {
const formData = new FormData();
formData.append('image', fs.createReadStream(imagePath));
const response = await this.client.post('/colorize', formData, {
headers: formData.getHeaders(),
params: { token: this.apiToken }
});
if (response.data.success) {
// 解码base64图片
const imageBuffer = Buffer.from(response.data.output_img_base64, 'base64');
fs.writeFileSync(outputPath, imageBuffer);
console.log(`✓ 上色完成: ${outputPath}`);
return outputPath;
} else {
throw new Error('上色失败');
}
} catch (error) {
if (error.response && error.response.status === 401) {
throw new Error('Token无效或已过期');
}
throw error;
}
}
// 从URL上色
async colorizeFromUrl(imageUrl, outputPath) {
try {
const response = await this.client.post('/colorize_url', {
url: imageUrl
}, {
params: { token: this.apiToken }
});
if (response.data.success) {
const imageBuffer = Buffer.from(response.data.output_img_base64, 'base64');
fs.writeFileSync(outputPath, imageBuffer);
console.log(`✓ 上色完成: ${outputPath}`);
return true;
} else {
throw new Error('上色失败');
}
} catch (error) {
if (error.response && error.response.status === 401) {
throw new Error('Token无效或已过期');
}
throw error;
}
}
}
// 使用示例
async function main() {
const client = new DeOldifyClient(
'http://api.yourdomain.com',
'your_token_here'
);
// 测试连接
console.log('测试API连接...');
const health = await client.healthCheck();
console.log('服务状态:', health);
// 处理图片
console.log('\n处理图片...');
try {
await client.colorizeImage('old_photo.jpg', 'old_photo_colored.jpg');
} catch (error) {
console.error('错误:', error.message);
}
}
main();
8. 监控和日志分析
8.1 配置Nginx日志格式
为了更好地监控API使用情况,我们可以自定义Nginx日志格式:
sudo nano /etc/nginx/nginx.conf
在http块中添加:
http {
# ... 其他配置 ...
# 自定义日志格式
log_format deoldify_json '{"time": "$time_iso8601", '
'"remote_addr": "$remote_addr", '
'"remote_user": "$remote_user", '
'"request": "$request", '
'"status": "$status", '
'"body_bytes_sent": "$body_bytes_sent", '
'"request_time": "$request_time", '
'"http_referer": "$http_referer", '
'"http_user_agent": "$http_user_agent", '
'"http_x_forwarded_for": "$http_x_forwarded_for", '
'"token": "$arg_token", '
'"username": "$upstream_http_x_username"}';
# ... 其他配置 ...
}
更新站点配置使用JSON日志:
server {
listen 80;
server_name api.yourdomain.com;
# 使用JSON格式日志
access_log /var/log/nginx/deoldify_access.log deoldify_json;
error_log /var/log/nginx/deoldify_error.log;
# ... 其他配置 ...
}
8.2 创建监控脚本
创建一个监控脚本,定期检查服务状态和使用情况:
sudo nano /etc/deoldify-auth/monitor.sh
#!/bin/bash
# DeOldify API监控脚本
LOG_FILE="/var/log/deoldify-monitor.log"
API_URL="http://localhost:7860"
NGINX_ACCESS_LOG="/var/log/nginx/deoldify_access.log"
TOKEN_DB="/etc/deoldify-auth/tokens.db"
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
# 检查服务健康状态
check_health() {
local response=$(curl -s "$API_URL/health" 2>/dev/null || echo '{"status":"unreachable"}')
local status=$(echo "$response" | jq -r '.status' 2>/dev/null || echo "error")
if [ "$status" = "healthy" ]; then
echo "✓ 服务健康"
log "服务健康检查: 正常"
return 0
else
echo "✗ 服务异常: $response"
log "服务健康检查: 异常 - $response"
return 1
fi
}
# 分析访问日志
analyze_logs() {
local today=$(date +%Y-%m-%d)
local log_file="$NGINX_ACCESS_LOG"
if [ ! -f "$log_file" ]; then
echo "日志文件不存在: $log_file"
return
fi
# 统计今日请求
local today_requests=$(grep "$today" "$log_file" 2>/dev/null | wc -l)
local today_success=$(grep "$today" "$log_file" 2>/dev/null | grep '"status":"200"' | wc -l)
local today_errors=$(grep "$today" "$log_file" 2>/dev/null | grep -v '"status":"200"' | wc -l)
# 统计Token使用情况
if [ -f "$TOKEN_DB" ]; then
local total_tokens=$(jq 'length' "$TOKEN_DB" 2>/dev/null || echo "0")
local active_tokens=$(jq '[.[] | select(.enabled == "true")] | length' "$TOKEN_DB" 2>/dev/null || echo "0")
else
local total_tokens="0"
local active_tokens="0"
fi
echo "📊 今日统计 ($today):"
echo " 总请求数: $today_requests"
echo " 成功请求: $today_success"
echo " 失败请求: $today_errors"
echo " Token总数: $total_tokens"
echo " 活跃Token: $active_tokens"
log "日志分析: 请求数=$today_requests, 成功=$today_success, 失败=$today_errors"
}
# 检查系统资源
check_resources() {
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
local mem_usage=$(free -m | awk 'NR==2{printf "%.2f", $3*100/$2}')
local disk_usage=$(df -h / | awk 'NR==2{print $5}' | cut -d'%' -f1)
echo "🖥️ 系统资源:"
echo " CPU使用率: ${cpu_usage}%"
echo " 内存使用率: ${mem_usage}%"
echo " 磁盘使用率: ${disk_usage}%"
# 检查Nginx状态
local nginx_status=$(systemctl is-active nginx)
local auth_status=$(systemctl is-active deoldify-auth)
echo "🔧 服务状态:"
echo " Nginx: $nginx_status"
echo " Token验证服务: $auth_status"
log "资源检查: CPU=${cpu_usage}%, 内存=${mem_usage}%, 磁盘=${disk_usage}%"
}
# 生成使用报告
generate_report() {
local report_file="/tmp/deoldify-report-$(date +%Y%m%d).txt"
{
echo "DeOldify API 使用报告"
echo "生成时间: $(date)"
echo "========================================"
echo ""
echo "1. 服务健康状态:"
check_health
echo ""
echo "2. 访问统计:"
analyze_logs
echo ""
echo "3. 系统资源:"
check_resources
echo ""
echo "4. Token使用情况:"
if [ -f "$TOKEN_DB" ]; then
jq -r '.[] | "\(.username): 使用次数=\(.usage_count), 最后使用=\(if .last_used then (.last_used | tonumber | strftime("%Y-%m-%d %H:%M:%S")) else "从未使用" end), 状态=\(if .enabled == "true" then "启用" else "禁用" end)"' "$TOKEN_DB" 2>/dev/null || echo "无法读取Token数据库"
else
echo "Token数据库不存在"
fi
} > "$report_file"
echo "报告已生成: $report_file"
log "生成使用报告: $report_file"
}
# 发送警报(如果需要)
send_alert() {
local message="$1"
# 这里可以集成邮件、Slack、钉钉等通知方式
# 示例:发送到syslog
logger -t deoldify-monitor "$message"
echo "⚠️ 警报: $message"
log "发送警报: $message"
}
# 主函数
main() {
echo "开始监控检查..."
log "开始监控检查"
# 检查服务健康
if ! check_health; then
send_alert "DeOldify服务异常"
fi
# 检查系统资源
check_resources
# 分析日志
analyze_logs
# 生成报告(每天一次)
local current_hour=$(date +%H)
if [ "$current_hour" = "00" ]; then
generate_report
fi
echo "监控检查完成"
log "监控检查完成"
}
# 安装依赖
if ! command -v jq &> /dev/null; then
echo "正在安装jq..."
sudo apt install -y jq
fi
# 运行主函数
main
给脚本添加执行权限:
sudo chmod +x /etc/deoldify-auth/monitor.sh
8.3 设置定时监控
创建cron任务定期运行监控:
# 编辑crontab
sudo crontab -e
添加以下行:
# 每分钟检查一次服务健康
* * * * * /etc/deoldify-auth/monitor.sh >> /var/log/deoldify-cron.log 2>&1
# 每天凌晨1点生成详细报告
0 1 * * * /etc/deoldify-auth/monitor.sh --report >> /var/log/deoldify-daily.log 2>&1
# 每周一凌晨2点清理旧日志
0 2 * * 1 find /var/log/nginx/deoldify_* -mtime +30 -delete
9. 高级安全配置
9.1 配置HTTPS
为了更安全,我们应该启用HTTPS:
# 安装Certbot(Let's Encrypt)
sudo apt install certbot python3-certbot-nginx -y
# 获取SSL证书
sudo certbot --nginx -d api.yourdomain.com
# 自动续期测试
sudo certbot renew --dry-run
Nginx会自动更新配置,启用HTTPS。
9.2 配置防火墙
# 安装ufw(如果未安装)
sudo apt install ufw -y
# 启用防火墙
sudo ufw enable
# 允许SSH
sudo ufw allow ssh
# 允许HTTP和HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 拒绝其他所有入站连接
sudo ufw default deny incoming
# 允许所有出站连接
sudo ufw default allow outgoing
# 查看规则
sudo ufw status verbose
9.3 配置Fail2ban防止暴力破解
# 安装Fail2ban
sudo apt install fail2ban -y
# 创建DeOldify专用的jail配置
sudo nano /etc/fail2ban/jail.local
添加:
[deoldify-auth]
enabled = true
port = http,https
filter = deoldify-auth
logpath = /var/log/nginx/deoldify_access.log
maxretry = 5
bantime = 3600
findtime = 600
创建过滤器:
sudo nano /etc/fail2ban/filter.d/deoldify-auth.conf
添加:
[Definition]
failregex = ^.*"remote_addr":"<HOST>".*"status":"(401|403|429)".*$
ignoreregex =
重启Fail2ban:
sudo systemctl restart fail2ban
sudo systemctl enable fail2ban
10. 故障排除和优化
10.1 常见问题解决
问题1:Nginx返回502错误
# 检查Nginx错误日志
sudo tail -f /var/log/nginx/error.log
# 检查Token验证服务
sudo systemctl status deoldify-auth
sudo journalctl -u deoldify-auth -f
# 检查端口是否监听
sudo netstat -tlnp | grep :9090
问题2:Token验证失败
# 手动验证Token
sudo /etc/deoldify-auth/validate_token.py validate "your_token"
# 检查Token数据库
sudo cat /etc/deoldify-auth/tokens.db | jq .
# 检查Token验证服务日志
sudo tail -f /var/log/deoldify-auth.log
问题3:API响应慢
# 检查系统资源
top
free -h
df -h
# 检查Nginx连接数
sudo netstat -an | grep :80 | wc -l
# 优化Nginx配置
sudo nano /etc/nginx/nginx.conf
在nginx.conf的events块中添加:
events {
worker_connections 4096;
multi_accept on;
use epoll;
}
在http块中添加:
http {
# 启用gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/octet-stream;
# 优化缓冲区
client_body_buffer_size 10K;
client_header_buffer_size 1k;
client_max_body_size 50M;
large_client_header_buffers 4 8k;
# 优化超时
client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 15;
send_timeout 10;
}
10.2 性能优化建议
- 启用缓存:对于相同的图片,可以缓存处理结果
- 使用CDN:将处理后的图片缓存到CDN
- 负载均衡:如果流量大,可以考虑多实例部署
- 数据库优化:如果Token很多,考虑使用Redis代替JSON文件
- 监控告警:设置资源使用告警,及时扩容
11. 总结
通过本文的配置,我们为DeOldify图像上色API构建了一个完整的安全接入方案:
11.1 实现的安全特性
- Token鉴权:只有持有有效Token的用户才能访问API
- 请求限流:防止API被滥用,保护后端服务
- 反向代理:隐藏真实服务地址,增加安全性
- HTTPS加密:数据传输加密,防止中间人攻击
- WAF防护:防止常见Web攻击
- 访问日志:完整记录所有访问,便于审计
- 自动监控:实时监控服务状态和使用情况
11.2 部署步骤回顾
- 安装和配置Nginx反向代理
- 创建Token管理系统
- 集成Token验证到Nginx
- 配置限流和防护规则
- 设置监控和告警
- 启用HTTPS加密
- 配置防火墙和Fail2ban
11.3 使用建议
- 定期轮换Token:建议每30-90天更换一次Token
- 按需生成Token:为不同用户生成不同的Token
- 监控使用情况:定期检查API使用统计
- 及时更新:保持系统和软件更新到最新版本
- 备份配置:定期备份Nginx和Token配置
11.4 扩展可能性
这个方案还可以进一步扩展:
- 多级权限:为不同Token设置不同的访问权限
- 使用量限制:限制每个Token的每日使用次数
- Web管理界面:提供图形化的Token管理界面
- OAuth集成:支持第三方登录认证
- 审计日志:更详细的访问审计和报告
现在,你的DeOldify API已经具备了企业级的安全防护能力,可以放心地对外提供服务了。记得定期检查日志和监控,确保服务稳定运行。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐
所有评论(0)