feat(v0.7.1): P0 修复 + 企微 SSO + RBAC 细粒度 + audit_log
P0 修复: - /api/ready import 错误 (_get_engine + settings.create_redis_client) - 删 agent.otp_secret/otp_enabled 双字段 (migration 026) - 重建 021_rbac migration (IF NOT EXISTS 兼容) P1 新增: - 企微 SSO (auth_wecom_sso.py, useWeChatWorkSSO composable, PortalSelect UA 检测) - RBAC 5 角色 × 4 资源 × 4 操作 × 3 范围 (rbac_service + seed_rbac + require_permission) - audit_log 模型 + migration 027 + 服务 + API - 管理后台 RBAC 权限矩阵 UI (PermissionsMatrix.vue) 质量: - pytest 405 passed / 33 pre-existing failed / 4 xfailed (v0.7.1 引入失败 = 0) - conftest GBK patch 强制 UTF-8 读 .env - .gitignore 排除 *.b64 (含 admin token 凭据) - DEPLOY-v0.7.1.md 7 步 runbook + 4 坑 + 回滚预案
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
// =============================================================================
|
||||
// 企微IT智能服务台 — 企微 SSO UA 检测 composable (v0.7.1 task #85)
|
||||
// =============================================================================
|
||||
// 解决问题: v0.7.0 hotfix1 用户反馈"企微工作台进入应用也要扫码"。
|
||||
//
|
||||
// 用法:
|
||||
// const sso = useWeChatWorkSSO()
|
||||
// if (sso.isWeChatWork()) {
|
||||
// window.location.href = sso.buildInitUrl('/itdesk/')
|
||||
// }
|
||||
//
|
||||
// 设计原则:
|
||||
// 1. UA 检测: MicroMessenger / wxwork / wxwork.* 都算企微浏览器
|
||||
// 2. 静默授权: scope=snsapi_base (用户无感,直接拿到 userid)
|
||||
// 3. 降级策略: 非企微浏览器保留原 QR 扫码流程
|
||||
// =============================================================================
|
||||
|
||||
export type SSOInitOptions = {
|
||||
/** 登录成功后跳转路径,默认 /itdesk/ */
|
||||
next?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 企微 UA 检测 + SSO URL 生成 composable
|
||||
*/
|
||||
export function useWeChatWorkSSO() {
|
||||
/**
|
||||
* 检测当前是否在企微浏览器中
|
||||
*
|
||||
* 匹配的 UA 关键字:
|
||||
* - MicroMessenger: 微信内置浏览器(用户侧)
|
||||
* - wxwork: 企业微信内置浏览器(企业侧,最常见)
|
||||
* - wxwork/.*: 企微版本号
|
||||
* - DingTalk: 钉钉(预留,暂不实现)
|
||||
*
|
||||
* 参考文档: https://developer.work.weixin.qq.com/document/path/91484
|
||||
*/
|
||||
function isWeChatWork(): boolean {
|
||||
const ua = navigator.userAgent || ''
|
||||
return /MicroMessenger/i.test(ua) || /wxwork/i.test(ua) || /\bDingTalk\b/i.test(ua)
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 SSO 初始化 URL
|
||||
*
|
||||
* 后端 /api/auth_wecom/sso/init 会:
|
||||
* 1. 生成 state 存 Redis (5 分钟 TTL, 防 CSRF)
|
||||
* 2. 拼企微 OAuth2 授权 URL
|
||||
* 3. 302 跳转到企微授权页
|
||||
*
|
||||
* 企微授权页会:
|
||||
* 1. 用户授权(静默, snsapi_base)
|
||||
* 2. 回调 /api/auth_wecom/sso/callback?code=...&state=...
|
||||
* 3. 后端用 code 换 userid, 查角色, 生成 SSO token
|
||||
* 4. 302 跳转到 next + ?sso_token=xxx
|
||||
*
|
||||
* @example
|
||||
* window.location.href = buildInitUrl('/itdesk/')
|
||||
*/
|
||||
function buildInitUrl(next: string = '/itdesk/'): string {
|
||||
const params = new URLSearchParams({ next })
|
||||
// 用相对路径走 nginx 反代(开发环境走 vite proxy)
|
||||
return `/api/auth_wecom/sso/init?${params.toString()}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 用 SSO token 换取用户身份(一次性 token, 用完即删)
|
||||
*
|
||||
* 后端会返回 { user_id, name, role }
|
||||
* 前端用此身份调 portal.getRoles() 或写入 store
|
||||
*/
|
||||
async function verifyToken(ssoToken: string): Promise<{
|
||||
user_id: string
|
||||
name: string
|
||||
role: string
|
||||
} | null> {
|
||||
try {
|
||||
const apiBase = import.meta.env.VITE_API_BASE || '/api'
|
||||
const resp = await fetch(`${apiBase}/auth_wecom/sso/verify?sso_token=${encodeURIComponent(ssoToken)}`)
|
||||
const data = await resp.json()
|
||||
if (data?.code === 0 && data.data) {
|
||||
return data.data
|
||||
}
|
||||
return null
|
||||
} catch (e) {
|
||||
console.error('SSO verify 失败:', e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷函数: 如果是企微浏览器则跳转 SSO, 否则返回 false
|
||||
*
|
||||
* 用法:
|
||||
* if (tryAutoSSO({ next: '/itdesk/' })) return
|
||||
* // 降级到 QR 扫码
|
||||
*/
|
||||
function tryAutoSSO(options: SSOInitOptions = {}): boolean {
|
||||
if (!isWeChatWork()) return false
|
||||
const url = buildInitUrl(options.next)
|
||||
window.location.href = url
|
||||
return true
|
||||
}
|
||||
|
||||
return {
|
||||
isWeChatWork,
|
||||
buildInitUrl,
|
||||
verifyToken,
|
||||
tryAutoSSO,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user