ORACLE PLSQL 使用由 :new 用户名和密码标识的触发器创建用户
Posted
技术标签:
【中文标题】ORACLE PLSQL 使用由 :new 用户名和密码标识的触发器创建用户【英文标题】:ORACLE PLSQL Create user with trigger identified by :new username and password 【发布时间】:2017-06-19 09:58:54 【问题描述】:我正在为一个学术项目使用 ORACLE 在 SQL 中编写一个在线视频游戏数据库,并且我正在尝试为每个在我的 ACCCOUNT TABLE 中提交信息的用户创建一个触发器
CREATE TABLE ACCOUNT (
USERNAME VARCHAR(20),
PASSWORD VARCHAR(20) NOT NULL,
NATIONALITY VARCHAR(15),
CREATION DATE DATE,
EMAIL_ACCOUNT VARCHAR(35) NOT NULL,
CONSTRAINT KEYACCOUNT PRIMARY KEY(USERNAME),
CONSTRAINT NO_USER_CSPEC CHECK(REGEXP_LIKE(USERNAME, '^[a-zA-Z0-9._]+$') AND USERNAME NOT LIKE '% %'),
CONSTRAINT NO_EASY_PASS CHECK(REGEXP_LIKE(PASSWORD, '^[a-zA-Z0-9._!#£$%&/()=?]') AND PASSWORD NOT LIKE '% %'),
CONSTRAINT LENGHTUSER CHECK(LENGTH(USERNAME)>3),
CONSTRAINT LENGHTPASS CHECK(LENGTH(PASSWORD)>5),
CONSTRAINT FK_EMAIL FOREIGN KEY(EMAIL_ACCOUNT) REFERENCES PERSONA(EMAIL) ON DELETE CASCADE
);
将触发一个触发器,该触发器将使用刚刚插入的新用户名和密码创建一个新用户。
这是我尝试编写的代码
CREATE OR REPLACE TRIGGER NEW_USER
AFTER INSERT ON ACCOUNT
FOR EACH ROW
BEGIN
CREATE USER :NEW.USERNAME IDENTIFIED BY :NEW.PASSWORD;
GRANT ROLE_NAME TO :NEW.USERNAME
END;
我为什么要这么做? 基本上是因为我想给出关于特定用户的特定视图。 (想象一下,如果在管理您的帐户时,您可以访问存储在表 ACCOUNT 中的每一行)
创建该特定用户后,我可以创建一些输入用户名(成功创建用户的)的过程,并返回该特定行的视图。
有没有办法做到这一点?
【问题讨论】:
您的过程不能仅仅根据传入的用户名从表中选择特定行吗?为什么拥有 Oracle 用户帐户对该过程有任何影响? (也许您出于其他原因计划让用户以自己的身份连接到数据库,但不确定这种情况本身是否有意义)。 你不能在触发器中使用 DDL,因为 DDL 语句提交当前事务,你不能在触发器中这样做 好吧,情况如何?我想为 n 个帐户创建 n 个用户。或者至少,尝试仅授予与特定用户相关的行的访问权限。 @GanniH 为什么不使用数据库存储过程将行插入ACCOUNT
表中,并在用户成功插入后在同一过程中创建具有密码的用户?
听起来不错。这甚至可能吗?
【参考方案1】:
首先,你不能将触发器体中的DDL语句作为开源使用,你应该把它放在execute immediate
命令中。您还应该注意将执行然后触发的用户权限,以及将授予用户的角色,是否有所有权限授予,create session
,execute
语句等。但是如果我是你我会把用户打开过程放在单独的程序中,我想不会那么简单的代码,所以很容易编辑包程序。
您可以为您的用户会话创建上下文,将您想要控制访问的所有表包装到视图中,然后按用户上下文过滤视图。
例如你表TAB_A
有很多行,在表中你存储列ACS_USER
并将表包装到V_TAB_A
,当你可以通过视图控制对表的访问时,所有用户访问对象都将使用像
select * from V_TAB_A where ACSUSER = SYS_CONTEXT('USERENV','SESSION_USER')
【讨论】:
所以没有正确的方法来为特定帐户创建特定用户对吗?所以,现在的问题是:还有另一种方法可以授予与用户名相关的特定行的权限吗? (我不想授予我的 USER 权限并让他从我的表中选择 *。) 按行或列控制访问是在这里用几个词来解释的大主题,但你可以看到this和this【参考方案2】:我在这里看到的主要问题是授予create user
。您可能不希望您的架构能够创建用户。所以触发器(当然,正如其他答案所说,这需要是execute immediate
)不应该直接调用创建用户。我将创建在您的工作模式之外的其他模式中创建用户的过程。该外部模式将有权授予create user
,而您的模式将仅有权从强特权模式执行该一个过程。在这种情况下,触发器只会从外部模式调用单个过程。
回顾一下:
CREATE OR REPLACE TRIGGER your_schema.NEW_USER
AFTER INSERT ON ACCOUNT
FOR EACH ROW
BEGIN
STRONG.CREATE_USER(:NEW.PASSWORD,:NEW.USERNAME);
END;
CREATE OR REPLACE PROCEDURE STRONG.CREATE_USER(PASS VARCHAR2, USERNAME VARCHAR2) AS
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
execute immediate 'CREATE USER ' || USERNAME || ' IDENTIFIED BY ' || PASS;
execute immediate 'GRANT ROLE_NAME, CONNECT, RESOURCE TO ' || USERNAME; --and whatever user needs more
END;
STRONG 用户有权创建用户并且 your_schema 有权执行STRONG.CREATE_USER
额外的东西。切勿以纯文本形式存储密码。使用一些哈希。
【讨论】:
非常感谢您的帮助,Kacper!我试过这个(连同 user75ponic 解决方案),但它给了我 ORA-00901 无效的 CREATE 命令,我认为这与无效的创建选项有关,因为 Oracle 手册建议我更正语法。这种语法在 Oracle 11g 中可用吗?而且,你对密码的哈希是什么意思?提前感谢您的回答。 以纯文本形式存储密码是不安全的。标准做法是在用户提供的条目上使用散列函数,并将其与存储在 db 中的散列函数的结果进行比较,在这种情况下,如果有人破解了您的 db 密码,则不会被泄露。试试这个话题***.com/questions/1054022/… 好的,我找到了导致 ORA-00901 的错误,它是 create 命令和 concat 'CREATE USER '^|| 之间的空格用户名 ||我把^符号放在哪里。现在它似乎可以工作,因为我试图在过程中插入任意值(不使用触发器)并且它工作(示例:'CREATE OR REPLACE PROCEDURE CREATE_USER IS USER VARCHAR(20):='User1'; PASS VARCHAR(20) := 'Password1';' 它起作用了,但是当我使用触发器时,它给了我 ORA-04092: cannot COMMIT in a trigger。有什么想法吗?(无论如何哈希听起来很有趣,我会看看,谢谢。 ) 你可以添加到过程DECLARE PRAGMA AUTONOMOUS_TRANSACTION;
但这有点废话,因为即使整个事务回滚,也会创建用户。这里 user75ponic 的解决方案似乎更好地在一个过程中插入和创建用户。
是的,它就像一个魅力,非常感谢你 Kacper!你认为一个程序会更好吗?但我应该在每次将行插入 ACCOUNT 时调用该过程,因为触发器会更好,因为会自动创建用户。此外,在过程中我添加了 EXECUTE IMMEDIATE 'GRANT SELECT ON ACCOUNT TO '||USERNAME,但 oracle 给了我错误 ORA-00921: SQL 命令意外结束。是否可以在程序中添加选择、删除和更新权限,如果是,我需要创建另一个程序以仅显示与用户相关的行?谢谢。【参考方案3】:
或者,您可以使用数据库存储过程而不是触发器来执行 DDL 操作。
这是一个伪代码,请根据您的要求进行必要的更改。您可以在此基础上构建您的逻辑,如果您遇到困难,请始终在 SO 中发布问题。
表格
CREATE TABLE account_info
(
user_name VARCHAR (20),
user_password VARCHAR (20) NOT NULL
);
程序
CREATE OR REPLACE PROCEDURE test_procedure (
user_name IN account_info.user_name%TYPE,
user_password IN account_info.user_password%TYPE)
AS
BEGIN
INSERT INTO account_info (user_name, user_password)
VALUES ('ABC', 'password123');
-- check the user exists or not, if yes proceed
EXECUTE IMMEDIATE
'CREATE USER ' || user_name || ' IDENTIFIED BY ' || user_password;
-- do the rest of the activities such as grant roles, privilges etc.
END;
/
【讨论】:
天哪,非常感谢!这看起来不错。我现在正在尝试这个,但我遇到了错误 ORA-00901 invalid CREATE command,我认为这与无效的创建选项有关,因为 Oracle 手册建议我更正语法。知道为什么吗?同样在我的表中,我有 EMAIL_ACCOUNT,它是来自 USER 的 NOT NULL 外键(我用来存储姓名、姓氏、出生日期和性别的表),并且我还将电子邮件作为 EMAIL IN ACCOUNT.EMAIL_ACCOUNT%TYPE 插入到函数中,但未插入到 create user 语句中。这是错的吗? @GanniH - 在USER
之后和INDENTIFIED
之前缺少空格,这使得生成的语句无效。尝试添加空格。以上是关于ORACLE PLSQL 使用由 :new 用户名和密码标识的触发器创建用户的主要内容,如果未能解决你的问题,请参考以下文章