必须用 case when 显式处理 null、空字符串和长度异常,再脱敏手机号;视图字段须加 _masked 后缀且禁止与基表同名;创建后须回收基表权限并授予视图权限;json 内敏感信息需先提取再脱敏。

视图里直接用 CASE WHEN 而不是裸 CONCAT
裸写 CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)) 看似简洁,但遇到 NULL、空字符串或长度异常(比如手机号只有 2 位),结果就是 NULL 或报错。下游系统拿到空值,根本没法展示或校验。
必须用 CASE WHEN 显式覆盖三层:
phone IS NULL-
TRIM(phone) = ''(MySQL)或phone != ''(PostgreSQL,因''不触发IS NOT NULL) -
LENGTH(TRIM(phone)) = 11(或其他合法长度)
示例(MySQL):
CASE WHEN phone IS NULL THEN '***'
WHEN TRIM(phone) = '' THEN '***'
WHEN LENGTH(TRIM(phone)) = 11 THEN CONCAT(LEFT(TRIM(phone), 3), '****', RIGHT(TRIM(phone), 4))
ELSE '****' END AS phone_masked
字段名必须加 _masked 后缀
视图里绝不能出现和基表同名的 phone 字段。BI 工具或 ORM 会按元数据自动映射,应用层拿到脱敏值却当成原始格式,后续 WHERE phone LIKE '138%' 一定查不到——因为视图里压根没保留原始结构。
统一命名习惯:
phone_maskedid_card_maskedemail_masked
PostgreSQL 创建含表达式的视图时,还必须显式声明列名:CREATE VIEW v_users AS SELECT ..., CONCAT(...) AS phone_masked,否则报错。
权限回收比脱敏逻辑更关键
建完视图不回收基表权限,等于没锁门。用户只要还有 SELECT 权限在 users 表上,就能绕开视图直接执行 SELECT phone FROM users 拿到明文。
必须立刻执行:
REVOKE SELECT ON users FROM 'app_user'@'%'GRANT SELECT ON v_users TO 'app_user'@'%'
MySQL 8.0+ 推荐用角色管理:CREATE ROLE role_read_masked → GRANT SELECT ON v_users TO role_read_masked → GRANT role_read_masked TO 'app_user'@'%'。验证是否干净:SHOW GRANTS FOR 'app_user'@'%',结果里不能出现 ON users 字样。
JSON 字段里的敏感信息要先提取再脱敏
视图不会自动递归解析 JSON。meta 字段里藏的手机号或身份证号,必须显式提取出来,再套 CASE WHEN 逻辑。否则要么返回带引号字符串,要么是 NULL。
提取方式因数据库而异:
- MySQL:
TRIM(BOTH '"' FROM JSON_EXTRACT(meta, '$.phone')) - PostgreSQL:
meta->>'phone'
提取后仍需走完整脱敏判断链:判 NULL、判空、判长度、再拼接。漏掉任意一环,JSON 里的敏感数据就暴露了。










