MySQL 角色是具名的權限集合。與使用者帳戶一樣,可以授予角色權限,也可以從角色撤銷權限。
可以將角色授予使用者帳戶,這會將與每個角色相關聯的權限授予該帳戶。這可以將權限集指派給帳戶,並為概念化所需的權限指派和實作提供方便的替代方案,而非授予個別權限。
以下列表摘要說明 MySQL 提供的角色管理功能
CREATE ROLE
和DROP ROLE
建立和移除角色。SHOW GRANTS
顯示使用者帳戶和角色的權限和角色指派。SET DEFAULT ROLE
指定預設啟用的帳戶角色。SET ROLE
變更目前工作階段中的啟用角色。CURRENT_ROLE()
函式會顯示目前工作階段中的啟用角色。mandatory_roles
和activate_all_roles_on_login
系統變數可定義強制角色,以及當使用者登入伺服器時自動啟用已授予的角色。
如需個別角色操作陳述式(包括使用它們所需的權限)的說明,請參閱第 15.7.1 節,〈帳戶管理陳述式〉。以下討論提供角色使用範例。除非另有指定,否則此處顯示的 SQL 陳述式應使用具有足夠管理權限的 MySQL 帳戶執行,例如 root
帳戶。
考慮以下情境
應用程式使用名為
app_db
的資料庫。與應用程式相關聯的,可能會有開發人員的帳戶,以建立和維護應用程式,以及與應用程式互動的使用者帳戶。
開發人員需要對資料庫的完整存取權。部分使用者只需要讀取權限,其他使用者則需要讀取/寫入權限。
為了避免個別授予權限給可能為數眾多的使用者帳戶,請建立角色作為所需權限集的名稱。這可以透過授予適當的角色,輕鬆地將所需的權限授予使用者帳戶。
若要建立角色,請使用 CREATE ROLE
陳述式
CREATE ROLE 'app_developer', 'app_read', 'app_write';
角色名稱很像使用者帳戶名稱,由使用者部分和主機部分組成,格式為 '
。如果省略主機部分,則預設為 user_name
'@'host_name
''%'
。除非使用者和主機部分包含特殊字元(如 -
或 %
),否則可以不用加上引號。與帳戶名稱不同,角色名稱的使用者部分不能為空。如需更多資訊,請參閱第 8.2.5 節「指定角色名稱」。
若要將權限指派給角色,請執行 GRANT
陳述式,其語法與將權限指派給使用者帳戶的語法相同。
GRANT ALL ON app_db.* TO 'app_developer';
GRANT SELECT ON app_db.* TO 'app_read';
GRANT INSERT, UPDATE, DELETE ON app_db.* TO 'app_write';
現在假設您最初需要一個開發人員帳戶、兩個需要唯讀權限的使用者帳戶,以及一個需要讀/寫權限的使用者帳戶。使用 CREATE USER
建立帳戶。
CREATE USER 'dev1'@'localhost' IDENTIFIED BY 'dev1pass';
CREATE USER 'read_user1'@'localhost' IDENTIFIED BY 'read_user1pass';
CREATE USER 'read_user2'@'localhost' IDENTIFIED BY 'read_user2pass';
CREATE USER 'rw_user1'@'localhost' IDENTIFIED BY 'rw_user1pass';
若要為每個使用者帳戶指派其所需的權限,您可以使用與前面所示形式相同的 GRANT
陳述式,但這需要列舉每個使用者的個別權限。請改用另一種 GRANT
語法,允許授予角色而不是權限。
GRANT 'app_developer' TO 'dev1'@'localhost';
GRANT 'app_read' TO 'read_user1'@'localhost', 'read_user2'@'localhost';
GRANT 'app_read', 'app_write' TO 'rw_user1'@'localhost';
用於 rw_user1
帳戶的 GRANT
陳述式會授予讀取和寫入角色,這些角色組合起來可提供所需的讀取和寫入權限。
將角色授予帳戶的 GRANT
語法與授予權限的語法不同:有一個 ON
子句可用於指派權限,而沒有 ON
子句可用於指派角色。由於語法不同,您無法在同一個陳述式中混合指派權限和角色。(允許將權限和角色都指派給帳戶,但您必須使用個別的 GRANT
陳述式,每個陳述式都具有適用於要授予內容的語法。)無法將角色授予匿名使用者。
建立角色時,該角色會處於鎖定狀態,沒有密碼,並指派預設的驗證外掛程式。(這些角色屬性稍後可以透過 ALTER USER
陳述式變更,由具有全域 CREATE USER
權限的使用者變更。)
在鎖定狀態下,無法使用角色向伺服器驗證。如果解除鎖定,則可以使用角色進行驗證。這是因為角色和使用者都是授權識別碼,兩者之間有很多共同之處,區別很小。另請參閱使用者和角色互換性。
可以透過在 mandatory_roles
系統變數的值中命名角色,將角色指定為強制角色。伺服器會將強制角色視為已授予所有使用者,因此無需明確將其授予任何帳戶。
若要在伺服器啟動時指定強制角色,請在伺服器 my.cnf
檔案中定義 mandatory_roles
。
[mysqld]
mandatory_roles='role1,role2@localhost,r3@%.example.com'
若要在執行階段設定並持久化 mandatory_roles
,請使用類似這樣的陳述式。
SET PERSIST mandatory_roles = 'role1,role2@localhost,r3@%.example.com';
SET PERSIST
設定執行中的 MySQL 執行個體的值。它也會儲存值,使其在後續伺服器重新啟動時繼續生效。若要變更執行中的 MySQL 執行個體的值,而不使其在後續重新啟動時繼續生效,請使用 GLOBAL
關鍵字,而不是 PERSIST
。請參閱第 15.7.6.1 節「用於變數指派的 SET 語法」。
設定 mandatory_roles
除了需要通常設定全域系統變數所需的 SYSTEM_VARIABLES_ADMIN
權限(或已棄用的 SUPER
權限)之外,還需要 ROLE_ADMIN
權限。
與明確授予的角色一樣,強制角色必須先啟用,才會生效(請參閱啟用角色)。在登入時,如果啟用 activate_all_roles_on_login
系統變數,則會針對所有授予的角色啟用角色,否則會針對設定為預設角色的角色啟用。在執行階段,SET ROLE
會啟用角色。
無法使用 REVOKE
撤銷在 mandatory_roles
值中命名的角色,也無法使用 DROP ROLE
或 DROP USER
刪除。
為了防止工作階段預設成為系統工作階段,具有 SYSTEM_USER
權限的角色不能列在 mandatory_roles
系統變數的值中。
如果在啟動時將具有
SYSTEM_USER
權限的角色指派給mandatory_roles
,則伺服器會將訊息寫入錯誤記錄檔並結束。如果在執行階段將具有
SYSTEM_USER
權限的角色指派給mandatory_roles
,則會發生錯誤,且mandatory_roles
的值會保持不變。
即使有此保護措施,為了防範權限提升的可能性,最好還是避免透過角色授予 SYSTEM_USER
權限。
如果 mandatory_roles
中命名的角色不在 mysql.user
系統資料表中,則不會將該角色授予使用者。當伺服器嘗試為使用者啟用角色時,它不會將不存在的角色視為強制角色,並將警告寫入錯誤記錄檔。如果稍後建立角色並因此變為有效,則可能需要使用 FLUSH PRIVILEGES
,才能使伺服器將其視為強制角色。
SHOW GRANTS
會根據第 15.7.7.22 節「SHOW GRANTS 陳述式」中所述的規則顯示強制角色。
若要驗證指派給帳戶的權限,請使用 SHOW GRANTS
。例如
mysql> SHOW GRANTS FOR 'dev1'@'localhost';
+-------------------------------------------------+
| Grants for dev1@localhost |
+-------------------------------------------------+
| GRANT USAGE ON *.* TO `dev1`@`localhost` |
| GRANT `app_developer`@`%` TO `dev1`@`localhost` |
+-------------------------------------------------+
但是,這會顯示每個授予的角色,而不會將其「展開」為角色所代表的權限。若要同時顯示角色權限,請新增一個 USING
子句,其中命名要顯示權限的授予角色。
mysql> SHOW GRANTS FOR 'dev1'@'localhost' USING 'app_developer';
+----------------------------------------------------------+
| Grants for dev1@localhost |
+----------------------------------------------------------+
| GRANT USAGE ON *.* TO `dev1`@`localhost` |
| GRANT ALL PRIVILEGES ON `app_db`.* TO `dev1`@`localhost` |
| GRANT `app_developer`@`%` TO `dev1`@`localhost` |
+----------------------------------------------------------+
以類似方式驗證其他類型的使用者。
mysql> SHOW GRANTS FOR 'read_user1'@'localhost' USING 'app_read';
+--------------------------------------------------------+
| Grants for read_user1@localhost |
+--------------------------------------------------------+
| GRANT USAGE ON *.* TO `read_user1`@`localhost` |
| GRANT SELECT ON `app_db`.* TO `read_user1`@`localhost` |
| GRANT `app_read`@`%` TO `read_user1`@`localhost` |
+--------------------------------------------------------+
mysql> SHOW GRANTS FOR 'rw_user1'@'localhost' USING 'app_read', 'app_write';
+------------------------------------------------------------------------------+
| Grants for rw_user1@localhost |
+------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `rw_user1`@`localhost` |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `app_db`.* TO `rw_user1`@`localhost` |
| GRANT `app_read`@`%`,`app_write`@`%` TO `rw_user1`@`localhost` |
+------------------------------------------------------------------------------+
SHOW GRANTS
會根據第 15.7.7.22 節「SHOW GRANTS 陳述式」中所述的規則顯示強制角色。
授予使用者帳戶的角色可以在帳戶工作階段中處於啟用或非啟用狀態。如果授予的角色在工作階段中處於啟用狀態,則其權限會生效;否則,權限不會生效。若要判斷哪些角色在目前工作階段中處於啟用狀態,請使用 CURRENT_ROLE()
函式。
依預設,將角色授予帳戶或在 mandatory_roles
系統變數值中命名角色並不會自動使角色在帳戶工作階段中變成啟用狀態。例如,由於在前面的討論中,沒有啟用任何 rw_user1
角色,如果您以 rw_user1
的身分連線至伺服器,並叫用 CURRENT_ROLE()
函式,則結果為 NONE
(沒有啟用的角色)。
mysql> SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE |
+----------------+
若要指定每次使用者連線至伺服器並驗證時應啟用的角色,請使用 SET DEFAULT ROLE
。若要將預設值設定為先前建立的每個帳戶的所有指派角色,請使用此陳述式。
SET DEFAULT ROLE ALL TO
'dev1'@'localhost',
'read_user1'@'localhost',
'read_user2'@'localhost',
'rw_user1'@'localhost';
現在,如果您以 rw_user1
的身分連線,則 CURRENT_ROLE()
的初始值會反映新的預設角色指派。
mysql> SELECT CURRENT_ROLE();
+--------------------------------+
| CURRENT_ROLE() |
+--------------------------------+
| `app_read`@`%`,`app_write`@`%` |
+--------------------------------+
若要讓所有明確授予的角色和強制角色在使用者連線至伺服器時自動啟用,請啟用 activate_all_roles_on_login
系統變數。依預設,會停用自動角色啟用。
在工作階段中,使用者可以執行 SET ROLE
來變更啟用的角色集。例如,對於 rw_user1
。
mysql> SET ROLE NONE; SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE |
+----------------+
mysql> SET ROLE ALL EXCEPT 'app_write'; SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| `app_read`@`%` |
+----------------+
mysql> SET ROLE DEFAULT; SELECT CURRENT_ROLE();
+--------------------------------+
| CURRENT_ROLE() |
+--------------------------------+
| `app_read`@`%`,`app_write`@`%` |
+--------------------------------+
第一個 SET ROLE
陳述式會停用所有角色。第二個陳述式會使 rw_user1
實際上變成唯讀。第三個陳述式會還原預設角色。
用於儲存的程式和檢視物件的有效使用者會受到 DEFINER
和 SQL SECURITY
屬性的約束,這些屬性會決定執行發生在叫用者還是定義者內容中(請參閱第 27.6 節「儲存物件存取控制」)。
在叫用者內容中執行的儲存程式和檢視物件會使用目前工作階段中啟用的角色執行。
在定義者內容中執行的儲存程式和檢視物件會使用其
DEFINER
屬性中命名的使用者的預設角色執行。如果啟用activate_all_roles_on_login
,則此類物件會使用授予DEFINER
使用者的所有角色執行,包括強制角色。對於儲存的程式,如果應該使用不同於預設值的角色執行,則程式本體可以執行SET ROLE
來啟用所需的角色。這必須謹慎處理,因為指派給角色的權限可能會變更。
正如角色可以授予帳戶一樣,也可以從帳戶撤銷角色。
REVOKE role FROM user;
在 mandatory_roles
系統變數值中指定的角色無法撤銷。
REVOKE
也可應用於角色,以修改授予該角色的權限。這不僅影響角色本身,也會影響任何被授予該角色的帳戶。假設您想要暫時將所有應用程式使用者設為唯讀。為此,請使用 REVOKE
從 app_write
角色撤銷修改權限
REVOKE INSERT, UPDATE, DELETE ON app_db.* FROM 'app_write';
如此一來,該角色將完全沒有任何權限,這可以使用 SHOW GRANTS
來查看(這也說明了此陳述式可以用於角色,而不僅僅是用於使用者)
mysql> SHOW GRANTS FOR 'app_write';
+---------------------------------------+
| Grants for app_write@% |
+---------------------------------------+
| GRANT USAGE ON *.* TO `app_write`@`%` |
+---------------------------------------+
由於從角色撤銷權限會影響任何被指派該修改後角色的使用者的權限,rw_user1
現在沒有任何表格修改權限(INSERT
、UPDATE
和 DELETE
不再存在)
mysql> SHOW GRANTS FOR 'rw_user1'@'localhost'
USING 'app_read', 'app_write';
+----------------------------------------------------------------+
| Grants for rw_user1@localhost |
+----------------------------------------------------------------+
| GRANT USAGE ON *.* TO `rw_user1`@`localhost` |
| GRANT SELECT ON `app_db`.* TO `rw_user1`@`localhost` |
| GRANT `app_read`@`%`,`app_write`@`%` TO `rw_user1`@`localhost` |
+----------------------------------------------------------------+
實際上,rw_user1
這個讀寫使用者已變成唯讀使用者。對於任何其他被授予 app_write
角色的帳戶也會發生同樣的情況,這說明了使用角色如何使修改個別帳戶的權限變得不必要。
要將修改權限還原給角色,只需重新授予它們
GRANT INSERT, UPDATE, DELETE ON app_db.* TO 'app_write';
現在,rw_user1
再次具有修改權限,任何其他被授予 app_write
角色的帳戶也一樣。
要刪除角色,請使用 DROP ROLE
DROP ROLE 'app_read', 'app_write';
刪除角色會從每個被授予該角色的帳戶中撤銷該角色。
在 mandatory_roles
系統變數值中指定的角色無法刪除。
正如先前在 SHOW GRANTS
中暗示的那樣,它會顯示使用者帳戶或角色的權限,帳戶和角色可以互換使用。
角色和使用者之間的一個區別是,CREATE ROLE
會建立預設為鎖定的授權識別符,而 CREATE USER
會建立預設為解鎖的授權識別符。您應該記住,這種區別並非不可改變;具有適當權限的使用者可以在角色或(其他)使用者建立後鎖定或解鎖它們。
如果資料庫管理員偏好特定授權識別符必須是角色,則可以使用命名方案來傳達此意圖。例如,您可以為所有您打算作為角色的授權識別符使用 r_
前綴。
角色和使用者之間的另一個區別在於管理它們的可用權限
CREATE ROLE
和DROP ROLE
權限僅分別允許使用CREATE ROLE
和DROP ROLE
陳述式。CREATE USER
權限允許使用ALTER USER
、CREATE ROLE
、CREATE USER
、DROP ROLE
、DROP USER
、RENAME USER
和REVOKE ALL PRIVILEGES
陳述式。
因此,CREATE ROLE
和 DROP ROLE
權限不如 CREATE USER
強大,可以授予那些只允許建立和刪除角色,而不能執行更一般帳戶操作的使用者。
關於使用者和角色的權限和互換性,您可以將使用者帳戶視為角色,並將該帳戶授予另一個使用者或角色。這樣做的效果是將該帳戶的權限和角色授予另一個使用者或角色。
這組陳述式說明您可以將使用者授予使用者、將角色授予使用者、將使用者授予角色,或將角色授予角色
CREATE USER 'u1';
CREATE ROLE 'r1';
GRANT SELECT ON db1.* TO 'u1';
GRANT SELECT ON db2.* TO 'r1';
CREATE USER 'u2';
CREATE ROLE 'r2';
GRANT 'u1', 'r1' TO 'u2';
GRANT 'u1', 'r1' TO 'r2';
在每種情況下,結果都是將與被授予物件相關聯的權限授予被授予者物件。執行這些陳述式後,u2
和 r2
都已從使用者 (u1
) 和角色 (r1
) 獲得權限
mysql> SHOW GRANTS FOR 'u2' USING 'u1', 'r1';
+-------------------------------------+
| Grants for u2@% |
+-------------------------------------+
| GRANT USAGE ON *.* TO `u2`@`%` |
| GRANT SELECT ON `db1`.* TO `u2`@`%` |
| GRANT SELECT ON `db2`.* TO `u2`@`%` |
| GRANT `u1`@`%`,`r1`@`%` TO `u2`@`%` |
+-------------------------------------+
mysql> SHOW GRANTS FOR 'r2' USING 'u1', 'r1';
+-------------------------------------+
| Grants for r2@% |
+-------------------------------------+
| GRANT USAGE ON *.* TO `r2`@`%` |
| GRANT SELECT ON `db1`.* TO `r2`@`%` |
| GRANT SELECT ON `db2`.* TO `r2`@`%` |
| GRANT `u1`@`%`,`r1`@`%` TO `r2`@`%` |
+-------------------------------------+
前面的範例僅用於說明,但使用者帳戶和角色的互換性具有實際應用,例如在以下情況中:假設在 MySQL 中出現角色之前,一個舊的應用程式開發專案開始了,因此與該專案相關聯的所有使用者帳戶都直接授予權限(而不是因為被授予角色而授予權限)。其中一個帳戶是一個開發人員帳戶,最初授予權限如下
CREATE USER 'old_app_dev'@'localhost' IDENTIFIED BY 'old_app_devpass';
GRANT ALL ON old_app.* TO 'old_app_dev'@'localhost';
如果此開發人員離開專案,則必須將權限指派給另一個使用者,或者如果開發活動擴大了,則可能需要指派給多個使用者。以下是一些處理此問題的方法
不使用角色的情況:變更帳戶密碼,使原開發人員無法使用它,並讓新的開發人員改用該帳戶
ALTER USER 'old_app_dev'@'localhost' IDENTIFIED BY 'new_password';
使用角色的情況:鎖定帳戶以防止任何人使用它連線到伺服器
ALTER USER 'old_app_dev'@'localhost' ACCOUNT LOCK;
然後將該帳戶視為角色。對於每個專案的新開發人員,建立一個新帳戶並將原開發人員帳戶授予該帳戶
CREATE USER 'new_app_dev1'@'localhost' IDENTIFIED BY 'new_password'; GRANT 'old_app_dev'@'localhost' TO 'new_app_dev1'@'localhost';
這樣做的效果是將原開發人員帳戶的權限指派給新帳戶。