Oracle密码验证函数必须用PL/SQL编写,签名严格限定为三个VARCHAR2输入参数(username、password、old_password)且返回BOOLEAN;须显式授权EXECUTE并全名引用,调试宜封装为带OUT参数的过程。
Oracle 密码验证函数必须用 PL/SQL 编写,且签名不能改
oracle 的密码验证机制只认一个固定签名的函数:verify_function(或任意名,但参数和返回值必须严格匹配)。它不是钩子、不是触发器,也不是配置开关——你得手写一个符合要求的 pl/sql 函数,然后通过 alter profile 绑定到用户配置文件上。
常见错误是直接写个普通函数,比如带额外参数或返回 VARCHAR2,结果创建时没报错,但绑定 profile 后改密码就静默失败,或者提示 ORA-28003: password verification for the specified password failed 却不告诉你哪条规则没过。
- 函数必须返回
BOOLEAN(注意:不是NUMBER或VARCHAR2) - 必须有且仅有三个
IN参数:username IN VARCHAR2、password IN VARCHAR2、old_password IN VARCHAR2 - 函数体里不能有 DML、DDL、自治事务;不能调用含副作用的包(如
DBMS_OUTPUT.PUT_LINE在验证时会被忽略,但可能干扰调试) - 建议在函数开头加
IF password IS NULL THEN RETURN FALSE; END IF;防空值误判
强密码规则要拆解成可检测的原子条件,别堆逻辑
“强密码”在 Oracle 里没有内置正则支持(11g 及以前),REGEXP_LIKE 从 10g 起可用,但要注意:它在密码验证函数中性能敏感,且某些字符集下可能行为异常(比如中文字符参与匹配时意外命中)。更稳妥的做法是用基础字符串函数逐项检查。
典型规则如“至少 1 个大写 + 1 个小写 + 1 个数字 + 1 个特殊字符 + 长度 ≥ 8”,别写成一行嵌套 AND 判断,容易漏掉边界情况(比如全 ASCII 特殊字符但长度只有 7)。
- 先用
LENGTH(password)检长度,短于最小值直接RETURN FALSE - 用
REGEXP_LIKE(password, '[A-Z]')查大写(若用 10g+),否则用INSTR(UPPER(password), password) > 0类似技巧 - 数字检查用
REGEXP_LIKE(password, '[0-9]')最直观;避免TRANSLATE(password, '0123456789', '') != password这种易出错写法 - 特殊字符列表要显式定义,比如
'!@#$%^&*()_+-=[]{}|;:,.?',再用循环或REGEXP_LIKE(password, '[^a-zA-Z0-9]')粗筛后二次确认 - 禁止用户名子串、禁止连续重复字符(如
'aaa')、禁止常见弱口令(如'oracle'、'password')都得单独写分支,别指望一个正则包打天下
绑定 profile 前必须显式授权 EXECUTE,且函数需在 SYS 或 PUBLIC 下可见
即使函数编译成功,ALTER PROFILE default LIMIT PASSWORD_VERIFY_FUNCTION my_verify_func 仍可能报 ORA-00942: table or view does not exist——这不是表不存在,而是 Oracle 在解析函数名时找不到可执行对象。根本原因是权限或作用域问题。
函数默认只能被创建者调用。而密码验证由 Oracle 内部进程以 SYS 身份执行,它不会自动跨 schema 查你的函数。
- 函数必须建在
SYS用户下(不推荐,维护风险高),或建在普通用户下后执行:GRANT EXECUTE ON your_schema.verify_function TO PUBLIC; - 绑定时写全名:
your_schema.verify_function,不能只写函数名(除非已创建同义词并授了 PUBLIC 权限) - 检查是否生效:查
SELECT profile, resource_name, limit FROM dba_profiles WHERE resource_name = 'PASSWORD_VERIFY_FUNCTION'; - 测试时用非 SYS 用户改密码(
ALTER USER scott IDENTIFIED BY ...),SYS 用户绕过验证,测了等于白测
调试验证函数最有效的办法是临时改写为存储过程 + 手动调用
Oracle 不允许在验证函数里直接输出日志,DBMS_OUTPUT 在密码校验上下文中被禁用,UTL_FILE 需目录权限且不实用。线上出问题时,光看 ORA-28003 根本不知道哪条规则挂了。
解决方法是把验证逻辑抽出来,封装成一个带 OUT 参数的存储过程,用于人工模拟验证过程:
CREATE OR REPLACE PROCEDURE debug_verify(
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_old_password IN VARCHAR2,
p_result OUT BOOLEAN,
p_reason OUT VARCHAR2
) AS
BEGIN
p_result := FALSE;
IF LENGTH(p_password) < 8 THEN
p_reason := 'Too short';
RETURN;
END IF;
IF NOT REGEXP_LIKE(p_password, '[A-Z]') THEN
p_reason := 'No uppercase';
RETURN;
END IF;
-- 其他规则...
p_result := TRUE;
p_reason := 'OK';
END;然后用匿名块调用:BEGIN debug_verify('test', 'abc123', NULL, :r, :msg); END;,立刻看到具体失败原因。上线前务必删掉这个调试过程,只留纯净的验证函数。
真正麻烦的不是写规则,是不同 Oracle 版本对正则、字符集、空格处理的细微差异——比如 12c 在多字节字符集下 LENGTH 和 LENGTHB 表现不同,测试必须覆盖目标环境的真实字符集和版本。










