MySQL中为开发者创建仅能操作特定Schema的权限,必须显式指定库名(如team_a_backend.*),不可用*.*或省略;需先REVOKE ALL PRIVILEGES ON *.*再授予业务库权限,并在MySQL 8.0.12+中额外执行REVOKE SELECT ON INFORMATION_SCHEMA.*以禁用元数据访问。

GRANT 必须显式写库名,不能用 *.* 或省略
直接 GRANT SELECT ON *.* 或 GRANT SELECT ON * 会把 mysql、performance_schema、information_schema 全部暴露——哪怕查不到数据,也能看到表结构、用户列表甚至密码哈希字段。开发团队只需要操作自己的 Schema,比如 team_a_backend,那就必须写成:
GRANT SELECT, INSERT, UPDATE ON team_a_backend.* TO 'dev_team_a'@'10.10.20.%'- 库名不用单引号,有特殊字符(如连字符)才用反引号:
`team-a-frontend`.* - host 要匹配真实连接来源:
'10.10.20.%'不匹配 IPv6 地址或带端口的连接,别偷懒用'%'
旧权限不自动覆盖,必须先 REVOKE 再 GRANT
执行新 GRANT 不会删掉旧权限。如果用户之前被授过 GRANT SELECT ON *.*,你现在只给 team_b_api.*,他仍能执行 SELECT * FROM information_schema.TABLES 查所有库的表名,甚至跨库 JOIN。
- 先查当前权限:
SHOW GRANTS FOR 'dev_team_b'@'10.10.20.%' - 如果有全局权限,必须显式回收:
REVOKE ALL PRIVILEGES ON *.* FROM 'dev_team_b'@'10.10.20.%' - 再授业务库权限:
GRANT SELECT, INSERT ON team_b_api.* TO 'dev_team_b'@'10.10.20.%' - 验证是否生效:
SELECT Select_priv FROM mysql.db WHERE User='dev_team_b' AND Db='team_b_api'\G,看是否为Y
MySQL 8.0+ 需额外控制 INFORMATION_SCHEMA 访问
只授 team_c_data.* 后,开发连上执行 SHOW CREATE TABLE users 或 ORM 自动查列信息,可能报错 ERROR 1142 (42000): SELECT command denied——这不是权限没给,而是 MySQL 8.0+ 默认收紧了元数据访问。
- 要支持开发工具读表结构,得额外授权:
GRANT SELECT ON INFORMATION_SCHEMA.COLUMNS TO 'dev_team_c'@'10.10.20.%' - 如果不想暴露元数据,MySQL 8.0.12+ 可回收:
REVOKE SELECT ON INFORMATION_SCHEMA.* FROM 'dev_team_c'@'10.10.20.%' - MySQL 5.7 及更早版本做不到回收,只能靠初始就不授任何系统库权限
每个团队必须配独立数据库 + 独立账号,禁用跨库授权
共用一个库、只给不同表权限(比如 GRANT SELECT ON app_db.orders, app_db.users)看似省事,但 information_schema.TABLES 会暴露全部表名,一旦代码出错或被注入,其他模块结构就全漏了;而且只要权限存在,SELECT * FROM users 就能跑通。
- 物理隔离是唯一可靠方式:每个团队一个库(
CREATE DATABASE team_d_analytics)、一个账号(CREATE USER 'dev_team_d'@'10.10.20.%') - 跨模块查询必须走视图或 API,严禁直连授
SELECT ON other_team_db.* - 视图权限也要单独授:
GRANT SELECT ON team_d_analytics.user_summary_view TO 'dev_team_d'@'10.10.20.%' - 不要用
WITH GRANT OPTION,避免团队自授权限
实操中最容易被忽略的是 host 匹配和 INFORMATION_SCHEMA 的显式控制——前者导致权限“看着给了却连不上”,后者导致“连上了却查不了结构”,两者都得在建账号阶段就定死,事后补救成本远高于预防。


















