IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。
前面几篇我们围绕数据操作、自动化逻辑进行了深入讲解,今天我们要谈一个至关重要但常被初学者忽视的话题------安全。MySQL 的用户体系如何设计?怎样精确控制谁能访问哪些数据?如何避免删库跑路的悲剧?本篇用 Python 配合实战,带你搭建一个安全的数据库访问体系。
1. MySQL 的用户体系概览
MySQL 的用户由用户名 和主机名 共同唯一确定。也就是说,'app_user'@'localhost' 和 'app_user'@'%' 是两个完全不同的用户,可以拥有不同的权限。
用户信息存储在哪? 在 mysql.user 表中。
bash
import mysql.connector
# 用 root 连接
conn = mysql.connector.connect(
host="127.0.0.1", port=3306,
user="root", password="MyNewPass123!"
)
cursor = conn.cursor()
# 查看当前所有用户
cursor.execute("SELECT host, user, plugin FROM mysql.user")
print("📋 当前数据库用户:")
for row in cursor.fetchall():
print(f" '{row[1]}'@'{row[0]}' 认证方式: {row[2]}")
预期输出(结果取决于你的环境):
bash
📋 当前数据库用户:
'root'@'%' 认证方式: mysql_native_password
'root'@'localhost' 认证方式: caching_sha2_password
'mysql.infoschema'@'localhost' 认证方式: caching_sha2_password
'mysql.session'@'localhost' 认证方式: caching_sha2_password
'mysql.sys'@'localhost' 认证方式: caching_sha2_password
MySQL 8.0 的认证方式变化 :默认从
mysql_native_password升级为caching_sha2_password,安全性更强。如果老旧的客户端连接报错Authentication plugin 'caching_sha2_password' cannot be loaded,需要将用户改回mysql_native_password或升级客户端。
2. 用户管理:从创建到删除
2.1 创建用户
bash
CREATE USER '用户名'@'主机' IDENTIFIED BY '密码';
主机通配符:
-
'localhost':仅本机可连接 -
'192.168.1.%':仅该网段可连接 -
'%':任意主机可连接(安全警告:生产环境尽量避免)
bash
# 创建一个只能从本机连接的应用用户
cursor.execute("CREATE USER IF NOT EXISTS 'app_user'@'localhost' IDENTIFIED BY 'App@2025!Pass'")
print("✅ 用户 app_user@localhost 创建成功")
# 创建一个允许从任意 IP 连接的只读用户
cursor.execute("CREATE USER IF NOT EXISTS 'reader'@'%' IDENTIFIED BY 'Reader@2025!Pass'")
print("✅ 用户 reader@% 创建成功")
# 查看新建用户
cursor.execute("SELECT host, user FROM mysql.user WHERE user IN ('app_user', 'reader')")
for row in cursor.fetchall():
print(f" '{row[1]}'@'{row[0]}'")
密码安全要求 :MySQL 8.0 默认启用密码复杂度校验插件 validate_password。如果密码太简单会报错:
bash
ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
可通过以下命令查看策略:
bash
cursor.execute("SHOW VARIABLES LIKE 'validate_password%'")
print("\n📋 密码策略:")
for row in cursor.fetchall():
print(f" {row[0]} = {row[1]}")
2.2 修改用户
bash
# 修改密码
cursor.execute("ALTER USER 'app_user'@'localhost' IDENTIFIED BY 'NewApp@2025!Pass'")
print("✅ 密码修改成功")
# 锁定 / 解锁用户(MySQL 8.0+)
cursor.execute("ALTER USER 'reader'@'%' ACCOUNT LOCK")
print("✅ 用户 reader 已锁定")
cursor.execute("ALTER USER 'reader'@'%' ACCOUNT UNLOCK")
print("✅ 用户 reader 已解锁")
# 重命名用户
cursor.execute("RENAME USER 'app_user'@'localhost' TO 'api_user'@'localhost'")
print("✅ 用户已重命名")
2.3 删除用户
bash
cursor.execute("DROP USER IF EXISTS 'reader'@'%'")
print("✅ 用户 reader 已删除")
3. 权限体系:五层金字塔
MySQL 的权限分为五个层级,从上到下粒度越来越细:
bash
┌────────────────────┐
│ 全局权限 │ 对所有数据库生效
│ (ON *.*) │
├────────────────────┤
│ 数据库级权限 │ 对某个数据库所有表生效
│ (ON shop.*) │
├────────────────────┤
│ 表级权限 │ 对某张表所有列生效
│ (ON shop.users) │
├────────────────────┤
│ 列级权限 │ 只对某列生效
│ (SELECT(name)) │
├────────────────────┤
│ 存储过程/函数权限 │ 对特定 Procedure 生效
└────────────────────┘
3.1 授予权限(GRANT)
基础语法:
bash
GRANT 权限列表 ON 作用范围 TO '用户名'@'主机';
常用权限速查表:
3.2 Python 实战:配置应用权限
为我们的 shop 应用配置合适的权限:
bash
# 先创建好 shop 数据库(如已存在则跳过)
cursor.execute("CREATE DATABASE IF NOT EXISTS shop")
cursor.execute("USE shop")
# 创建开发用户
cursor.execute("CREATE USER IF NOT EXISTS 'dev_user'@'localhost' IDENTIFIED BY 'Dev@2025!Pass'")
# 创建只读用户
cursor.execute("CREATE USER IF NOT EXISTS 'readonly_user'@'%' IDENTIFIED BY 'Read@2025!Pass'")
# 1. 给开发用户授予 shop 库的全部操作权限(不含管理权限)
cursor.execute("GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX ON shop.* TO 'dev_user'@'localhost'")
print("✅ dev_user 获得 shop 库的 DML + DDL 权限")
# 2. 给只读用户授予 SELECT 权限(仅限 shop 库)
cursor.execute("GRANT SELECT ON shop.* TO 'readonly_user'@'%'")
print("✅ readonly_user 获得 shop 库的只读权限")
# 3. 授予 EXECUTE 权限(调用存储过程)
cursor.execute("GRANT EXECUTE ON shop.* TO 'dev_user'@'localhost'")
# 刷新权限(修改权限表后建议执行)
cursor.execute("FLUSH PRIVILEGES")
3.3 查看用户权限
bash
cursor.execute("SHOW GRANTS FOR 'dev_user'@'localhost'")
print("\n📋 dev_user 的权限:")
for row in cursor.fetchall():
print(f" {row[0]}")
预期输出:
bash
📋 dev_user 的权限:
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, EXECUTE ON `shop`.* TO `dev_user`@`localhost`
3.4 验证权限效果
用只读用户连接,尝试写入数据,应该被拒绝:
bash
try:
# 用只读用户重新连接
ro_conn = mysql.connector.connect(
host="127.0.0.1", port=3306,
user="readonly_user", password="Read@2025!Pass",
database="shop"
)
ro_cursor = ro_conn.cursor()
# 尝试 SELECT(应该成功)
ro_cursor.execute("SELECT 1")
print("✅ 只读用户 SELECT 成功")
# 尝试 INSERT(应该被拒绝)
try:
ro_cursor.execute("CREATE TABLE IF NOT EXISTS test (id INT)")
print("❌ 不应该到这里")
except mysql.connector.Error as e:
print(f"❌ 只读用户 INSERT 被拒绝: {e}")
ro_cursor.close()
ro_conn.close()
except Exception as e:
print(f"连接失败: {e}")
预期输出:
bash
✅ 只读用户 SELECT 成功
❌ 只读用户 INSERT 被拒绝: 1142 (42000): CREATE command denied to user 'readonly_user'@'localhost' ...
3.5 撤销权限(REVOKE)
bash
# 撤销 dev_user 的 DROP 权限(安全加固)
cursor.execute("REVOKE DROP ON shop.* FROM 'dev_user'@'localhost'")
print("✅ DROP 权限已撤销")
cursor.execute("SHOW GRANTS FOR 'dev_user'@'localhost'")
print("\n📋 撤销后 dev_user 的权限:")
for row in cursor.fetchall():
print(f" {row[0]}")
最佳实践:最小权限原则------只授予完成任务所必需的权限。没有人应该用 root 跑应用,开发者也不该有 DROP 权限。
4. 角色管理(MySQL 8.0+)
MySQL 8.0 支持角色(Role),这是一组权限的集合。把权限打包成角色,再把角色授予用户,管理起来更清晰。
4.1 创建角色并分配权限
bash
# 创建角色
cursor.execute("CREATE ROLE IF NOT EXISTS 'app_developer', 'app_reader', 'app_admin'")
# 给角色分配权限
cursor.execute("GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'app_developer'")
cursor.execute("GRANT SELECT ON shop.* TO 'app_reader'")
cursor.execute("GRANT ALL PRIVILEGES ON shop.* TO 'app_admin'")
print("✅ 三个角色创建成功")
# 将角色授予用户
cursor.execute("GRANT 'app_developer' TO 'dev_user'@'localhost'")
print("✅ 角色 app_developer 已授予 dev_user")
# 设置默认角色(登录时自动激活)
cursor.execute("SET DEFAULT ROLE 'app_developer' TO 'dev_user'@'localhost'")
4.2 查看用户角色
bash
cursor.execute("SELECT * FROM mysql.role_edges")
print("\n📋 角色分配关系:")
for row in cursor.fetchall():
print(f" {row}")
# 查看当前会话的活跃角色
cursor.execute("SELECT CURRENT_ROLE()")
print(f"当前活跃角色: {cursor.fetchone()[0]}")
4.3 角色管理的优势
5. 用 Python 封装权限管理工具
将常用的权限管理操作封装成可复用的工具类:
bash
class MySQLUserManager:
def __init__(self, conn):
self.conn = conn
self.cursor = conn.cursor()
def create_user(self, username, password, host='localhost'):
"""创建用户"""
self.cursor.execute(
f"CREATE USER IF NOT EXISTS '{username}'@'{host}' IDENTIFIED BY %s",
(password,)
)
return f"'{username}'@'{host}' 创建成功"
def grant_privileges(self, username, host, privileges, database='*', table='*'):
"""授予权限"""
priv_str = ', '.join(privileges) if isinstance(privileges, list) else privileges
sql = f"GRANT {priv_str} ON {database}.{table} TO '{username}'@'{host}'"
self.cursor.execute(sql)
self.cursor.execute("FLUSH PRIVILEGES")
return f"已授予 {priv_str} 给 '{username}'@'{host}'"
def show_grants(self, username, host='localhost'):
"""查看用户权限"""
self.cursor.execute(f"SHOW GRANTS FOR '{username}'@'{host}'")
return [row[0] for row in self.cursor.fetchall()]
def revoke_all(self, username, host='localhost'):
"""撤销某用户所有权限"""
self.cursor.execute(f"REVOKE ALL PRIVILEGES, GRANT OPTION FROM '{username}'@'{host}'")
self.cursor.execute("FLUSH PRIVILEGES")
# 使用
mgr = MySQLUserManager(conn)
print(mgr.create_user("analyst", "Analyst@2025!"))
print(mgr.grant_privileges("analyst", "localhost", ["SELECT"], "shop"))
print("\n📋 analyst 权限:")
for grant in mgr.show_grants("analyst"):
print(f" {grant}")
6. 安全加固建议
6.1 必须遵守的安全准则
- 删除匿名用户和测试数据库
bash
cursor.execute("DELETE FROM mysql.user WHERE user = ''")
cursor.execute("DROP DATABASE IF EXISTS test")
cursor.execute("FLUSH PRIVILEGES")
- 限制 root 远程登录
bash
cursor.execute("DELETE FROM mysql.user WHERE user = 'root' AND host = '%'")
# 或改为仅允许特定 IP
cursor.execute("RENAME USER 'root'@'%' TO 'root'@'192.168.1.%'")
- 定期审计用户权限
bash
cursor.execute("""
SELECT user, host,
SUM(Select_priv + Insert_priv + Update_priv + Delete_priv + Create_priv + Drop_priv) AS priv_count
FROM mysql.user
GROUP BY user, host
ORDER BY priv_count DESC
""")
print("\n📋 用户权限统计:")
for row in cursor.fetchall():
print(f" '{row[0]}'@'{row[1]}' 权限数: {row[2]}")
- 使用环境变量管理密码
bash
import os
password = os.getenv("MYSQL_PASSWORD")
conn = mysql.connector.connect(
host="127.0.0.1",
user="app_user",
password=password # 永远不要硬编码
)
7. 动手试试:搭建安全的多角色权限体系
为你的 shop 数据库设计三套权限:
-
管理员角色
shop_admin:拥有 shop 库的所有 DML + DDL 权限,但不能删库。 -
开发者角色
shop_dev:有 SELECT、INSERT、UPDATE、DELETE 权限,不能改表结构。 -
只读角色
shop_reader:只能 SELECT。
然后:
-
创建三个用户分别对应三种角色
-
用每个用户的凭据连接 MySQL,测试能做什么、不能做什么
-
尝试用只读用户执行
DROP TABLE,确认被拒绝
8. 总结
今天我们掌握了 MySQL 安全体系的核心:
-
用户模型 :
'user'@'host'唯一标识,支持锁定、重命名。 -
权限层级:全局 → 库 → 表 → 列 → 存储过程,粒度可精细控制。
-
角色管理:MySQL 8.0 的亮点,一组权限打包成角色,简化管理。
-
安全加固:删匿名用户、禁 root 远程、最小权限原则、密码不入仓库。
下一篇我们将正式进入性能优化的核心------索引,学习 B+Tree 原理和最左前缀法则。下次见!
想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !