批量修改密码前必须确认三件事:一是当前用户具备ALTER USER系统权限,通常需SYSDBA身份;二是跳过SYS、SYSTEM等关键内置账户,防止数据库启动失败;三是检查密码复杂度策略是否启用,避免ORA-28003错误。

批量修改前必须确认的三件事
直接跑动态 SQL 批量改密码不是不行,但容易锁库、触发密码策略报错、甚至把 SYS 或 SYSTEM 密码搞丢导致无法登录。动手前先做这三步:
- 确认当前用户有
ALTER USER系统权限(通常只有SYSDBA身份能安全执行) - 查清哪些用户不能动:
SYS、SYSTEM、OUTLN、DIP、CTXSYS等内置账户必须跳过,否则数据库可能启动失败 - 检查密码复杂度策略是否启用:
SELECT * FROM dba_profiles WHERE profile = 'DEFAULT' AND resource_name = 'PASSWORD_VERIFY_FUNCTION';如果返回非NULL,新密码必须满足规则,否则ALTER USER会报ORA-28003
用 EXECUTE IMMEDIATE 动态拼接并执行 ALTER USER
PL/SQL 块里用游标遍历 + EXECUTE IMMEDIATE 是最可控的方式。注意:不能直接拼接密码明文进字符串再执行——如果密码含单引号或特殊字符会语法报错;必须用绑定变量或双引号包裹。
推荐写法(兼容含特殊字符的密码):
BEGIN
FOR rec IN (SELECT username FROM dba_users
WHERE username NOT IN ('SYS','SYSTEM','OUTLN','DIP','CTXSYS','ANONYMOUS','XS$NULL')
AND account_status = 'OPEN') LOOP
BEGIN
EXECUTE IMMEDIATE 'ALTER USER ' || rec.username || ' IDENTIFIED BY "MyP@ssw0rd2026"';
DBMS_OUTPUT.PUT_LINE('✓ ' || rec.username);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('✗ ' || rec.username || ': ' || SQLERRM);
END;
END LOOP;
END;
/- 密码用双引号
"MyP@ssw0rd2026"包裹,避免因特殊字符解析失败 - 过滤条件加了
account_status = 'OPEN',跳过已锁定或过期用户,防止误激活 - 每个
ALTER USER单独 try-catch,一个失败不影响其余用户
只生成语句不执行:用 SELECT ... FROM dba_users 构造脚本
更稳妥的做法是先生成所有 ALTER USER 语句,人工检查后再执行。适合生产环境或审计要求严格的场景。
运行以下查询,复制结果到 SQL*Plus 中执行:
SELECT 'ALTER USER ' || username || ' IDENTIFIED BY "MyP@ssw0rd2026";'
FROM dba_users
WHERE username NOT IN ('SYS','SYSTEM','OUTLN','DIP','CTXSYS','ANONYMOUS','XS$NULL')
AND account_status = 'OPEN'
ORDER BY username;- 输出是纯文本语句,可 grep、sed、vim 逐行审查
- 方便加注释、临时注释掉个别用户、或替换为不同密码(比如按用户名分组设不同密码)
- 避免 PL/SQL 块中隐式提交或异常中断带来的状态不确定性
为什么不用 PASSWORD 命令或 GRANT ALTER USER 方式批量处理
PASSWORD 是 SQL*Plus 专有命令,不能在 PL/SQL 或脚本中调用;而给普通用户授 ALTER USER 权限再让其自改密码,等于把高危权限下放,违反最小权限原则。
-
PASSWORD只能在交互式 SQL*Plus 里用,无法嵌入循环或脚本 -
GRANT ALTER USER TO scott后,scott就能改任意用户密码,包括SYS(如果权限链没限制死),风险不可控 - 密码哈希复用(
IDENTIFIED BY VALUES)虽可绕过策略检查,但 Oracle 12c+ 默认禁用该语法,且哈希值跨版本不兼容
真正要落地批量改密,核心就两点:用 SYSDBA 身份、严格过滤用户范围、密码加双引号、每条单独捕获异常。其他花招反而增加不可预知性。


















