做MySQL数据库用户权限规范的时候,将原来业务帐号'user'@'%'修改为'user'@'192.168.1.%'时,忽然发现业务系统部分业务报404错误,经排查,发现是因为报错的业务在调用数据库时,使用了视图,而视图的定义者是原来的'user'@'%',并且安全性设置的是definer,现在这个用户不存在了,虽然新用户'user'@'192.168.1.%’拥有和原来相同的数据库权限,但是依然不能使用这些视图。
原因找到了,那么解决办法也就出来了:
一是将所有视图的定义者修改为'user'@'192.168.1.%';
二是将安全性定义由definer设置为invoker。
因为是生产环境,不能随便搞,所以采用的是第一种方法,第二种方法没有再试。
在创建视图或者是存储过程的时候,是需要定义安全验证方式的(也就是安全性SQL SECURITY),其值可以为definer或invoker,表示在执行过程中,使用谁的权限来执行。
definer: 由definer(定义者)指定的用户的权限来执行
invoker: 由调用这个视图(存储过程)的用户的权限来执行
definer
当定义为DEFINER时,必须数据库中存在DEFINER指定的用户,并且该用户拥有对应的操作权限,才能成功执行,与当前用户是否有权限无关。
invoker
当定义为INVOKER时,只要执行者有执行权限,就可以成功执行。
我们在MySQL创建view、trigger、function、procedure、event时都会定义一个Definer='XXX'
加红的部分SQL SECURITY 其实后面有两个选项,一个为DEFINER,一个为INVOKER
SQL SECURITY { DEFINER | INVOKER } :指明谁有权限来执行。
DEFINER 表示按定义者拥有的权限来执行
INVOKER 表示用调用者的权限来执行
默认情况下,系统指定为DEFINER
以存储过程为例:
(1)MySQL存储过程是通过指定SQL SECURITY子句指定执行存储过程的实际用户;
(2)如果SQL SECURITY子句指定为DEFINER,存储过程将使用存储过程的DEFINER执行存储过程,验证调用存储过程的用户是否具有存储过程的execute权限和DEFINER用户是否具有存储过程引用的相关对象的权限;
(3)如果SQL SECURITY子句指定为INVOKER,那么MySQL将使用当前调用存储过程的用户执行此过程,并验证用户是否具有存储过程的execute权限和存储过程引用的相关对象的权限;
(4)如果不显示的指定SQL SECURITY子句,MySQL默认将以DEFINER执行存储过程。
我们来看下面几个小例子。
用root帐号登陆
你会发现系统报错查询不到了,这是因为我们在上述定义的SQL SECURITY值为INVOKER,存储过程执行过程中会以user1具有的权限来执行,其中调用到了mysql的库,而我们的user1帐户只有testdb库的使用权限,所以会返回失败。
我们把上面的invoker改为definer再来试一下
发现可以查询出来了,因为user1对存储过程user_count有执行的权限,虽然它依旧没有权限直接操作mysql库,由于我们定义的SQL SECURITY为DEFINER,所以在执行时是以root的身份执行的,所以可以正常查询出来。
现在在MySQL涉及的definer有view、trigger、function、procedure、event。
一般前期在测试库上开发的缘故,我们经常定义到的definer为`root`@`%`,安全整改,使用其他普通用户,务必要批量修改相关view、trigger、function、procedure、event的definer,否则到出现服务不可用!
MySQL视图定义者和安全性definer/invoker
https://pdf.us/2018/02/24/679.html
MySQL如何修改所有的definer?
https://www.cnblogs.com/zejin2008/p/4767531.html
MySQL error 1449: The user specified as a definer does not exist
https://www.jianshu.com/p/43153801d2af