使用Oracle的PROFILE对用户资源限制和密码限制

1.用户创建语句PROFILE选项
如果大家细心的话,在创建用户的语法中有这么一个选项“PROFILE profile”。
下面是Oracle 11gR2官方文档中关于创建用户的语法描述(较之10g的文档可读性加强了,当然功能也同样有所增加):
CREATE USER user
   IDENTIFIED { BY password
              | EXTERNALLY [ AS 'certificate_DN'  |  AS 'kerberos_principal_name' ]
              | GLOBALLY [ AS '[ directory_DN ]' ]
              }
   [ DEFAULT TABLESPACE tablespace
   | TEMPORARY TABLESPACE
        { tablespace | tablespace_group_name }
   | { QUOTA { size_clause | UNLIMITED } ON tablespace }...
   | PROFILE profile
   | PASSWORD EXPIRE
   | ACCOUNT { LOCK | UNLOCK }
     | ENABLE EDITIONS
     ]...
  ] ;


2.提问:PROFILE选项是做什么用的呢?
回答:简要的说,PROFILE在Oracle数据库对数据库用户能够使用的资源做进一步的限制的一种手段,
如对连接到某个用户的session或sql所能使用的CPU资源进行控制,又如控制Oracle用户的密码管理策略等等……

3.查看系统的默认PROFILE
1)通过dba_profiles视图查看一下系统中默认都有哪些PROFILE
数据库创建以后,系统中只会存在一个名为DEFAULT的默认PROFILE,在用户创建之后,如果不做特殊指定,
每个用户的PROFILE都会默认的使用个默认的PROFILE。
sys@ora10g> select distinct profile from dba_profiles;

PROFILE
----------
DEFAULT

2)全面查看一下这个PROFILE都包含哪些限制定义


sys@ora10g> select * from dba_profiles;

PROFILE    RESOURCE_NAME                    RESOURCE LIMIT
---------- -------------------------------- -------- -----------------
DEFAULT    COMPOSITE_LIMIT                  KERNEL   UNLIMITED
DEFAULT    SESSIONS_PER_USER                KERNEL   UNLIMITED
DEFAULT    CPU_PER_SESSION                  KERNEL   UNLIMITED
DEFAULT    CPU_PER_CALL                     KERNEL   UNLIMITED
DEFAULT    LOGICAL_READS_PER_SESSION        KERNEL   UNLIMITED
DEFAULT    LOGICAL_READS_PER_CALL           KERNEL   UNLIMITED
DEFAULT    IDLE_TIME                        KERNEL   UNLIMITED
DEFAULT    CONNECT_TIME                     KERNEL   UNLIMITED
DEFAULT    PRIVATE_SGA                      KERNEL   UNLIMITED
DEFAULT    FAILED_LOGIN_ATTEMPTS            PASSWORD 10
DEFAULT    PASSWORD_LIFE_TIME               PASSWORD UNLIMITED
DEFAULT    PASSWORD_REUSE_TIME              PASSWORD UNLIMITED
DEFAULT    PASSWORD_REUSE_MAX               PASSWORD UNLIMITED
DEFAULT    PASSWORD_VERIFY_FUNCTION         PASSWORD NULL
DEFAULT    PASSWORD_LOCK_TIME               PASSWORD UNLIMITED
DEFAULT    PASSWORD_GRACE_TIME              PASSWORD UNLIMITED

16 rows selected.

3)结论
通过上面的查看,可以得到这个系统默认的PROFILE “DEFAULT”的配置信息只对密码的错误尝试次数做了限制
(最多可以错误输错10次密码),如果我们启用了PROFILE(下面有介绍如何启用和停止PROFILE),默认的限制只有这些。
如果想要改变限制条件,有两种方法:第一种方法是可以对这个默认的PROFILE做个性化的调整;
第二种方法是再重新定制一个新的PROFILE满足新的需求。

4.对创建PROFILE的标准语法做一下“解剖麻雀”(更多细节可以参考Oracle的官方文档)
1)创建PROFILE的语法如下:
CREATE PROFILE profile
   LIMIT { resource_parameters 对资源的限制
         | password_parameters 对密码的限制
         }...
;


修改参数的值ALTER PROFILE LIMIT ;;
例如:alter profile default limit password_life_time 1;

2)对数据库资源做限制时,下面是全部可用的参数:

RESOURCE_LIMIT=TRUE is required for resource limiting portions of the profile.
Password limiting functionality is not affected by this parameter.

SELECT name, value
FROM gv$parameter
WHERE name = 'resource_limit';

ALTER SYSTEM SET resource_limit=TRUE SCOPE=spfile;
startup force;

SELECT name, value
FROM gv$parameter
WHERE name = 'resource_limit';


{ { SESSIONS_PER_USER 每个用户名所允许的并行会话数
  | CPU_PER_SESSION   一个会话一共可以使用的CPU时间,单位是百分之一秒
  | CPU_PER_CALL      一次SQL调用(解析、执行和获取)允许使用的CPU时间
  | CONNECT_TIME      限制会话连接时间,单位是分钟
  | IDLE_TIME         允许空闲会话的时间,单位是分钟
  | LOGICAL_READS_PER_SESSION 限制会话对数据块的读取,单位是块
  | LOGICAL_READS_PER_CALL    限制SQL调用对数据块的读取,单位是块
  | COMPOSITE_LIMIT   “组合打法”
  }
  { integer | UNLIMITED | DEFAULT }
| PRIVATE_SGA   限制会话在SGA中Shared Pool中私有空间的分配
  { size_clause | UNLIMITED | DEFAULT }
}

 
3)对密码做限制时,下面是全部可用的参数:
{ { FAILED_LOGIN_ATTEMPTS 帐户被锁定之前可以错误尝试的次数
  | PASSWORD_LIFE_TIME    密码可以被使用的天数,单位是天,默认值180天
  | PASSWORD_REUSE_TIME   密码可重用的间隔时间(结合PASSWORD_REUSE_MAX)
  | PASSWORD_REUSE_MAX    密码的最大改变次数(结合PASSWORD_REUSE_TIME)
  | PASSWORD_LOCK_TIME    超过错误尝试次数后,用户被锁定的天数,默认1天
  | PASSWORD_GRACE_TIME   当密码过期之后还有多少天可以使用原密码
  }
  { expr | UNLIMITED | DEFAULT }
| PASSWORD_VERIFY_FUNCTION
  { function | NULL | DEFAULT }
}

5.OK,语法已经了然于胸,那么我们来实际创建一个PROFILE(这里的名字我们称其为:sec_profile),
在感性认识的同时一同探索一下在这个过程中会遇到的一些有趣现象。
1)尝试创建一个混合型的PROFILE(包含资源限制和密码限制)
sys@ora10g> CREATE PROFILE sec_profile LIMIT
  2     SESSIONS_PER_USER          UNLIMITED
  3     CPU_PER_SESSION            UNLIMITED
  4     CPU_PER_CALL               6000
  5     CONNECT_TIME               60
  6     LOGICAL_READS_PER_SESSION  DEFAULT
  7     LOGICAL_READS_PER_CALL     6000
  8     COMPOSITE_LIMIT            6000000
  9     PRIVATE_SGA                66K
 10     FAILED_LOGIN_ATTEMPTS      6
 11     PASSWORD_LIFE_TIME         60
 12     PASSWORD_REUSE_TIME        60
 13     PASSWORD_REUSE_MAX         5
 14     PASSWORD_LOCK_TIME         1/24
 15     PASSWORD_GRACE_TIME        10  
 16     PASSWORD_VERIFY_FUNCTION   verify_function
 17  /
CREATE PROFILE sec_profile LIMIT
*
ERROR at line 1:
ORA-07443: function VERIFY_FUNCTION not found

2)报错原因
为什么会报错?错误提示很清楚,VERIFY_FUNCTION这个函数不存在。既然知道不存在,那我们如何创建它呢?
回答:
VERIFY_FUNCTION函数是通过utlpwdmg.sql脚本创建的,该脚本所在的目录是$ORACLE_HOME/rdbms/admin/

3)创建密码复杂性检验函数VERIFY_FUNCTION
我们使用utlpwdmg.sql脚本创建以下这个VERIFY_FUNCTION密码复杂度校验函数
sys@ora10g> @?/rdbms/admin/utlpwdmg.sql

Function created.


Profile altered.

4)utlpwdmg.sql脚本中的小秘密
细心的您在执行utlpwdmg.sql脚本的过程中一定发现了有一个“Profile altered”的提示,难道这个脚本修改了某个PROFILE信息么?
正如您所料,它修改了系统的默认PROFILE “DEFAULT”,再次查看一下现在的DEFAULT的定义信息:
sys@ora10g> select * from dba_profiles;

PROFILE    RESOURCE_NAME                    RESOURCE LIMIT
---------- -------------------------------- -------- -----------------
DEFAULT    COMPOSITE_LIMIT                  KERNEL   UNLIMITED
DEFAULT    SESSIONS_PER_USER                KERNEL   UNLIMITED
DEFAULT    CPU_PER_SESSION                  KERNEL   UNLIMITED
DEFAULT    CPU_PER_CALL                     KERNEL   UNLIMITED
DEFAULT    LOGICAL_READS_PER_SESSION        KERNEL   UNLIMITED
DEFAULT    LOGICAL_READS_PER_CALL           KERNEL   UNLIMITED
DEFAULT    IDLE_TIME                        KERNEL   UNLIMITED
DEFAULT    CONNECT_TIME                     KERNEL   UNLIMITED
DEFAULT    PRIVATE_SGA                      KERNEL   UNLIMITED
DEFAULT    FAILED_LOGIN_ATTEMPTS            PASSWORD 3
DEFAULT    PASSWORD_LIFE_TIME               PASSWORD 60
DEFAULT    PASSWORD_REUSE_TIME              PASSWORD 1800
DEFAULT    PASSWORD_REUSE_MAX               PASSWORD UNLIMITED
DEFAULT    PASSWORD_VERIFY_FUNCTION         PASSWORD VERIFY_FUNCTION
DEFAULT    PASSWORD_LOCK_TIME               PASSWORD .0006
DEFAULT    PASSWORD_GRACE_TIME              PASSWORD 10

16 rows selected.

与系统创建时的DEFAULT PROFILE相比较,之前只有10次密码输入错误的限制,执行完成这个函数创建之后,
我们发现系统默认的PROFILE已经对系统有了一个非常严格的约束。
也就是说现在系统上的每一个用户在默认情况下都多了这些密码方面的限制。

5)验证一下新默认的PROFILE “DEFAULT”关于错误密码输入次数的限制
我们模拟一下错误输错密码三次后的效果,可以看到,在第四次输入后,sec用户被锁定了。
sys@ora10g> conn sec/a
ERROR:
ORA-01017: invalid username/password; logon denied


Warning: You are no longer connected to ORACLE.
sys@ora10g> conn sec/a
ERROR:
ORA-01017: invalid username/password; logon denied


sys@ora10g> conn sec/a
ERROR:
ORA-01017: invalid username/password; logon denied


sys@ora10g> conn sec/a
ERROR:
ORA-28000: the account is locked

6)使用dba_users视图比较一下用户的前后状态
用户sec已经因错误密码输入次数超过3此导致用户被锁定了,进一步比较一下用户的状态。
sys@ora10g> col USERNAME for a10
sys@ora10g> col ACCOUNT_STATUS for a20
sys@ora10g> col PROFILE for a10
sys@ora10g> select USERNAME,ACCOUNT_STATUS,LOCK_DATE,EXPIRY_DATE from dba_users where username = 'SEC';

USERNAME   ACCOUNT_STATUS       LOCK_DATE           EXPIRY_DATE
---------- -------------------- ------------------- -------------------
SEC        OPEN

sys@ora10g> select USERNAME,ACCOUNT_STATUS,LOCK_DATE,EXPIRY_DATE from dba_users where username = 'SEC';

USERNAME   ACCOUNT_STATUS       LOCK_DATE           EXPIRY_DATE
---------- -------------------- ------------------- -------------------
SEC        LOCKED(TIMED)        2009-10-11 16:05:03 2009-12-09 08:46:36

7)既然默认的PROFILE发生了变化,那么就会面临一个实际问题,如何恢复成原始状态呢?这里给出一个比较简单的方法,
就是使用ALTER PROFILE命令修改成原来的模样
sys@ora10g> ALTER PROFILE DEFAULT LIMIT
  2   COMPOSITE_LIMIT             UNLIMITED
  3   SESSIONS_PER_USER           UNLIMITED
  4   CPU_PER_SESSION             UNLIMITED
  5   CPU_PER_CALL                UNLIMITED
  6   LOGICAL_READS_PER_SESSION   UNLIMITED
  7   LOGICAL_READS_PER_CALL      UNLIMITED
  8   IDLE_TIME                   UNLIMITED
  9   CONNECT_TIME                UNLIMITED
 10   PRIVATE_SGA                 UNLIMITED
 11   FAILED_LOGIN_ATTEMPTS       10
 12   PASSWORD_LIFE_TIME          UNLIMITED
 13   PASSWORD_REUSE_TIME         UNLIMITED
 14   PASSWORD_REUSE_MAX          UNLIMITED
 15   PASSWORD_VERIFY_FUNCTION    NULL
 16   PASSWORD_LOCK_TIME          UNLIMITED
 17   PASSWORD_GRACE_TIME         UNLIMITED
 18  /

Profile altered.

8)最后我们重新创建一下这个实验PROFILE:sec_profile,可以预测,这次一定会成功滴~~~
sys@ora10g> CREATE PROFILE sec_profile LIMIT
  2     SESSIONS_PER_USER          UNLIMITED
  3     CPU_PER_SESSION            UNLIMITED
  4     CPU_PER_CALL               6000
  5     CONNECT_TIME               60
  6     LOGICAL_READS_PER_SESSION  DEFAULT
  7     LOGICAL_READS_PER_CALL     6000
  8     COMPOSITE_LIMIT            6000000
  9     PRIVATE_SGA                66K
 10     FAILED_LOGIN_ATTEMPTS      6
 11     PASSWORD_LIFE_TIME         60
 12     PASSWORD_REUSE_TIME        60
 13     PASSWORD_REUSE_MAX         5
 14     PASSWORD_LOCK_TIME         1/24
 15     PASSWORD_GRACE_TIME        10 
 16     PASSWORD_VERIFY_FUNCTION   verify_function
 17  /

Profile created.

创建成功鸟~~
在这个实验中,我们使用了PROFILE几乎所有的参数,惟一没有包含的参数是IDLE_TIME,
目的仅仅是为了说明一个细节:如果该PROFILE被授权给用户后,这个被省略的IDLE_TIME参数将继承默认PROFILE “DEFALT”中的定义。

对上面我们创建的PROFILE逐行做一下诠释,以便加深理解:
第 2行:对用户的并发连接会话数不做限制
第 3行:对于连接到用户的每一个session的CPU时间的使用不做限制
第 4行:一次调用消耗的CPU时间不能超过60秒(不超过一分钟)
第 5行:连接到用户的每次会话时间不能超过60分钟(不超过一个小时)
第 6行:一次会话使用的物理读与逻辑读数据块总量与DEFAULT profile中定义保持一致
第 7行:一次调用使用的物理读与逻辑读数据块总量不超过6000个数据块
第 8行:一次会话总的资源消耗不超过6000000个服务单元(service units)
第 9行:一次会话在SGA中不能分配超过66K的空间
第10行:帐户被锁定之前允许6次的错误尝试
第11行:超过此生命周期后密码作废
第12行:密码重用时间60天
第13行:密码重用之前密码需要完成5次改变
第14行:超过错误尝试次数后,用户将被锁定1小时
第15行:当密码过期之后原密码还可以使用10天
第16行:使用密码复杂度校验函数verify_function对密码做检查

再单独深入解释一下第12行和13行组合搭配之后的含义:也就是说只有完成了5次的密码修改后并且已经超过60天后,
之前的密码才能够再一次被使用。

9)通过dba_profiles视图再一次查看一下此时系统中存在的PROFILE的信息
sys@ora10g> col PROFILE for a12
sys@ora10g> col RESOURCE_NAME for a25
sys@ora10g> col LIMIT for a15
sys@ora10g> select * from dba_profiles order by 1,2;

PROFILE      RESOURCE_NAME             RESOURCE LIMIT
------------ ------------------------- -------- ---------------
DEFAULT      COMPOSITE_LIMIT           KERNEL   UNLIMITED
DEFAULT      CONNECT_TIME              KERNEL   UNLIMITED
DEFAULT      CPU_PER_CALL              KERNEL   UNLIMITED
DEFAULT      CPU_PER_SESSION           KERNEL   UNLIMITED
DEFAULT      FAILED_LOGIN_ATTEMPTS     PASSWORD 3
DEFAULT      IDLE_TIME                 KERNEL   UNLIMITED
DEFAULT      LOGICAL_READS_PER_CALL    KERNEL   UNLIMITED
DEFAULT      LOGICAL_READS_PER_SESSION KERNEL   UNLIMITED
DEFAULT      PASSWORD_GRACE_TIME       PASSWORD 10
DEFAULT      PASSWORD_LIFE_TIME        PASSWORD 60
DEFAULT      PASSWORD_LOCK_TIME        PASSWORD .0006
DEFAULT      PASSWORD_REUSE_MAX        PASSWORD UNLIMITED
DEFAULT      PASSWORD_REUSE_TIME       PASSWORD 1800
DEFAULT      PASSWORD_VERIFY_FUNCTION  PASSWORD VERIFY_FUNCTION
DEFAULT      PRIVATE_SGA               KERNEL   UNLIMITED
DEFAULT      SESSIONS_PER_USER         KERNEL   UNLIMITED
SEC_PROFILE  COMPOSITE_LIMIT           KERNEL   5000000
SEC_PROFILE  CONNECT_TIME              KERNEL   45
SEC_PROFILE  CPU_PER_CALL              KERNEL   3000
SEC_PROFILE  CPU_PER_SESSION           KERNEL   UNLIMITED
SEC_PROFILE  FAILED_LOGIN_ATTEMPTS     PASSWORD 5
SEC_PROFILE  IDLE_TIME                 KERNEL   DEFAULT
SEC_PROFILE  LOGICAL_READS_PER_CALL    KERNEL   1000
SEC_PROFILE  LOGICAL_READS_PER_SESSION KERNEL   DEFAULT
SEC_PROFILE  PASSWORD_GRACE_TIME       PASSWORD 10
SEC_PROFILE  PASSWORD_LIFE_TIME        PASSWORD 60
SEC_PROFILE  PASSWORD_LOCK_TIME        PASSWORD .0416
SEC_PROFILE  PASSWORD_REUSE_MAX        PASSWORD 5
SEC_PROFILE  PASSWORD_REUSE_TIME       PASSWORD 60
SEC_PROFILE  PASSWORD_VERIFY_FUNCTION  PASSWORD VERIFY_FUNCTION
SEC_PROFILE  PRIVATE_SGA               KERNEL   15360
SEC_PROFILE  SESSIONS_PER_USER         KERNEL   UNLIMITED

32 rows selected.

10)删除PROFILE
既然有创建,就一定对应着删除,删除方法很简单,语法如下:
DROP PROFILE profile [ CASCADE ] ;

如果创建的PROFILE已经授权给了具体的用户,则需要使用CASCADE选项级联的收回相应的限制信息,
在收回这些限制信息之后将会以系统默认的PROFILE对该用户进行限制。
删除示例如下:
sys@ora10g> drop profile SEC_PROFILE cascade;

Profile dropped.

如果该PROFILE已经授权给具体的用户,使用不带有cascade选项的删除命令将得到如下的报错信息:
sys@ora10g> drop profile SEC_PROFILE;
drop profile SEC_PROFILE
*
ERROR at line 1:
ORA-02382: profile SEC_PROFILE has users assigned, cannot drop without CASCADE

6.关于PROFILE内容何时生效的一点注意事项
这里讨论需要谈到一个系统参数,它就是resource_limit。

如果要开启“资源限制”的话,这个参数必须是“TRUE”的状态
sys@ora10g> show parameter resource_limit

NAME            TYPE                 VALUE
--------------- -------------------- ----------------------
resource_limit  boolean              FALSE

这里有一个小细节不得不提:
之所以称这个参数为resource_limit(资源限制)而没有称作“profile_limit”,是因为这个参数只对PROFILE中的资源限制部分起作用,
如果是默认的FALSE状态,表示PROFILE定义中的资源限制条件不起作用,只有该参数为TRUE时,资源限制的定义才生效。
不过,关于PROFILE中密码限制的条件则与这个参数无关,无论这个参数设置为什么值,有关密码限制的条件都会默认生效,
从这个细节上可以看出来Oracle对用户密码的重视程度。

因此关于PROFILE的生效情况结论是:
1)用户所有拥有的PROFILE中有关密码的限制永远生效,不受限制。
2)用户所有拥有的PROFILE中有关资源的限制与resource_limit参数的设置有关,当为TRUE时生效,当为FALSE时(默认值是FALSE)无效。

7.一切都OK了,最后我们来演示一下将新创建的PROFILE sec_profile指定给具体用户sec的操作
sys@ora10g> alter user sec profile sec_profile;

User altered.

sys@ora10g> select USERNAME,PROFILE from dba_users where USERNAME = 'SEC';
                                                                         
USERNAME   PROFILE                                                      
---------- ------------                                                 
SEC        SEC_PROFILE                                                  

8.【附】密码验证函数VERIFY_FUNCTION的创建脚本内容utlpwdmg.sql,供参考。
ora10g@secDB /home/oracle$ cat $ORACLE_HOME/rdbms/admin/utlpwdmg.sql
Rem
Rem $Header: utlpwdmg.sql 31-aug-2000.11:00:47 nireland Exp $
Rem
Rem utlpwdmg.sql
Rem
Rem  Copyright (c) Oracle Corporation 1996, 2000. All Rights Reserved.
Rem
Rem    NAME
Rem      utlpwdmg.sql - script. for Default Password Resource Limits
Rem
Rem    DESCRIPTION
Rem      This is a script. for enabling the password management features
Rem      by setting the default password resource limits.
Rem
Rem    NOTES
Rem      This file contains a function for minimum checking of password
Rem      complexity. This is more of a sample function that the customer
Rem      can use to develop the function for actual complexity checks that the
Rem      customer wants to make on the new password.
Rem
Rem    MODIFIED   (MM/DD/YY)
Rem    nireland    08/31/00 - Improve check for username=password. #1390553
Rem    nireland    06/28/00 - Fix null old password test. #1341892
Rem    asurpur     04/17/97 - Fix for bug479763
Rem    asurpur     12/12/96 - Changing the name of password_verify_function
Rem    asurpur     05/30/96 - New script. for default password management
Rem    asurpur     05/30/96 - Created
Rem

-- This script. sets the default password resource parameters
-- This script. needs to be run to enable the password features.
-- However the default resource parameters can be changed based
-- on the need.
-- A default password complexity function is also provided.
-- This function makes the minimum complexity checks like
-- the minimum length of the password, password not same as the
-- username, etc. The user may enhance this function according to
-- the need.
-- This function must be created in SYS schema.
-- connect sys/ as sysdba before running the script

CREATE OR REPLACE FUNCTION verify_function
(username varchar2,
  password varchar2,
  old_password varchar2)
  RETURN boolean IS
   n boolean;
   m integer;
   differ integer;
   isdigit boolean;
   ischar  boolean;
   ispunct boolean;
   digitarray varchar2(20);
   punctarray varchar2(25);
   chararray varchar2(52);

BEGIN
   digitarray:= '0123456789';
   chararray:= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
   punctarray:='!"#$%&()``*+,-/:;<=>?_';

   -- Check if the password is same as the username
   IF NLS_LOWER(password) = NLS_LOWER(username) THEN
     raise_application_error(-20001, 'Password same as or similar to user');
   END IF;

   -- Check for the minimum length of the password
   IF length(password) < 4 THEN
      raise_application_error(-20002, 'Password length less than 4');
   END IF;

   -- Check if the password is too simple. A dictionary of words may be
   -- maintained and a check may be made so as not to allow the words
   -- that are too simple for the password.
   IF NLS_LOWER(password) IN ('welcome', 'database', 'account', 'user', 'password', 'oracle', 'computer', 'abcd') THEN
      raise_application_error(-20002, 'Password too simple');
   END IF;

   -- Check if the password contains at least one letter, one digit and one
   -- punctuation mark.
   -- 1. Check for the digit
   isdigit:=FALSE;
   m := length(password);
   FOR i IN 1..10 LOOP
      FOR j IN 1..m LOOP
         IF substr(password,j,1) = substr(digitarray,i,1) THEN
            isdigit:=TRUE;
             GOTO findchar;
         END IF;
      END LOOP;
   END LOOP;
   IF isdigit = FALSE THEN
      raise_application_error(-20003, 'Password should contain at least one digit, one character and one punctuation');
   END IF;
   -- 2. Check for the character
   <>
   ischar:=FALSE;
   FOR i IN 1..length(chararray) LOOP
      FOR j IN 1..m LOOP
         IF substr(password,j,1) = substr(chararray,i,1) THEN
            ischar:=TRUE;
             GOTO findpunct;
         END IF;
      END LOOP;
   END LOOP;
   IF ischar = FALSE THEN
      raise_application_error(-20003, 'Password should contain at least one /
              digit, one character and one punctuation');
   END IF;
   -- 3. Check for the punctuation
   <>
   ispunct:=FALSE;
   FOR i IN 1..length(punctarray) LOOP
      FOR j IN 1..m LOOP
         IF substr(password,j,1) = substr(punctarray,i,1) THEN
            ispunct:=TRUE;
             GOTO endsearch;
         END IF;
      END LOOP;
   END LOOP;
   IF ispunct = FALSE THEN
      raise_application_error(-20003, 'Password should contain at least one /
              digit, one character and one punctuation');
   END IF;

   <>
   -- Check if the password differs from the previous password by at least
   -- 3 letters
   IF old_password IS NOT NULL THEN
     differ := length(old_password) - length(password);

     IF abs(differ) < 3 THEN
       IF length(password) < length(old_password) THEN
         m := length(password);
       ELSE
         m := length(old_password);
       END IF;

       differ := abs(differ);
       FOR i IN 1..m LOOP
         IF substr(password,i,1) != substr(old_password,i,1) THEN
           differ := differ + 1;
         END IF;
       END LOOP;

       IF differ < 3 THEN
         raise_application_error(-20004, 'Password should differ by at /
         least 3 characters');
       END IF;
     END IF;
   END IF;
   -- Everything is fine; return TRUE ;
   RETURN(TRUE);
END;
/

-- This script. alters the default parameters for Password Management
-- This means that all the users on the system have Password Management
-- enabled and set to the following values unless another profile is
-- created with parameter values set to different value or UNLIMITED
-- is created and assigned to the user.

ALTER PROFILE DEFAULT LIMIT
PASSWORD_LIFE_TIME 60
PASSWORD_GRACE_TIME 10
PASSWORD_REUSE_TIME 1800
PASSWORD_REUSE_MAX UNLIMITED
FAILED_LOGIN_ATTEMPTS 3
PASSWORD_LOCK_TIME 1/1440
PASSWORD_VERIFY_FUNCTION verify_function;

仔细阅读上面的脚本,您可以得到关于verify_function函数的密码复杂性校验规则如下:
1)密码最小长度为4个字符
2)密码不能和用户名相同
3)密码要包含至少一个字符、一个数字和一个特殊字符
4)密码需要至少有3个字符与之前的口令不相同

9.小结
通过PROFILE对用户的资源和登录密码的限制方法使用起来还是很贴心的,毕竟Oracle提供了这样一种便捷的管理手段,
对付数据库方面的一般性审计还是游刃有余的,即使这些规则仍然不能满足需求,Oracle也提供了添加和修改的接口,
以便按需定制,当然会有一定的开发周期和成本。

Oracle对数据库的安全性方面提供了非常多的手段来保证,如审计和权限控制等优秀手段。对于一个商业数据库的安全性审计是非常重要的,
如果后台的数据库存在安全隐患,可以说必定会带来一场浩劫,只是早晚的事情。如果您是一名维护DBA,
一定要从数据库的安全性上多多动脑筋,不要等待问题出现了才恍然大悟:哇塞,原来Oracle同学早就有补救的措施啦!

BTW:关于权限控制的策略在使用前一定要做好充分的测试,确保对每一条限制规则理解透彻,不然会带来不必要的麻烦,
另外一个经验就是:将每一步的限制规则记录到一个特定的数据库维护手册中,方便查询,
                  同时也便于知识的传承,利己利人,“好事两桩”。

-- The End --

你可能感兴趣的:(oracle,管理)