mysql-logo.gif (3082 bytes)MySQL中文参考手册

译者:晏子 (clyan@sohu.com)      主页:http://linuxdb.yeah.net


第一章, 前一章, 下一章, 最后一章目录.


6 MySQL 存取权限系统

MySQL有一个先进但非标准的安全/权限系统。本节描述它的工作原理。

6.1 权限系统做什么

MySQL权限系统的主要功能是证实连接到一台给定主机的一个用户,并且赋予该用户在一个数据库上selectinsertupdatedelete的权限。

附加的功能包括有一个匿名的用户和对于MySQL特定的功能例如LOAD DATA INFILE进行授权及管理操作的能力。

6.2 MySQL 用户名和口令

MySQL使用用户名和口令的方法与Unix或Windows使用的方式有很多不同之处:

6.3 与MySQL服务器连接

当你想要存取一个MySQL服务器时,MySQL客户程序一般要求你指定连接参数:你想要联接的主机、你的用户名和你的口令。例如,mysql客户可以象这样启动(可选的参数被包括在“[”“]”之间):

shell> mysql [-h host_name][-u user_name][-pyour_pass ]

-h, -u-p选项的另一种形式是--host=host_name--user=user_name--password=your_pass。注意在-p--password=与跟随它后面的口令之间没有空格

注意:在命令行上指定一个口令是不安全的!随后在你系统上的任何用户可以通过打类似这样的命令发现你的口令:ps auxww。见4.15.4 选项文件

对于命令行没有的联接参数,mysql使用缺省值:

这样, 对一个Unix用户joe,下列命令是等价的:

shell>mysql -h localhost -u joe 
shell>mysql -h localhost 
shell>mysql -u joe 
shell>mysql

其它MySQL客户程序有同样表现。

在Unix系统上,当你进行一个连接时,你可以指定要使用的不同的缺省值,这样你不必每次在你调用一个客户程序是在命令行上输入他们。这可以有很多方法做到:

如果连接参数以多种方法被指定,在命令行上被指定的值优先于在配置文件和环境变量中指定的值,而在配置文件指定的值优先于在环境变量指定的值。

6.4 使你的口令安全

以一种暴露的可被其他用户发现的方式指定你的口令是不妥当的。当你运行客户程序时,你可以使用下列方法指定你的口令,还有每个方法的风险评估:

总之,最安全的方法是让客户程序提示口令或在一个适当保护的“.my.cnf”文件中指定口令。

6.5   MySQL提供的权限

权限信息用userdbhosttables_privcolumns_priv表被存储在mysql数据库中(即在名为mysql的数据库中)。在MySQL启动时和在6.9 权限修改何时生效所说的情况时,服务器读入这些数据库表内容。

本手册所用的涉及由MySQL提供的权限名称显示在下表,还有在授权表中每个权限的表列名称和每个权限有关的上下文:

权限 上下文
select Select_priv
insert Insert_priv
update Update_priv
delete Delete_priv
index Index_priv
alter Alter_priv
create Create_priv 数据库、表或索引
drop Drop_priv 数据库或表
grant Grant_priv 数据库或表
references References_priv 数据库或表
reload Reload_priv 服务器管理
shutdown Shutdown_priv 服务器管理
process Process_priv 服务器管理
file File_priv 在服务器上的文件存取

selectinsertupdatedelete权限允许你在一个数据库现有的表上实施操作。

SELECT语句只有在他们真正从一个表中检索行是才需要select权限,你可以执行某个SELECT语句,甚至没有任何到服务器上的数据库里的存取任何东西的许可。例如,你可使用mysql客户作为一个简单的计算器:

mysql> SELECT 1+1;
mysql> SELECT PI()*2;

index权限允许你创建或抛弃(删除)索引。

alter权限允许你使用ALTER TABLE

createdrop权限允许你创建新的数据库和表,或抛弃(删除)现存的数据库和表。

注意:如果你将mysql数据库的drop权限授予一个用户,该用户能抛弃存储了MySQL存取权限的数据库!

grant权限允许你把你自己拥有的那些权限授给其他的用户。

file权限给予你用LOAD DATA INFILESELECT ... INTO OUTFILE语句读和写服务器上的文件,任何被授予这个权限的用户都能读或写MySQL服务器能读或写的任何文件。

其余的权限用于管理性操作,它使用mysqladmin程序实施。下表显示mysqladmin支配每个管理性权限允许你执行的命令:

优惠 权限拥有者允许执行的命令
reload reload, refresh, flush-privileges, flush-hosts, flush-logs, flush-tables
shutdown shutdown
precess processlist, kill

reload命令告诉服务器再读入授权表,refresh命令清洗所有表并打开和关闭记录文件,flush-privilegesreload的一个同义词,其它flush-*命令执行类似refresh的功能,但是范围更有限,并且在某些情况下可能更好用。例如,如果你只是想清洗记录文件,flush-logsrefresh是更好的选择。

shutdown命令关掉服务器。

processlist命令显示在服务器内执行的线程的信息。kill命令杀死服务器线程。你总是能显示或杀死你自己的线程,但是你需要process权限来显示或杀死其他用户启动的线程。

总的说来,只授予权限给需要他们的那些用户是一个好主意,但是你应该在授予某个权限时试验特定的警告:

有一些事情你不能用MySQL权限系统做到:

6.6 权限系统工作原理

MySQL权限系统保证所有的用户可以严格地做他们假定被允许做的事情。当你连接一个MySQL服务器时, 你的身份由你从那连接的主机你指定的用户名来决定,系统根据你的身份和你想做什么来授予权限。

MySQL在认定身份中考虑你的主机名和用户名字,是因为有很小的原因假定一个给定的用户在因特网上属于同一个人。例如,用户从whitehouse.gov连接的bill不必和从mosoft.com连接bill是同一个人。 MySQL通过允许你区分在不同的主机上碰巧有同样名字用户来处理它:你可以对从whitehouse.gov连接授与bill一个权限集,而为从microsoft.com的连接授予一个不同的权限集。

MySQL存取控制包含2个阶段:

服务器在存取控制的两个阶段使用在mysql的数据库中的userdbhost表,在这些授权表中字段如下:

表名称 user db host
范围字段 Host Host Host
User Db Db
Password User
权限字段 Select_priv Select_priv Select_priv
Insert_priv Insert_priv Insert_priv
Update_priv Update_priv Update_priv
Delete_priv Delete_priv Delete_priv
Index_priv Index_priv Index_priv
Alter_priv Alter_priv Alter_priv
Create_priv Create_priv Create_priv
Drop_priv Drop_priv Drop_priv
Grant_priv Grant_priv Grant_priv
Reload_priv
Shutdown_priv
Process_priv
File_priv

对存取控制的第二阶段(请求证实),如果请求涉及表,服务器可以另外参考tables_privcolumns_priv表。这些表的字段如下:

表名称 tables_priv columns_priv
范围字段 Host Host
Db Db
User User
Table_name Table_name
Column_name
权限字段 Table_priv Column_priv
Column_priv
其他字段 Timestamp Timestamp
Grantor

每个授权表包含范围字段和权限字段。

范围字段决定表中每个条目的范围,即,条目适用的上下文。例如, 一个user表条目的HostUser值为'thomas.loc.gov''bob'将被用于证实来自主机thomas.loc.govbob对服务器的连接。同样,一个db表条目的HostUserDb字段的值是'thomas.loc.gov''bob''reports'将用在bob从主机联接thomas.loc.gov存取reports数据库的时候。 tables_privcolumns_priv表包含范围字段,指出每个条目适用的表或表/列的组合。

对于检查存取的用途,比较Host值是忽略大小写的。UserPassword、DbTable_name值是区分大小写的。Column_name值在MySQL3.22.12或以后版本是忽略大小写的。

权限字段指出由一个表条目授予的权限,即,可实施什么操作。服务器组合各种的授权表的信息形成一个用户权限的完整描述。为此使用的规则在6.8 存取控制, 阶段2:请求证实描述。

范围字段是字符串,如下所述;每个字段的缺省值是空字符串:

字段名 类型
Host CHAR(60)
User CHAR(16)
Password CHAR(16)
Db CHAR(64) (tables_privcolumns_priv表为CHAR(60)

userdbhost表中,所有权限字段被声明为ENUM('N','Y')--每一个都可有值'N''Y',并且缺省值是'N'.

tables_privcolumns_priv表中,权限字段被声明为SET字段:

表名 字段名 可能的集合成员
tables_priv Table_priv 'Select', 'Insert', 'Update', 'Delete', 'Create', 'Drop', 'Grant', 'References', 'Index', 'Alter'
tables_priv Column_priv 'Select', 'Insert', 'Update', 'References'
columns_priv Column_priv 'Select', 'Insert', 'Update', 'References'

简单地说,服务器使用这样的授权表:

注意管理权限(reload, shutdown, 等等)仅在user表中被指定。这是因为管理性操作是服务器本身的操作并且不是特定数据库,因此没有理由在其他授权表中列出这样的权限。事实上,只需要请教user表来决定你是否执行一个管理操作。

file权限也仅在user表中指定。它不是管理性权限,但你读或谢在服务器主机上的文件的的能力独立于你正在存取的数据库。

mysqld服务器启动时,读取一次授权表内容。对授权表的更改生效在6.9 权限更改何时生效描述。

当你修改授权表的内容时,确保你按你想要的方式更改权限设置是一个好主意。为帮助诊断问题,见6.13 “存取拒绝引起错误的原因。对于安全问题上的忠告,见6.14 怎么对使MySQL安全对抗解密高手

一个有用的诊断工具是mysqlaccess脚本,由Carlier Yves 提供给MySQL分发。使用--help选项调用mysqlaccess查明它怎样工作。注意:mysqlaccess仅用userdbhost表仅检查存取。它不检查表或列级权限。

6.7 存取控制, 阶段1:连接证实

当你试图联接一个MySQL服务器时,服务器基于你的身份和你是否能通过供应正确的口令验证身份来接受或拒绝连接。如果不是,服务器完全具结你的存取,否则,服务器接受连接,然后进入阶段2并且等待请求。

你的身份基于2个信息:

身份检查使用3个user表(Host, UserPassword)范围字段执行。服务器只有在一个user表条目匹配你的主机名和用户名并且你提供了正确的口令时才接受连接。

user表范围字段可以如下被指定:

非空白Password值代表加密的口令。 MySQL不以任何人可以看的纯文本格式存储口令,相反,正在试图联接的一个用户提供的口令被加密(使用PASSWORD()函数),并且与存储了user表中的已经加密的版本比较。如果他们匹配,口令是正确的。

下面的例子显示出各种user表中HostUser条目的值的组合如何应用于到来的连接:

Host User 被条目匹配的连接
'thomas.loc.gov' 'fred' fred, 从thomas.loc.gov 连接
'thomas.loc.gov' '' 任何用户, 从thomas.loc.gov连接
'%' 'fred' fred, 从任何主机连接
'%' '' 任何用户, 从任何主机连接
'%.loc.gov' 'fred' fred, 从在loc.gov域的任何主机连接
'x.y.%' 'fred' fred, 从x.y.netx.y.com,x.y.edu等联接。(这或许无用)
'144.155.166.177' 'fred' fred, 从有144.155.166.177 IP 地址的主机连接
'144.155.166.%' 'fred' fred, 从144.155.166 C类子网的任何主机连接

既然你能在Host字段使用IP通配符值(例如,'144.155.166.%'匹配在一个子网上的每台主机),有可能某人可能企图探究这种能力,通过命名一台主机为144.155.166.somewhere.com。为了阻止这样的企图,MySQL不允许匹配以数字和一个点起始的主机名,这样,如果你用一个命名为类似1.2.foo.com的主机,它的名字决不会匹配授权表中Host列。只有一个IP数字能匹配IP通配符值。

一个到来的连接可以被在user表中的超过一个条目匹配。例如,一个由fredthomas.loc.gov的连接匹配多个条目如上所述。如果超过一个匹配,服务器怎么选择使用哪个条目呢?服务器在启动时读入user表后通过排序来解决这个问题,然后当一个用户试图连接时,以排序的顺序浏览条目,第一个匹配的条目被使用。

user表排序工作如下,假定user表看起来像这样:

+-----------+----------+-
| Host      | User     | ...
+-----------+----------+-
| %         | root     | ...
| %         | jeffrey  | ...
| localhost | root     | ...
| localhost |          | ...
+-----------+----------+-

当服务器在表中读取时,它以最特定的Host值为先的次序排列('%'Host列里意味着“任何主机”并且是最不特定的)。有相同Host值的条目以最特定的User值为先的次序排列(一个空白User值意味着“任何用户”并且是最不特定的)。最终排序的user表看起来像这样:

+-----------+----------+-
| Host      | User     | ...
+-----------+----------+-
| localhost | root     | ...
| localhost |          | ...
| %         | jeffrey  | ...
| %         | root     | ...
+-----------+----------+-

当一个连接被尝试时,服务器浏览排序的条目并使用找到的第一个匹配。对于由jeffreylocalhost的一个连接,在Host列的'localhost'条目首先匹配。那些有空白用户名的条目匹配连接的主机名和用户名。('%'/'jeffrey'条目也将匹配,但是它不是在表中的第一匹配。)

这是另外一个例子。假定user桌子看起来像这样:

+----------------+----------+-
| Host           | User     | ...
+----------------+----------+-
| %              | jeffrey  | ...
| thomas.loc.gov |          | ...
+----------------+----------+-

排序后的表看起来像这样:

+----------------+----------+-
| Host           | User     | ...
+----------------+----------+-
| thomas.loc.gov |          | ...
| %              | jeffrey  | ...
+----------------+----------+-

一个由jeffreythomas.loc.gov的连接被第一个条目匹配,而一个由jeffreywhitehouse.gov的连接被第二个匹配。

普遍的误解是认为,对一个给定的用户名,当服务器试图对连接寻找匹配时,明确命名那个用户的所有条目将首先被使用。这明显不是事实。先前的例子说明了这点,在那里一个由jeffreythomas.loc.gov的连接没被包含'jeffrey'作为User字段值的条目匹配,但是由没有用户名的题目匹配!

如果你有服务器连接的问题,打印出user表并且手工排序它看看第一个匹配在哪儿进行。

6.8 存取控制,阶段2:请求证实

一旦你建立了一个连接,服务器进入阶段2。对在此连接上进来的每个请求,服务器检查你是否有足够的权限来执行它,它基于你希望执行的操作类型。这正是在授权表中的权限字段发挥作用的地方。这些权限可以来子userdbhosttables_privcolumns_priv表的任何一个。授权表用GRANTREVOKE命令操作。见7.26 GRANTREVOKE 句法。(你可以发觉参考6.6 权限系统怎样工作很有帮助,它列出了在每个权限表中呈现的字段。)

user表在一个全局基础上授予赋予你的权限,该权限不管当前的数据库是什么均适用。例如,如果user表授予你delete权限, 你可以删除在服务器主机上从任何数据库删除行!换句话说,user表权限是超级用户权限。只把user表的权限授予超级用户如服务器或数据库主管是明智的。对其他用户,你应该把在user表中的权限设成'N'并且仅在一个特定数据库的基础上授权, 使用dbhost表。

dbhost表授予数据库特定的权限。在范围字段的值可以如下被指定:

dbhost表在服务器启动时被读取和排序(同时它读user表)。db表在HostDbUser范围字段上排序,并且host表在HostDb范围字段上排序。对于user表,排序首先放置最特定的值然后最后最不特定的值,并且当服务器寻找匹配入条目时,它使用它找到的第一个匹配。

tables_privcolumns_priv表授予表和列特定的权限。在范围字段的值可以如下被指定:

tables_privcolumns_priv表在HostDbUser字段上被排序。这类似于db表的排序,尽管因为只有Host字段可以包含通配符,但排序更简单。

请求证实进程在下面描述。(如果你熟悉存取检查的源代码,你会注意到这里的描述与在代码使用的算法略有不同。描述等价于代码实际做的东西;它只是不同于使解释更简单。)

对管理请求(shutdownreload等等),服务器仅检查user表条目,因为那是唯一指定管理权限的表。如果条目许可请求的操作,存取被授权了,否则拒绝。例如,如果你想要执行mysqladmin shutdown,但是你的user表条目没有为你授予shutdown权限,存取甚至不用检查dbhost表就被拒绝。(因为他们不包含Shutdown_priv行列,没有这样做的必要。)

对数据库有关的请求(insertupdate等等),服务器首先通过查找user表条目来检查用户的全局(超级用户)权限。如果条目允许请求的操作,存取被授权。如果在user表中全局权限不够,服务器通过检查dbhost表确定特定的用户数据库权限:

  1. 服务器在db表的HostDbUser字段上查找一个匹配。 HostUser对应连接用户的主机名和MySQL用户名。Db字段对应用户想要存取的数据库。如果没有HostUser的条目,存取被拒绝。
  2. 如果db表中的条目有一个匹配而且它的Host字段不是空白的,该条目定义用户的数据库特定的权限。
  3. 如果匹配的db表的条目的Host字段是空白的,它表示host表列举主机应该被允许存取数据库的主机。在这种情况下,在host表中作进一步查找以发现HostDb字段上的匹配。如果没有host表条目匹配,存取被拒绝。如果有匹配,用户数据库特定的权限以在dbhost表的条目的权限,即在两个条目都是'Y'的权限的交集(而不是并集!)计算。(这样你可以授予在db表条目中的一般权限,然后用host表条目按一个主机一个主机为基础地有选择地限制它们。)

在确定了由dbhost表条目授予的数据库特定的权限后,服务器把他们加到由user表授予的全局权限中。如果结果允许请求的操作,存取被授权。否则,服务器检查在tables_privcolumns_priv表中的用户的表和列权限并把它们加到用户权限中。基于此结果允许或拒绝存取。

用布尔术语表示,前面关于一个用户权限如何计算的描述可以这样总结:

global privileges
OR (database privileges AND host privileges)
OR table privileges
OR column privileges

它可能不明显,为什么呢,如果全局user条目的权限最初发现对请求的操作不够,服务器以后把这些权限加到数据库、表和列的特定权限。原因是一个请求可能要求超过一种类型的权限。例如,如果你执行一个INSERT ... SELECT语句,你就都要insertselect权限。你的权限必须如此以便user表条目授予一个权限而db表条目授予另一个。在这种情况下,你有必要的权限执行请求,但是服务器不能自己把两个表区别开来;两个条目授予的权限必须组合起来。

host表能被用来维护一个“安全”服务器列表。在TcX,host表包含一个在本地的网络上所有的机器的表,这些被授予所有的权限。

你也可以使用host表指定安全的主机。假定你有一台机器public.your.domain,它位于你不认为是安全的一个公共区域,你可以用下列的host表条目子允许除了那台机器外的网络上所有主机的存取:

+--------------------+----+-
| Host               | Db | ...
+--------------------+----+-
| public.your.domain | %  | ... (所有权限设为 'N')
| %.your.domain      | %  | ... (所有权限设为 'Y')
+--------------------+----+-

当然,你应该总是测试你在授权表中的条目(例如,使用mysqlaccess)让你确保你的存取权限实际上以你认为的方式被设置。

6.9 权限更改何时生效

mysqld启动时,所有的授权表内容被读进存储器并且从那点生效。

GRANT、REVOKE或SET PASSWORD对授权表施行的修改会立即被服务器注意到。

如果你手工地修改授权表(使用INSERT、UPDATE等等),你应该执行一个FLUSH PRIVILEGES语句或运行mysqladmin flush-privileges告诉服务器再装载授权表,否则你的改变将不生效,除非你重启服务器。

当服务器注意到授权表被改变了时,现存的客户连接有如下影响:

全局权限的改变和口令改变在下一次客户连接时生效。

6.10 建立初始的MySQL权限

在安装MySQL后,你通过运行scripts/mysql_install_db安装初始的存取权限。见4.7.1 快速安装概述scripts/mysql_install_db脚本启动mysqld服务器,然后初始化授权表,包含下列权限集合:

注意:对Win32的初始权限是不同的。见4.12.4 在Win32上运行MySQL

既然你的安装初始时广开大门,你首先应该做的事情之一是为MySQL root用户指定一个口令。你可以做如下(注意,你使用PASSWORD()函数指定口令):

shell> mysql -u root mysql
mysql> UPDATE user SET Password=PASSWORD('new_password')
           WHERE user='root';
mysql> FLUSH PRIVILEGES;

MySQL 3.22和以上版本中,你可以使用SET PASSWORD语句:

shell> mysql -u root mysql
mysql> SET PASSWORD FOR root=PASSWORD('new_password');

设置口令的另一种方法是使用mysqladmin命令:

shell> mysqladmin -u root password new_password 

注意:如果你使用第一种方法在user表里直接更新口令,你必须告诉服务器再次读入授权表(用FLUSH PRIVILEGES),因为否则改变将不被注意到。

一旦root口令被设置,此后当你作为root与服务器连接时,你必须供应那个口令。

你可能希望让root口令为空白以便当你施行附加的安装时,你不需要指定它或测试,但是保证在任何真实的生产工作中使用你的安装之前,设置它。

看看scripts/mysql_install_db脚本,看它如何安装缺省的权限。你可用它作为一个研究如何增加其他用户的基础。

如果你想要初始的权限不同于上面描述的那些,在你运行mysql_install_db之前,你可以修改它。

为了完全重建权限表,删除在包含mysql数据库的目录下所有“*.frm”,“*.MYI”“*.MYD”文件。(这是在数据库目录下面命名为“mysql”的目录,当你运行mysqld --help时,它被列出。)然后运行mysql_install_db脚本,可能在首先编辑它拥有你想要的权限之后。

注意:对于比MySQL 3.22.10旧的版本,你不应该删除“*.frm”文件。如果你偶然做了,你应该在运行mysql_install_db之前你的MySQL分发中拷回它们。

6.11 向MySQL增加新用户权限

你可以有2个不同的方法增加用户:通过使用GRANT语句或通过直接操作MySQL授权表。比较好的方法是使用GRANT语句,因为他们是更简明并且好像错误少些。

下面的例子显示出如何使用mysql客户安装新用户。这些例子假定权限根据以前的章节描述的缺省被安装。这意味着为了改变,你必须在mysqld正在运行同一台机器上,你必须作为MySQL root用户连接,并且root用户必须对mysql数据库有insert权限和reload管理权限。另外,如果你改变了root用户口令,你必须如下的mysql命令指定它。

你可以通过发出GRANT语句增加新用户:

shell> mysql --user=root mysql
mysql> GRANT ALL PRIVILEGES ON *.* TO monty@localhost
           IDENTIFIED BY 'something' WITH GRANT OPTION;
mysql> GRANT ALL PRIVILEGES ON *.* TO monty@"%"
           IDENTIFIED BY 'something' WITH GRANT OPTION;
mysql> GRANT RELOAD,PROCESS ON *.* TO admin@localhost;
mysql> GRANT USAGE ON *.* TO dummy@localhost;

这些GRANT语句安装3个新用户:

monty
可以从任何地方连接服务器的一个完全的超级用户,但是必须使用一个口令('something'做这个。注意,我们必须对monty@localhostmonty@"%"发出GRANT语句。如果我们增加localhost条目,对localhost的匿名用户条目在我们从本地主机连接接时由mysql_install_db创建的条目将优先考虑,因为它有更特定的Host字段值,所以以user表排列顺序看更早到来。
admin
可以从localhost没有一个口令进行连接并且被授予reloadprocess管理权限的用户。这允许用户执行mysqladmin reloadmysqladmin refreshmysqladmin flush-*命令,还有mysqladmin processlist。没有授予数据库有关的权限。他们能在以后通过发出另一个GRANT语句授权。
dummy
可以不用一个口令连接的一个用户,但是只能从本地主机。全局权限被设置为'N'--USAGE权限类型允许你无需权限就可设置一个用户。它假定你将在以后授予数据库相关的权限。

你也可以直接通过发出INSERT语句增加同样的用户存取信息,然后告诉服务器再次装入授权表:

shell> mysql --user=root mysql
mysql> INSERT INTO user VALUES('localhost','monty',PASSWORD('something'),
                'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y')
mysql> INSERT INTO user VALUES('%','monty',PASSWORD('something'),
                'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y')
mysql> INSERT INTO user SET Host='localhost',User='admin',
                 Reload_priv='Y', Process_priv='Y';
mysql> INSERT INTO user (Host,User,Password)
                        VALUES('localhost','dummy','');
mysql> FLUSH PRIVILEGES;

取决于你的MySQL版本,对上述,你可能必须使用一个不同数目'Y'值(在3.22.11以前的版本有更少的权限列)。对admin用户,只用在3.22.11开始的版本具有的更加可读的INSERT扩充的语法。

注意,为了设置一个超级用户,你只需创造一个user表条目,其权限字段设为'Y'。不需要dbhost表的条目。

user表中的权限列不是由最后一个INSERT语句明确设置的(对dummy用户),因此那些列被赋予缺省值'N'。这是GRANT USAGE做的同样的事情。

下列例子增加一个用户custom,他能从主机localhostserver.domainwhitehouse.gov连接。他只想要从localhost存取bankaccount数据库,从whitehouse.gov存取expenses数据库和从所有3台主机存取customer数据库。他想要从所有3台主机上使用口令stupid

为了使用GRANT语句设置个用户的权限,运行这些命令:

shell> mysql --user=root mysql
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
           ON bankaccount.*
           TO custom@localhost
           IDENTIFIED BY 'stupid';
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
           ON expenses.*
           TO custom@whitehouse.gov
           IDENTIFIED BY 'stupid';
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
           ON customer.*
           TO custom@'%'
           IDENTIFIED BY 'stupid';

通过直接修改授权表设置用户权限,运行这些命令(注意,在结束时FLUSH PRIVILEGES):

shell> mysql --user=root mysql
mysql> INSERT INTO user (Host,User,Password)
       VALUES('localhost','custom',PASSWORD('stupid'));
mysql> INSERT INTO user (Host,User,Password)
       VALUES('server.domain','custom',PASSWORD('stupid'));
mysql> INSERT INTO user (Host,User,Password)
       VALUES('whitehouse.gov','custom',PASSWORD('stupid'));
mysql> INSERT INTO db
       (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,
        Create_priv,Drop_priv)
       VALUES
       ('localhost','bankaccount','custom','Y','Y','Y','Y','Y','Y');
mysql> INSERT INTO db
       (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,
        Create_priv,Drop_priv)
       VALUES
       ('whitehouse.gov','expenses','custom','Y','Y','Y','Y','Y','Y');
mysql> INSERT INTO db
       (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,
        Create_priv,Drop_priv)
       VALUES('%','customer','custom','Y','Y','Y','Y','Y','Y');
mysql> FLUSH PRIVILEGES;

头3个INSERT语句增加user表条目,允许用户custom用给定口令从不同的主机进行连接,但是没有授予任何许可(所有权限被设置为缺省值'N')。后3个INSERT语句增加db表条目,授予custombankaccountexpensescustomer数据库权限,但是只能在从正确的主机存取时。通常,在授权表直接被修改时,服务器必须被告知再次装入他们(用FLUSH PRIVILEGES)以便使权限修改生效。

如果你想要给特定的用户从一个给定的域上的任何机器上存取权限,你可以发出一个如下的GRANT语句:

mysql> GRANT ...
           ON *.*
           TO myusername@"%.mydomainname.com"
           IDENTIFIED BY 'mypassword';

为了通过直接修改授权表做同样的事情,这样做:

mysql> INSERT INTO user VALUES ('%.mydomainname.com', 'myusername',
           PASSWORD('mypassword'),...);
mysql> FLUSH PRIVILEGES;

你也可以使用xmysqladminmysql_webadmin甚至xmysql在授权表中插入、改变和更新值。你可以在MySQLContrib目录找到这些实用程序。

6.12 怎样设置口令

在前面小节的例子里说明了一个重要的原则:当你使用INSERTUPDATE语句存储一个非空的口令时,你必须使用PASSWORD()函数加密它。这是因为在user表中以加密形式存储口令,而不是作为纯文本。如果你忘记这个事实,你可能像这样试图设置口令:

shell> mysql -u root mysql 
mysql> INSERT INTO user (Host,User,Password) VALUES('%','jeffrey','biscuit'); 
mysql> FLUSH PRIVILEGES

结果是纯文本值'biscuit'作为口令被存储在user表中。在用户jeffrey试图用这个口令连接服务器时,mysql客户用PASSWORD()加密它并且将结果送给服务器,服务器比较在user表中的值(它是纯文本值'biscuit')和加密的口令(而不是 'biscuit'),比较失败并且服务器拒绝连接:

shell> mysql -u jeffrey -pbiscuit test
Access denied

因为当他们被插入user表时,口令必须被加密,相反,INSERT语句应该象这样被指定:

mysql> INSERT INTO user (Host,User,Password)
       VALUES('%','jeffrey',PASSWORD('biscuit'));

当你使用SET PASSWORD语句时,你也必须使用PASSWORD()函数:

mysql> SET PASSWORD FOR jeffrey@"%" = PASSWORD('biscuit'); 

如果你使用GRANT ... IDENTIFIED BY语句或mysqladmin password命令设置口令,PASSWORD()函数是不必要的。他们都考虑到为你加密口令,多以你可像这样指定一个口令'biscuit'

mysql> GRANT USAGE ON *.* TO jeffrey@"%" IDENTIFIED BY 'biscuit';

shell> mysqladmin -u jeffrey password biscuit 

注意: PASSWORD()不是以在Unix口令加密的同样方法施行口令加密。你不应该假定如果你的Unix口令和你的MySQL口令是一样的,PASSWORD()将导致与在Unix口令文件被存储的同样的加密值。见6.2 MySQL 用户名和口令

6.13 Access denied错误的原因

当你试着联接MySQL服务器时,如果你碰到Access denied错误,显示在下面的表指出一些你能用来更正这个问题的动作:

6.14 怎样使MySQL安全以对抗解密高手

当你连接一个MySQL服务器时,你通常应该使用一个口令。口令不以明文在连接上传输。

所有其它信息作为能被任何人读懂的文本被传输。如果你担心这个,你可使用压缩协议(MySQL3.22和以上版本)使事情变得更难。甚至为了使一切更安全,你应该安装ssh(见http://www.cs.hut.fi/ssh)。用它,你能在一个MySQL服务器与一个MySQL客户之间得到一个加密的TCP/IP连接。

为了使一个MySQL系统安全,强烈要求你考虑下列建议:

下列mysqld选项影响安全:

--secure
gethostbyname()系统调用返回的IP数字被检查,确保他们解析回到原来的主机名。这对某些外人通过模仿其它主机获得存取权限变得更难。这个选项也增加一些聪明的主机名检查。在MySQL3.21里,选择缺省是关掉的,因为它有时它花很长时间执行反向解析。MySQL 3.22缓存主机名并缺省地启用了这个选项。
--skip-grant-tables
这个选项导致服务器根本不使用权限系统。这给每个人以完全存取所有的数据库的权力!(通过执行mysqladmin reload,你能告诉一个正在运行的服务器再次开始使用授权表。)
--skip-name-resolve
主机名不被解析。所有在授权表的Host的列值必须是IP数字或localhost
--skip-networking
在网络上不允许TCP/IP连接。所有到mysqld的连接必须经由Unix套接字进行。这个选项对使用MIT-pthreads的系统是不合适的,因为MIT-pthreads包不支持Unix套接字。

第一章, 前一章, 下一章, 最后一章目录