MySQL视图权限:说说那点事_SQL SECURITY DEFINER | INVOKER

【前言】

最近遇到的一个真实案例:开发前期,Mysql库中有个'root'@'%' 账号,权限为GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'。因前期不规范,开发组前期使用该账号进行项目开发;现系统要试运行,考虑到'root'@'%'权限太高,名字也比较敏感,故创建了一个运维账号'yunwei'@'%',并对'root'@'%'做了删除操作。

然,'applicator'@'%'查询视图时出现问题指定定义者的用户不存在。该sql中调用了视图,查了很久,最后才发现是视图权限SQL SECURITY { DEFINER | INVOKER 的问题。

本文,将针对MySQL的视图创建时权限:

   SQL SECURITY DEFINER|INVOKER ,说道说道。

一、 MySQL视图创建介绍

MySQL中,创建视图的完整语法:

CREATE

    [OR REPLACE]

    [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]

    [DEFINER = { user | CURRENT_USER }]

    [SQL SECURITY { DEFINER | INVOKER }]

    VIEW view_name [(column_list)]

    AS select_statement

    [WITH [CASCADED | LOCAL] CHECK OPTION]

由上,可以看出,MySQL数据库中的View创建在标准SQL的基础之上做了些扩展,语法变量信息如下:

1. ALGORITHM:

指定视图的处理方式,ALGORITHM子句是可选的,它表示使用何种算法来处理视图。并不属于标准SQL的一部分,而是MySQL对标准SQL进行的功能扩展,默认值为UNDEFINED(未定义的)。

[ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]    

1) MERGE:

将视图的定义和查询视图的语句合并处理;使得视图定义的某一部分取代语句的对应部分;

2) TEMPTABLE:

视图的结果将被置于临时表中,而后在该临时表基础上执行查询视图的语句;TEMPTABLE在创建临时表之后、并在完成语句处理之前,能够释放基表上的锁定。与MERGE算法相比,锁定释放的速度更快,这样,使用视图的其他客户端不会被屏蔽过长时间。

3) UNDEFINED:

MySQL选择使用哪种算法,一般会首选MERGE,因为MERGE更有效率,TEMPTABLE因使用了临时表,也不支持更新操作。

2. DEFINER:

指定视图的创建者/属主

1) MySQL中的对象其实不注重属主,

这点与ORACLE数据库极为不同,熟悉MySQL和Oracle的朋友都知道,默认当然就是执行CREATE VIEW语句的CURRENT_USER,但是创建时也可以指定不同的用户做为创建者,或者叫视图持有人。

3. SQL SECURITY:

视图查询数据时的安全验证方式,有两处选项:       

1) DEFINER:

不是指创建者,而是指在创建视图时验证是否有权限访问视图所引用的对象;由definer(定义者)指定的用户的权限来执行命令;

2) INVOKER:

指查询视图时,验证查询的用户是否拥有权限访问视图及视图所引用的对象。

划重点:

1) MERGE算法要求视图中的行和基表中的行具有一对一的关系。如果视图包含聚合函数(SUM(), MIN(), MAX(), COUNT()等)、DISTINCT、GROUP BY、HAVING、UNION或UNION ALL、没有基表的引用文字值等结构中的任何一种,将失去一对一的关系,此时必须使用临时表取而代之。

4. 最后一行[WITH [CASCADED | LOCAL] CHECK OPTION]

1) 该选项中的 CASCADED为默认值, 

LOCAL CHECK OPTION用于在可更新视图中防止插入或更新行。由于此选项一般不使用,因此不再赘述,具体信息请参考MySQL官方网站上的相关信息

铺垫这么多,想说明个嘛?

问题现象

删除'root'@'%'后,应用账号'applicator'@'%'查询一个view,报错The user specified as a definer ('root'@'%') does not exist。经查证是create view时指定了DEFINER和SQL SECURITY DEFINER的缘故。

问题来了:

DEFINER和SQL SECURITY DEFINER是个啥?有啥用?

DEFINER和SQL SECURITY DEFINER/INVOKER是MySQL中视图安全验证方式,核心思想还是权限 ,本质为用户是否有足够的权限去访问想访问到的数据。

这里将探讨查询视图时,对视图所引用的基表的DEFINER和SQL SECURITY DEFINER/INVOKER权限验证。

 

二、实验验证

1. 使用'root'@'%'创建库ts_db_02,使用如下方式

[root@ethan_mysql ~]# mysql -uroot -pmysql -h 10.10.178.112 -P 3308

mysql> create database ts_db_02;

Query OK, 1 row affected (0.03 sec)

mysql> show databases;

+--------------------+

| Database           |

+--------------------+

| information_schema |

| mysql              |

| performance_schema |

| sys                |

| ts_db_01           |

| ts_db_02           |

+--------------------+

6 rows in set (0.00 sec)


mysql> use ts_db_02;

Database changed

2. 查看'root'@'%'的权限

mysql> show grants for 'root'@'%';

+-------------------------------------------+

| Grants for root@%                         |

+-------------------------------------------+

| GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' |

+-------------------------------------------+

1 row in set (0.00 sec)

3. 因ALL PRIVILEGES ON *.*,根据前面SQL SECURITY参数的介绍,可以在ts_db_02中创建对ts_db_01的视图,引用账号'root'@'%',在ts_db_01中按照不同的方式创建两个视图:

mysql> create sql security definer view ts02_v_d as select * from ts_db_01.employee;

Query OK, 0 rows affected (0.04 sec)


mysql> create sql security invoker view ts02_v_i as select * from ts_db_01.employee;

Query OK, 0 rows affected (0.05 sec)


mysql> show full tables;

+--------------------+------------+

| Tables_in_ts_db_02 | Table_type |

+--------------------+------------+

| ts02_v_d           | VIEW       |

| ts02_v_i           | VIEW       |

+--------------------+------------+

2 rows in set (0.00 sec)


mysql> show create view ts_db_02.ts02_v_d \G;

*************************** 1. row ***************************

View: ts02_v_d

Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`%` SQL SECURITY DEFINER VIEW `ts_db_02`.`ts02_v_d` AS select `ts_db_01`.`employee`.`HRID` AS `HRID`,`ts_db_01`.`employee`.`EMPLOYEE_ID` AS `EMPLOYEE_ID`,`ts_db_01`.`employee`.`EMPLOYEE_NAME` AS `EMPLOYEE_NAME`,`ts_db_01`.`employee`.`EMPLOYEE_SHORT` AS `EMPLOYEE_SHORT`,`ts_db_01`.`employee`.`SEX` AS `SEX`,`ts_db_01`.`employee`.`DEPT_CODE` AS `DEPT_CODE` from `ts_db_01`.`employee`

character_set_client: utf8

collation_connection: utf8_general_ci

1 row in set (0.00 sec)


mysql> show create view ts_db_02.ts02_v_i \G;

*************************** 1. row ***************************

View: ts02_v_i

Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`%` SQL SECURITY INVOKER VIEW `ts_db_02`.`ts02_v_i` AS select `ts_db_01`.`employee`.`HRID` AS `HRID`,`ts_db_01`.`employee`.`EMPLOYEE_ID` AS `EMPLOYEE_ID`,`ts_db_01`.`employee`.`EMPLOYEE_NAME` AS `EMPLOYEE_NAME`,`ts_db_01`.`employee`.`EMPLOYEE_SHORT` AS `EMPLOYEE_SHORT`,`ts_db_01`.`employee`.`SEX` AS `SEX`,`ts_db_01`.`employee`.`DEPT_CODE` AS `DEPT_CODE` from `ts_db_01`.`employee`

character_set_client: utf8

collation_connection: utf8_general_ci

1 row in set (0.00 sec)

4. 使用账号`root`@`localhost`新建一个用户 'app_user'@'%' ,授予查看ts_db_02库视图的权限

注意:'app_user'@'%'并没有访问表ts_db_01.employee的权限 

mysql> grant select,show view on ts_db_02.* to 'app_user'@'%' identified by 'mysql';

Query OK, 0 rows affected, 1 warning (0.03 sec)

5. 查看'app_user'@'%'用户权限

mysql> show grants for 'app_user'@'%';

+-----------------------------------------------------------+

| Grants for app_user@%                                     |

+-----------------------------------------------------------+

| GRANT USAGE ON *.* TO 'app_user'@'%'                      |

| GRANT SELECT, SHOW VIEW ON `ts_db_02`.* TO 'app_user'@'%' |

+-----------------------------------------------------------+

2 rows in set (0.00 sec)

6. 使用新创建的'app_user'@'%'用户登录执行操作:

[root@ethan_mysql mysqldata]#  mysql -uapp_user -pmysql -h 10.10.178.112 -P 3308 -D ts_db_02


mysql> show full tables;

+--------------------+------------+

| Tables_in_ts_db_02 | Table_type |

+--------------------+------------+

| ts02_v_d           | VIEW       |

| ts02_v_i           | VIEW       |

+--------------------+------------+

2 rows in set (0.00 sec)


mysql> select * from ts02_v_d;

+--------+-------------+---------------+----------------+------+--------------------+

| HRID   | EMPLOYEE_ID | EMPLOYEE_NAME | EMPLOYEE_SHORT | SEX  | DEPT_CODE          |

+--------+-------------+---------------+----------------+------+--------------------+

| 334578 | 666         | ethan         | discipline     | male | application center |

+--------+-------------+---------------+----------------+------+--------------------+

1 row in set (0.00 sec)

可以看到,执行没有问题。

简单推导下过程:

ts02_v_d创建时使用的是SQL SECURITY DEFINER,其含义只验证创建视图ts02_v_d时的创建者是否有权限访问视图所引用的数据ts_db_01.employee,因为ts02_v_d创建使用的是'root'@'%',其权限是all privileges on *.*,所以创建视图过程成功;

又因为用户'app_user'@'%'有如下权限,所以当然可以查看视图ts_db_02.ts02_v_d。

 GRANT SELECT, SHOW VIEW ON `ts_db_02`.* TO 'app_user'@'%'

7. 接下来再访问invoker权限定义的视图

mysql> select * from ts02_v_i;

ERROR 1356 (HY000): View 'ts_db_02.ts02_v_i' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them

执行失败。

推导过程:

ts02_v_d创建时使用的是SQL SECURITY INVOKER,其含义是查询视图时,验证查询的用户是否拥有权限访问视图及视图所引用的对象。因为'app_user'@'%'虽然有访问视图的权限,但没有访问视图所引用对象ts_db_01.employee表的权限,所以不能执行查看视图ts_db_02.ts02_v_i。

那怎么才能执行ts_db_02.ts02_v_i?

很简单,给用户赋予访问视图所引用的对象即可。

mysql> grant select on ts_db_01.employee to 'app_user'@'%';

ERROR 2006 (HY000): MySQL server has gone away

No connection. Trying to reconnect...

Connection id:    92

Current database: *** NONE ***

Query OK, 0 rows affected (0.02 sec)

8. 再次使用'app_user'@'%';查看视图ts_db_02.ts02_v_i

[root@ethan_mysql mysqldata]#  mysql -uapp_user -pmysql -h 10.10.178.112 -P 3308 -D ts_db_02

mysql> select * from ts02_v_i;

+--------+-------------+---------------+----------------+------+--------------------+

| HRID   | EMPLOYEE_ID | EMPLOYEE_NAME | EMPLOYEE_SHORT | SEX  | DEPT_CODE          |

+--------+-------------+---------------+----------------+------+--------------------+

| 334578 | 666         | ethan         | discipline     | male | application center |

+--------+-------------+---------------+----------------+------+--------------------+

1 row in set (0.00 sec)

可以看到,执行成功。

模拟文初案例,删除用户'root'@'%'后,会发生什么?

1. 删除用户'root'@'%'

[root@ethan_mysql ~]# mysql -uroot -pmysql --socket=/mysqldata/3308/tmp/mysql.sock

mysql> drop user 'root'@'%';

Query OK, 0 rows affected (0.03 sec)

mysql> select host,user from mysql.user;

+-----------+---------------+

| host      | user          |

+-----------+---------------+

| %         | ethan_yang    |

| localhost | mysql.session |

| localhost | mysql.sys     |

| localhost | root          |

+-----------+---------------+

4 rows in set (0.00 sec)

2. 使用'app_user'@'%'用户登录执行查看视图操作

[root@ethan_mysql]#mysql -uapp_user -pmysql -h 10.10.178.112 -P 3308 -D ts_db_02

3. 查看视图ts02_v_i

mysql> select * from ts02_v_i;

+--------+-------------+---------------+----------------+------+--------------------+

| HRID   | EMPLOYEE_ID | EMPLOYEE_NAME | EMPLOYEE_SHORT | SEX  | DEPT_CODE          |

+--------+-------------+---------------+----------------+------+--------------------+

| 334578 | 666         | ethan         | discipline     | male | application center |

+--------+-------------+---------------+----------------+------+--------------------+

1 row in set (0.00 sec)


mysql> show create view ts02_v_i \G

*************************** 1. row ***************************

View: ts02_v_i

Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`%` SQL SECURITY INVOKER VIEW `ts02_v_i` AS select `ts_db_01`.`employee`.`HRID` AS `HRID`,`ts_db_01`.`employee`.`EMPLOYEE_ID` AS `EMPLOYEE_ID`,`ts_db_01`.`employee`.`EMPLOYEE_NAME` AS `EMPLOYEE_NAME`,`ts_db_01`.`employee`.`EMPLOYEE_SHORT` AS `EMPLOYEE_SHORT`,`ts_db_01`.`employee`.`SEX` AS `SEX`,`ts_db_01`.`employee`.`DEPT_CODE` AS `DEPT_CODE` from `ts_db_01`.`employee`

character_set_client: utf8

collation_connection: utf8_general_ci

1 row in set (0.00 sec)

执行成功,此刻虽然没有`root`@`%`,但视图ts02_v_i已经创建,且'app_user'@'%'访问ts02_v_i所需要的视图权限和视图所引用表的select权限均有,所以命令可以执行。

4. 查看视图ts02_v_d

mysql> select * from ts02_v_d;

ERROR 1045 (28000): Access denied for user 'app_user'@'%' (using password: YES)

mysql> show create view ts02_v_d \G

*************************** 1. row ***************************

View: ts02_v_d

Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`%` SQL SECURITY DEFINER VIEW `ts02_v_d` AS select `ts_db_01`.`employee`.`HRID` AS `HRID`,`ts_db_01`.`employee`.`EMPLOYEE_ID` AS `EMPLOYEE_ID`,`ts_db_01`.`employee`.`EMPLOYEE_NAME` AS `EMPLOYEE_NAME`,`ts_db_01`.`employee`.`EMPLOYEE_SHORT` AS `EMPLOYEE_SHORT`,`ts_db_01`.`employee`.`SEX` AS `SEX`,`ts_db_01`.`employee`.`DEPT_CODE` AS `DEPT_CODE` from `ts_db_01`.`employee`

character_set_client: utf8

collation_connection: utf8_general_ci

1 row in set, 1 warning (0.00 sec)

执行成功,此刻虽然没有`root`@`%`,但视图ts02_v_d已经创建,访问机制使用的是SQL SECURITY DEFINER,需使用创建视图时的DEFINER(`root`@`%`)来访问视图。因此时`root`@`%`删除,所以视图ts02_v_d不能被访问。

HK

怎么破? 三种方法

方式1:添加会用户`root`@`%`,并赋原来的权限。

`root`@`%`的删除操作,就是为了防止高权限用户。故此方案被否。

方式2:把SQL SECURITY 的DEFINER改为invoker

mysql> alter ALGORITHM=UNDEFINED DEFINER=`root`@`%` SQL SECURITY invoker VIEW ts_db_02.ts02_v_d AS select * from ts_db_01.employee;

Query OK, 0 rows affected, 1 warning (0.05 sec)

mysql> select * from ts02_v_d;

+--------+-------------+---------------+----------------+------+--------------------+

| HRID   | EMPLOYEE_ID | EMPLOYEE_NAME | EMPLOYEE_SHORT | SEX  | DEPT_CODE          |

+--------+-------------+---------------+----------------+------+--------------------+

| 334578 | 666         | ethan         | discipline     | male | application center |

+--------+-------------+---------------+----------------+------+--------------------+

1 row in set (0.00 sec)

方式3:把DEFINER=`root`@`%` 改为DEFINER=`app_user`@`%`

mysql> alter ALGORITHM=UNDEFINED DEFINER=`app_user`@`%` SQL SECURITY DEFINER VIEW ts_db_02.ts02_v_d AS select * from ts_db_01.employee;

Query OK, 0 rows affected (0.10 sec)

mysql> select * from ts02_v_d;

+--------+-------------+---------------+----------------+------+--------------------+

| HRID   | EMPLOYEE_ID | EMPLOYEE_NAME | EMPLOYEE_SHORT | SEX  | DEPT_CODE          |

+--------+-------------+---------------+----------------+------+--------------------+

| 334578 | 666         | ethan         | discipline     | male | application center |

+--------+-------------+---------------+----------------+------+--------------------+

1 row in set (0.00 sec)

【结论】

1. definer是在定义对象是判断是否有权限,只要创建的用户有权限,那么创建就可以成功,且所有有权限查询该视图的用户也能够成功执行查询语句 ,不管是否拥有该视图所引用对象的权限;

2. invoker是指在查询时验证用户是否有权限执行操作,当然创建时也会判断,如果创建的用户没有视图所引用表对象的访问权限,那创建都会失败。

3. 熟悉oracle的朋友可能联想到了,MySQL的definer/invoker涉及理念与ORACLE中的定义者权限和调用者权限过程非常类似。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值