Files
wecom_it_smart_desk/deliverables/software-bugfix-avatar-coep-delivery-2026-07-10.md
T

78 lines
3.2 KiB
Markdown
Raw Normal View History

# 头像显示修复报告 — 2026-07-10
## 问题摘要
坐席端(`https://itsupport.servyou.com.cn/itagent/workspace`)员工头像仍不显示,浏览器控制台报三类错误:
1. **COEP 阻断**`ERR_BLOCKED_BY_RESPONSE.NotSameOriginAfterDefaultedToSameOriginByCoep`
2. **Mixed Content** — HTTPS 页面不允许加载 HTTP 资源
3. **CSP 限制**`img-src` 白名单不含 `wework.qpic.cn`
## 根因分析
三层安全策略叠加导致企微头像域名 `wework.qpic.cn` 被完全拦截:
| 安全策略 | nginx 配置 | 拦截表现 |
|---------|-----------|---------|
| COEP | `require-corp` | 跨域图片无 CORS 头被拦截 |
| Mixed Content | — | HTTP 图片在 HTTPS 页面被拦截 |
| CSP | `img-src 'self' data: https://res.wx.qq.com` | `wework.qpic.cn` 不在白名单 |
## 修复方案:后端头像代理
**不修改 nginx 安全头**(保持安全级别不变),通过后端代理绕过所有三层限制:
```
前端 img.src = "/api/avatar/proxy?url=<encoded_avatar_url>"
|
v
后端 httpx.AsyncClient → wework.qpic.cn
|
v
返回图片字节流 (同域, HTTPS, 不受 CSP 限制)
```
## 修改文件
| 文件 | 操作 | 修改内容 |
|-----|------|---------|
| `backend/app/services/avatar_service.py` | 修改 | `clean_avatar_url` 增加 HTTP→HTTPS 转换;新增 `wrap_avatar_url` 代理 URL 包装函数 |
| `backend/app/services/session_service.py` | 修改 | 3个头像返回路径统一应用 `wrap_avatar_url` |
| `backend/app/api/avatar.py` | 新建 | 头像代理端点 `/api/avatar/proxy`SSRF 白名单校验 `wework.qpic.cn``httpx.AsyncClient` 异步获取,返回 `Response(content=..., media_type="image/jpeg")` |
| `backend/app/api/router.py` | 修改 | 注册 `avatar_router` 路由,前缀 `/api/avatar/proxy` |
## 验证结果
### 1. API 层(curl
| 测试项 | 结果 |
|-------|------|
| 代理 HTTPS 头像 URL | ✅ HTTP 200, image/jpeg, 42KB |
| 代理 HTTP 头像 URL(自动转 HTTPS | ✅ HTTP 200, image/jpeg, 65KB |
| 无效 URL(非企微域名) | ✅ 403 拒绝 |
### 2. 浏览器验证(agent-browser
- ✅ 坐席工作台正常加载,无重定向到登录页
-**3个头像全部通过代理加载**
- `GET /api/avatar/proxy?url=... (Image) 200` × 3
-**控制台零错误**:无 COEP、无 Mixed Content、无 CSP 报错
-**网络请求零 4xx/5xx**
- ✅ 头像在左侧会话列表和顶部导航栏均正常显示
### 3. 截图证据
`screenshot-1783683005062.png` — 坐席工作台界面,头像正常显示在左侧列表和顶部用户信息区域。
## 部署摘要
1. 上传 4 个修改文件 → `/tmp/`
2. `cp``/opt/wecom-it-desk/app/` 对应路径
3. `docker compose restart backend`
4. 清除 Redis 头像缓存 `employee:avatar:*`3 个 key
5. 代理端点 `/api/avatar/proxy` 即时生效
## 关于"获取坐席列表失败"
本次浏览器验证中未复现该错误。该错误与头像代理无关,如持续出现需检查后端日志中 `/api/agents` 的具体报错(可能为认证或数据库问题)。