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 sessionexecute 语句等。但是如果我是你我会把用户打开过程放在单独的程序中,我想不会那么简单的代码,所以很容易编辑包程序。

您可以为您的用户会话创建上下文,将您想要控制访问的所有表包装到视图中,然后按用户上下文过滤视图。 例如你表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 用户名和密码标识的触发器创建用户的主要内容,如果未能解决你的问题,请参考以下文章

PLSQL :NEW 和 :OLD

PLSQL创建Oracle定时任务

PLSQL创建Oracle定时任务

oracle表删除后在plsql的左侧还是能看到

Oracle数据库,我把用户删除了,但是plsql中还能登录

安装oracle和PLSQL的经历