什么是无感登录
用户登录博客(飞书或密码)后,点击 OpenClaw 入口直接进入,无需二次输入密码。原理是 Cookie 桥接:
Django 写 cookie → Nginx 读 cookie 转 HTTP Header → Gateway 信任 Header → 认证通过
架构图
浏览器 │ Cookie: openclaw_user=kivenz │ ▼ Nginx (:443) │ 读 $cookie_openclaw_user 注入 X-Forwarded-User: kivenz │ ▼ OpenClaw Gateway (:18789) trusted-proxy 模式 信任 127.0.0.1 / 172.18.0.1 │ ▼ 认证为用户 kivenz
三层配置
第一层:Django(写 Cookie)
console/views.py — ConsoleIndexView 在用户访问控制台首页时写入 cookie:
response.set_cookie( "openclaw_user", request.user.username, httponly=True, # JS 不可读,防 XSS samesite="Strict", # 仅同站请求携带,防 CSRF max_age=86400, # 24 小时有效 )
settings.py — 环境变量:
OPENCLAW_GATEWAY_URL = os.environ["OPENCLAW_GATEWAY_URL"] # Gateway 外部地址
OPENCLAW_TOKEN = os.environ["OPENCLAW_TOKEN"] # Gateway 管理员 Token
OPENCLAW_INTERNAL_URL = os.environ.get("OPENCLAW_INTERNAL_URL", "http://127.0.0.1:18789")
.env 中配置:
OPENCLAW_GATEWAY_URL=https://openclaw-gateway.example.com/login OPENCLAW_TOKEN=your-token-here OPENCLAW_INTERNAL_URL=http://127.0.0.1:18789 # 可选,默认就是这个
console/views.py — OpenClawProxyView 开发环境下作为兜底代理:
headers["X-Forwarded-User"] = request.user.username # 转发请求到 Gateway resp = requests.request(method=request.method, url=target, headers=headers, ...)
第二层:Nginx(Cookie → Header)
deploy/nginx_openclaw.conf:
location /console/openclaw {
proxy_pass http://127.0.0.1:18789;
# 核心:把 cookie 值注入到 HTTP 头
proxy_set_header X-Forwarded-User $cookie_openclaw_user;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400s;
proxy_buffering off;
client_max_body_size 100m;
}
关键行:proxy_set_header X-Forwarded-User $cookie_openclaw_user;
$cookie_openclaw_user是 Nginx 内置变量,自动读取请求中名为openclaw_user的 cookie 值proxy_set_header X-Forwarded-User将该值作为 HTTP 头注入到转发请求中
部署到宝塔:把这个文件复制到 /www/server/panel/vhost/nginx/extension/kivenz_blog/openclaw.conf,然后在站点配置中 include。
第三层:Gateway(信任 Header)
openclaw.json:
{
"gateway": {
"auth": {
"mode": "trusted-proxy",
"trustedProxy": {
"userHeader": "x-forwarded-user"
}
},
"controlUi": {
"basePath": "/console/openclaw"
},
"trustedProxies": ["127.0.0.1", "172.18.0.1"]
}
}
mode: "trusted-proxy"—— Gateway 不在自身做认证,信任上游代理userHeader: "x-forwarded-user"—— 从哪个 HTTP 头读取用户名trustedProxies—— 安全关键:只有来自这些 IP 的请求才会被信任。外部 IP 伪造X-Forwarded-User头会被忽略
完整请求流程
生产环境(Nginx 在中间)
1. 用户登录博客 → Django 认证通过 → 写 openclaw_user=kivenz cookie 2. 用户点击 OpenClaw 入口 → 浏览器发 GET /console/openclaw/ + Cookie: openclaw_user=kivenz 3. Nginx 收到请求 → 读 cookie → 转 HTTP Header: X-Forwarded-User: kivenz → 代理到 127.0.0.1:18789 4. Gateway 收到请求 → 检查来源 IP 在 trustedProxies 中 (127.0.0.1 ✓) → 读 X-Forwarded-User: kivenz → 认证为用户 kivenz → 返回 OpenClaw 控制台
开发环境(Django 直接代理)
1. 同上
2. 同上
3. Django OpenClawProxyView
→ 从 request.user.username 拿用户名
→ 注入 X-Forwarded-User: kivenz header
→ requests.get("http://127.0.0.1:18789", headers=...)
4. Gateway 同上
安全机制
| 层级机制防护 | ||
| Cookie | httponly=True | JS 无法读取,防 XSS 窃取 |
| Cookie | samesite="Strict" | 跨站请求不带 cookie,防 CSRF |
| Cookie | max_age=86400 | 24 小时过期,减少泄露窗口 |
| Django | LoginRequiredMixin + is_staff | 只有管理员能访问 OpenClaw |
| Gateway | trustedProxies 白名单 | 外部 IP 伪造 X-Forwarded-User 无效 |
| Nginx | 仅转发 $cookie_openclaw_user | 不接受客户端传入的 X-Forwarded-User |
常见问题
Q: Cookie 过期了怎么办?
A: openclaw_user cookie 有效期 24 小时。过期后重新访问一次 /console/ 会刷新 cookie(无需重新登录,只要 Django session 还在)。
Q: 为什么有时要重新登录?
A: Django session 默认 3 天。如果 session 也过期,需要重新登录(飞书或密码)。
Q: 能在公网直接访问 Gateway 绕过认证吗?
A: 不能。Gateway 在 trusted-proxy 模式下,只要 X-Forwarded-User 头不是来自白名单 IP,就会被忽略。公网请求到 Gateway 会因为没有有效身份信息而被拒绝。
Q: 本地开发怎么测?
A: 启动 Gateway → 启动 Django python manage.py runserver → 访问 http://127.0.0.1:8000/console/login/ 登录 → 然后访问 /console/openclaw/。Django 的 OpenClawProxyView 会自动处理代理。