bea288e414
== 已部署上线 (9项) == - 代办事项真实数据源集成 (企微审批API 8bug修复链) - H5/坐席端 Logo样式统一+绿色背景 - 视频引导页修复 (localStorage key v2) - 坐席端 v9 Vue版本修复 (ElMessage._context) - 截图按钮 v10 修复 (getDisplayMedia user gesture) - 扫码样式恢复+H5扫码登录跳转修复 - H5截图快捷键提示 == 代码完成待部署 (3项) == - 知识迭代3Bug修复 (#8 POST端点/#7 MERGE幂等/#6 过期检查) - 会议室预定-小鱼易联终端 (40文件, 40/40测试通过) - IT资产升级审批推送 (asset_service.py) == 需求文档 (2项) == - 坐席端AI辅助消息框-PRD (4项新功能确认) - 坐席端布局优化建议 v2.0 (7天计划) == 新增文档 == - 日报-2026-07-11.md - 知识迭代Bug修复报告-20260711.md - 会议室预定-部署指南.md - CHANGELOG.md 更新 == 测试 == - test_todo_integration.py: 40/40 - test_meetingroom.py: 40/40 - test_bugfix_ki_suggestions.py: 21/21
78 lines
3.2 KiB
Markdown
78 lines
3.2 KiB
Markdown
# 头像显示修复报告 — 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` 的具体报错(可能为认证或数据库问题)。
|