MySQL 系列:第12篇 用户、权限与安全基础

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 必须遵守的安全准则

  1. 删除匿名用户和测试数据库
bash 复制代码
cursor.execute("DELETE FROM mysql.user WHERE user = ''")
cursor.execute("DROP DATABASE IF EXISTS test")
cursor.execute("FLUSH PRIVILEGES")
  1. 限制 root 远程登录
bash 复制代码
cursor.execute("DELETE FROM mysql.user WHERE user = 'root' AND host = '%'")
# 或改为仅允许特定 IP
cursor.execute("RENAME USER 'root'@'%' TO 'root'@'192.168.1.%'")
  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]}")
  1. 使用环境变量管理密码
bash 复制代码
import os
password = os.getenv("MYSQL_PASSWORD")
conn = mysql.connector.connect(
    host="127.0.0.1",
    user="app_user",
    password=password  # 永远不要硬编码
)

7. 动手试试:搭建安全的多角色权限体系

为你的 shop 数据库设计三套权限:

  1. 管理员角色 shop_admin:拥有 shop 库的所有 DML + DDL 权限,但不能删库。

  2. 开发者角色 shop_dev:有 SELECT、INSERT、UPDATE、DELETE 权限,不能改表结构。

  3. 只读角色 shop_reader:只能 SELECT。

然后:

  • 创建三个用户分别对应三种角色

  • 用每个用户的凭据连接 MySQL,测试能做什么、不能做什么

  • 尝试用只读用户执行 DROP TABLE,确认被拒绝

8. 总结

今天我们掌握了 MySQL 安全体系的核心:

  • 用户模型'user'@'host' 唯一标识,支持锁定、重命名。

  • 权限层级:全局 → 库 → 表 → 列 → 存储过程,粒度可精细控制。

  • 角色管理:MySQL 8.0 的亮点,一组权限打包成角色,简化管理。

  • 安全加固:删匿名用户、禁 root 远程、最小权限原则、密码不入仓库。

下一篇我们将正式进入性能优化的核心------索引,学习 B+Tree 原理和最左前缀法则。下次见!

想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !

相关推荐
云技纵横1 小时前
Gap Lock 死锁实战:5 秒在本地复现 MySQL 间隙锁死锁
后端·mysql
张居邪1 小时前
GitHub Actions + 阿里云 OSS:OIDC 免密同步构建产物
后端·github
砍材农夫2 小时前
python环境|conda安装和使用(2)
后端·python
MacroZheng2 小时前
Claude Code官方桌面端正式发布,夯爆了!
java·人工智能·后端
IT_陈寒2 小时前
React的useEffect依赖数组把我坑惨了,真相其实很简单
前端·人工智能·后端
Oneslide3 小时前
ubuntu 手动安装claude
后端
GetcharZp11 小时前
玩转 Linux 机器视觉:手把手带你搞定 Ubuntu 下海康工业相机 C++ SDK
后端
星星在线14 小时前
MusicFree:一个「All in One」的个人音乐服务器,让听歌回归简单
前端·后端