让普通用户可以kill自己用户下的session(转)
原文地址:http://space.itpub.net/?uid-4227-action-viewspace-itemid-580406
这个问题通过权限是没有办法控制的,因为允许KILL会话的权限就是ALTER SYSTEM。ALTER SYSTEM本身就是一个很强大的权限,可以对数据库实例进行设置。Oracle不但没有控制KILL哪些会话的权限,连单独KILL SESSION的权限都不存在。
而对于这种权限没有办法解决的问题,最好的处理方法就是通过存储过程将功能封装起来,然后将过程授权给用户:
SQL> CONN SYS@"172.25.198.224/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;
授权成功。
过程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连接到数据库:
SQL> CONN U1/U1@172.25.198.224/TESTRAC
已连接。
SQL> SET SQLP 'SQL2> '
SQL2>
另一个连接:
SQL> CONN U1/U1@172.25.198.224/TESTRAC
已连接。
SQL> SET SQLP 'SQL3> '
查询一下连接信息:
SQL> SELECT SID, USERNAME
2 FROM V$SESSION
3 WHERE MACHINE LIKE '%YTK%';
SID USERNAME
---------- ------------------------------
286 U1
291 U1
307 SYS
U1连查询V$SESSION的权限都没有,但是可以KILL另外一个以U1用户登陆的会话:
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过程已成功完成。
验证一下会话已经被KILL:
SQL3> SELECT * FROM DUAL;
SELECT * FROM DUAL
*
第1行出现错误:
ORA-00028:您的会话己被终止
SQL3>
这个问题并不复杂,不过刚好可以体现定义者存储过程和调用者存储过程的区别。