这个问题通过权限是没有办法控制的,因为允许KILL会话的权限就是ALTER SYSTEM。ALTER SYSTEM本身就是一个很强大的权限,可以对数据库实例进行设置。Oracle不但没有控制KILL哪些会话的权限,连单独KILL SESSION的权限都不存在。
而对于这种权限没有办法解决的问题,最好的处理方法就是通过存储过程将功能封装起来,然后将过程授权给用户:
[PHP]
SQL> CONN SYS@TESTRAC AS SYSDBA
输入口令: ****
已连接。
SQL> CREATE OR REPLACE PROCEDURE P_KILL_SESSION(P_USER IN VARCHAR2, P_SID IN VARCHAR2) AS
2 V_SQL VARCHAR2(32767);
3 BEGIN
4 SELECT 'ALTER SYSTEM KILL SESSION ''' || SID || ',' || SERIAL# || ''''
5 INTO V_SQL
6 FROM V$SESSION
7 WHERE USERNAME = P_USER
8 AND SID = P_SID;
9 EXECUTE IMMEDIATE V_SQL;
10 EXCEPTION
11 WHEN NO_DATA_FOUND THEN
12 RAISE_APPLICATION_ERROR(-20001,
13 'SID: ' || P_SID || ' DOES NOT EXISTS, OR THE SESSION USER IS NOT ' || P_USER);
14 END;
15 /
过程已创建。
SQL> CREATE OR REPLACE PROCEDURE P_KILL_USER_SESSION(P_SID IN NUMBER)
2 AUTHID CURRENT_USER AS
3 V_USERNAME VARCHAR2(30);
4 V_SID NUMBER;
5 BEGIN
6 SELECT SYS_CONTEXT('USERENV', 'SESSION_USER'), SYS_CONTEXT('USERENV', 'SID')
7 INTO V_USERNAME, V_SID FROM DUAL;
8 IF P_SID != V_SID THEN
9 P_KILL_SESSION(V_USERNAME, P_SID);
10 ELSE
11 RAISE_APPLICATION_ERROR(-20000, 'CAN NOT KILL CURRENT SESSION!');
12 END IF;
13 END;
14 /
过程已创建。
SQL> CREATE USER U1 IDENTIFIED BY U1;
用户已创建。
SQL> GRANT CONNECT TO U1;
授权成功。
SQL> GRANT EXECUTE ON P_KILL_USER_SESSION TO U1;
授权成功。
.
[/PHP]
过程P_KILL_SESSION用来KILL指定的会话,由于这个过程在SYS用户下创建,因此可以KILL任何会话,不过在KILL SESSION之前,过程检查输入的SID和USERNAME是否相符,如果不相符则报错。
过程P_KILL_USER_SESSION是一个调用者权限过程,通过在这个过程中获取当前会话的SID和USERNAME信息,并检查用户KILL的SESSION是否与当前会话SID相同,如果相同则报错。
下面创建一个用户,并授予CONNECT角色,使得这个用户可以登陆数据库。这个用户除了CREATE SESSION权限外,没有其他的系统权限。下面将P_KILL_USER_SESSION过程授权给U1。
并启动两个会话以U1连接到数据库:
[php]
SQL> CONN U1/U1@TESTRAC
已连接。
SQL> SET SQLP 'SQL2> '
SQL2>
.
[/php]
另一个连接:
[php]
SQL> CONN U1/U1@TESTRAC
已连接。
SQL> SET SQLP 'SQL3> '
.
[/php]
查询一下连接信息:
[php]
SQL> SELECT SID, USERNAME
2 FROM V$SESSION
3 WHERE MACHINE LIKE '%YTK%';
SID USERNAME
---------- ------------------------------
286 U1
291 U1
307 SYS
.
[/php]
U1连查询V$SESSION的权限都没有,但是可以KILL另外一个以U1用户登陆的会话:
[php]
SQL2> SELECT * FROM V$SESSION;
SELECT * FROM V$SESSION
*
第 1 行出现错误:
ORA-00942: 表或视图不存在
SQL2> SELECT SYS_CONTEXT('USERENV', 'SID')
2 FROM DUAL;
SYS_CONTEXT('USERENV','SID')
----------------------------------------------------------------------------
286
SQL2> EXEC SYS.P_KILL_USER_SESSION(286)
BEGIN SYS.P_KILL_USER_SESSION(286); END;
*
第 1 行出现错误:
ORA-20000: CAN NOT KILL CURRENT SESSION!
ORA-06512: 在 "SYS.P_KILL_USER_SESSION", line 11
ORA-06512: 在 line 1
SQL2> EXEC SYS.P_KILL_USER_SESSION(307)
BEGIN SYS.P_KILL_USER_SESSION(307); END;
*
第 1 行出现错误:
ORA-20001: SID: 307 DOES NOT EXISTS, OR THE SESSION USER IS NOT U1
ORA-06512: 在 "SYS.P_KILL_SESSION", line 12
ORA-06512: 在 "SYS.P_KILL_USER_SESSION", line 9
ORA-06512: 在 line 1
SQL2> EXEC SYS.P_KILL_USER_SESSION(291)
PL/SQL 过程已成功完成。
.
[/php]
验证一下会话已经被KILL:
[php]
SQL3> SELECT * FROM DUAL;
SELECT * FROM DUAL
*
第 1 行出现错误:
ORA-00028: 您的会话己被终止
SQL3>
.
[/php]
这个问题并不复杂,不过刚好可以体现定义者存储过程和调用者存储过程的区别。
原文出自:http://yangtingkun.itpub.net/post/468/481170
[本帖最后由 yangtingkun 于 2009-3-27 23:48 编辑]