5.3.3 表
表做为具体的对象,当我们谈论到对某个对象授权时,已经进入到一个相对细粒度的权限级别了,表对象的授权信息保存在mysql.tables_priv字典表中。
我知道很多初学者在学习MySQL权限操作时,由于对权限体系了解有限不够熟悉,甚至可能不清楚究竟有哪些权限可供授权。这个问题解决起来也很简单,直接看官方文档中有相关内容,文档中有个表格罗列了所有可授予的权限。当然啦看本书也靠谱,前面5.2.2小节中列的表格就是抄(提起这个字儿我脸就红了)自官方文档的权限列表,里面各种权限明细写的清清楚楚明明白白,看完后记住就不会再迷茫啦。
还有些朋友文档也没少看,可就是记不住,一方面由于权限类型多(其实MySQL中的权限相比ORACLE已经少太多了),再一个权限还分了多个粒度,谁能记的清哪个粒度都能有哪些权限哪。针对这种情况也很好解决,desc查看相关表对象的结构即可。
比方说,现在咱们都不知道在表一级,究竟能够授予用户什么样的权限(或者说用户有什么样的选择),那么直接desc mysql.tables_priv查看,例如:
(system@localhost) [(none)]> desc mysql.tables_priv;
+-------------+--------------+------+-----+----------+------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+----------+------------+
| Host | char(60) | NO | PRI | | |
| Db | char(64) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Table_name | char(64) | NO | PRI | | |
| Grantor | char(77) | NO | MUL | | |
| Timestamp | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| Table_priv | set('Select','Insert','Update','Delete','Create','Drop','Grant','References','Index','Alter','Create View','Show view','Trigger') | NO | | | |
| Column_priv | set('Select','Insert','Update','References') | NO | | | |
+-------------+--------------+------+-----+----------+------------+
8 rows in set (0.00 sec)
直接输出的信息较长,这里做了些删减。简要说一下tables_priv字典表的结构:
n Host:来源主机;
n Db:对象所属数据库;
n User:用户名;
n Table_name:表对象名称;
n Grantor:执行权限授予的用户;
n Timestamp:授予权限的时间;
n Table_priv:能够授予的表粒度的权限,也就是我们最关注的信息;
n Column_priv:能够授予的列粒度的权限;
Host+Db+User+Table_name四个纬度的共同作用成就一条权限,粒度够细。
注意看Table_priv/Column_priv两列对应的列值,这些列值就是表对象能够授予的权限。这下知道权限关键字怎么写了吧,三思老早就表达过这样一种观点,学习是有技巧的,死记硬背(SJYB)是技巧之一,但不一定是最好的,随机应变(SJYB)才是~~~
知道了关键字,就可以根据需求进行授权了。比如说,向jss_tables用户授予users表的全部权限,该怎么写GRANT语句呢:
(system@localhost) [(none)]> grant all on jssdb.users to jss_tbls;
Query OK, 0 rows affected (0.02 sec)
哎哟哟,咋没写前面desc里看到的权限关键字呢,这样写也能成功授权吗,黑黑,三思都说了要SJYB的嘛,让事实来说话吧:
(system@localhost) [(none)]> select * from mysql.tables_priv where user='jss_tbls' and table_name='users'\G
*************************** 1. row ***************************
Host: %
Db: jssdb
User: jss_tbls
Table_name: users
Grantor: system@localhost
Timestamp: 0000-00-00 00:00:00
Table_priv: Select,Insert,Update,Delete,Create,Drop,References,Index,Alter,Create View,Show view,Trigger
Column_priv:
1 row in set (0.00 sec)
这下理解了吧,ALL就是所有权限嘛。
不过细心的朋友想必早已注意到tables_priv表中的Columns_priv列了吧,你说表粒度的权限怎么会出现对列级权限的指定呢,这点在我看来其实就是体现MySQL细节设计上的特点,做为一款开源软件,它在整体设计上有时确实让人感觉摸不着头脑,说的更直白些就是它自己都没想清楚啊,这一点不仅仅体现在权限设计上,在其它设计比较初始化参数,管理功能等等都有体现。
总之就是Column_priv列在声明表级权限时没用,但在授予列级权限时就有反映了,继续往下看吧。
5.3.4 列
列级权限,是MySQL权限体系中的最细粒度,属于权限认证体系中的高精尖武器。通过对表中列的授权,可以实现只允许从某主机来的某用户访问某库的某表的某列。
列级权限保存在mysql.columns_priv字典中,该字典结构如下:
(system@localhost) [(none)]> desc mysql.columns_priv;
+-------------+---------------+------+-----+----------+-----------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------+------+-----+----------+-----------+
| Host | char(60) | NO | PRI | | |
| Db | char(64) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Table_name | char(64) | NO | PRI | | |
| Column_name | char(64) | NO | PRI | | |
| Timestamp | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| Column_priv | set('Select','Insert','Update','References') | NO | | | |
+-------------+---------------+------+-----+----------+-----------+
7 rows in set (0.00 sec)
列级权限需要Host+Db+User+Table_name+Column_name五个粒度,另外从上面的对象结构可以看出,对于列级权限可授予的共有4项,其中只有前3项有实际意义:
l Select:查询权限;
l Insert:插入权限;
l Update:修改权限;
l References:尚未应用,直接无视;
授予列级权限,在执行GRANT语句时,语法上稍有不同,主要体现在指定列级的粒度语法并不在ON子句,而是在指定priv_type时顺道附带列名的方式,例如,授予jss_cols用户查询jssdb.users表phoneno列的权限,执行语句如下:
(system@localhost) [(none)]> grant select (phoneno) on jssdb.users to jss_cols;
Query OK, 0 rows affected (0.00 sec)
查看字典表中存储的信息:
(system@localhost) [(none)]> select * from mysql.columns_priv;
+------+-------+----------+------------+-------------+---------------------+-------------+
| Host | Db | User | Table_name | Column_name | Timestamp | Column_priv |
+------+-------+----------+------------+-------------+---------------------+-------------+
| % | jssdb | jss_cols | users | phoneno | 0000-00-00 00:00:00 | Select |
+------+-------+----------+------------+-------------+---------------------+-------------+
1 row in set (0.00 sec)
(system@localhost) [(none)]> select * from mysql.tables_priv where user='jss_cols';
+------+-------+----------+------------+------------------+---------------------+------------+-------------+
| Host | Db | User | Table_name | Grantor | Timestamp | Table_priv | Column_priv |
+------+-------+----------+------------+------------------+---------------------+------------+-------------+
| % | jssdb | jss_cols | users | system@localhost | 0000-00-00 00:00:00 | | Select |
+------+-------+----------+------------+------------------+---------------------+------------+-------------+
1 row in set (0.00 sec)
列级字典表中是有数据的,表级字典表中也有记录存在的,就目前的实际情况来看,columns_priv表控制具体的权限,tables_priv表中的数据则是用来标记该条授权的一些基础信息,比如授予者,操作时间等。
对同一个表对象再授权另一个权限,看看字典表中如何存储就更加明确了:
(system@localhost) [(none)]> grant insert (address) on jssdb.users to jss_cols;
Query OK, 0 rows affected (0.00 sec)
(system@localhost) [(none)]> select * from mysql.columns_priv;
+------+-------+----------+------------+-------------+---------------------+-------------+
| Host | Db | User | Table_name | Column_name | Timestamp | Column_priv |
+------+-------+----------+------------+-------------+---------------------+-------------+
| % | jssdb | jss_cols | users | phoneno | 0000-00-00 00:00:00 | Select |
| % | jssdb | jss_cols | users | address | 0000-00-00 00:00:00 | Insert |
+------+-------+----------+------------+-------------+---------------------+-------------+
2 rows in set (0.00 sec)
(system@localhost) [(none)]> select * from mysql.tables_priv where user='jss_cols';
+------+-------+----------+------------+------------------+---------------------+------------+---------------+
| Host | Db | User | Table_name | Grantor | Timestamp | Table_priv | Column_priv |
+------+-------+----------+------------+------------------+---------------------+------------+---------------+
| % | jssdb | jss_cols | users | system@localhost | 0000-00-00 00:00:00 | | Select,Insert |
+------+-------+----------+------------+------------------+---------------------+------------+---------------+
1 row in set (0.00 sec)
注意到差别了吧,tables_priv只是表级粗粒度的记录,columns_priv才是决定列级权限粒度的核心。
下面使用刚刚创建的jss_cols用户连接到数据库查看一下:
[mysql@mysqldb02 ~]$ mysql -ujss_cols -h 192.168.30.243
Welcome to the MySQL monitor. Commands end with ; or \g.
..........
..........
(jss_cols@192.168.30.243) [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| jssdb |
+--------------------+
2 rows in set (0.00 sec)
(jss_cols@192.168.30.243) [(none)]> use jssdb;
Database changed
(jss_cols@192.168.30.243) [jssdb]> show tables;
+-----------------+
| Tables_in_jssdb |
+-----------------+
| users |
+-----------------+
1 row in set (0.00 sec)
(jss_cols@192.168.30.243) [jssdb]> desc users;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| address | varchar(50) | YES | | NULL | |
| phoneno | varchar(15) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
2 rows in set (0.02 sec)
能,且仅能查看授权了的表的指定列,看起来该表似乎只有这两个列,其实是因为它只能看到这两列,实际操作这两珍时也将发现,这两列的权限都是不一样的。
问:怎么查看当前用户拥有的权限呢?
答:盆友,可还记的5.2.3小节讲过的SHOW GRANTS命令吗。
这里需要注意的一点是,尽管通过DESC查看表结构,或者是使用SELECT语句查询表数据时只能查到被授予权限的列,但是,该用户查询information_schema.tables或其它相关字典表时,看到的表的信息,仍然是完整的,比如表的大小、索引大小、平均列长度等等信息,这也是INFORMATION_SCHEMA库比较特殊的另一个体现吧。
5.3.5 程序
MySQL中的程序(ROUTINE)主要是指Procedure和Function两类对象,这两类对象的权限与前面描述的4种基本无关联(如果说有的话,也只是用户是否拥有连接数据库的权限),相对比较独立。
对于已存在的Procedure/Function,DBA可以对用户授予执行(execute)、修改(alter routine)、授予(grant)权限,这部分权限体现在mysql.procs_priv表中,例如:
(system@localhost) [(none)]> desc mysql.procs_priv;
+--------------+----------------------------------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+----------------------------------------+------+-----+-------------------+-----------------------------+
| Host | char(60) | NO | PRI | | |
| Db | char(64) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Routine_name | char(64) | NO | PRI | | |
| Routine_type | enum('FUNCTION','PROCEDURE') | NO | PRI | NULL | |
| Grantor | char(77) | NO | MUL | | |
| Proc_priv | set('Execute','Alter Routine','Grant') | NO | | | |
| Timestamp | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+--------------+----------------------------------------+------+-----+-------------------+-----------------------------+
8 rows in set (0.00 sec)
除此之后,还可以授予用户创建(create routine)权限,这个权限在user/db/host几个表中都有体现。拥有create routine权限的用户能够创建procedure/function对象。这个权限是用户/库一级权限,而execute/alter routine/grant这三个权限则是对象极,都是针对某个指定的procedure/function做授权。
关于"程序"对象的权限操作就不演示了,实在是跟之前的权限授予/收回操作没啥区别,重复的事情做起来实在没意思,还浪费纸张,很不低碳,咱们还是接着做点对全人类有益的事情,少说点儿废话,多做点儿实事儿吧。