liuziyi

liuziyi

Oracle修改管理员密码密码

版本兼容矩阵

版本 关键差异
Oracle 9i / 10g 密码文件由 orapwd 生成,REMOTE_LOGIN_PASSWORDFILE=EXCLUSIVE;密码大小写不敏感
Oracle 11g 引入 sec_case_sensitive_logon(默认 TRUE),密码开始区分大小写
Oracle 12c 及以上 多租户架构,SYS 为公共用户,须在 CDB$ROOT 下修改;密码文件格式升级,主备库不一致会报 ORA-46952
Oracle 18c / 19c 密码文件默认位置不变,orapwd 支持 format=12 显式指定
Oracle 21c / 23ai 强化密码策略,弱口令易被 profile 的 ora12c_verify_function 拒绝

脚本

#!/usr/bin/env bash
# ============================================================
# Oracle 管理员(SYS/SYSTEM)密码修改工具
# 用法:bash ora_chpass.sh --help
# ============================================================
set -uo pipefail

SCRIPT_NAME=$(basename "$0")
SCRIPT_VERSION="1.0.0"

usage(){
cat <<EOF
${SCRIPT_NAME} ${SCRIPT_VERSION} — Oracle 管理员密码修改工具

用法:
  bash ${SCRIPT_NAME} [选项]

选项:
  -h, --help                显示本帮助并退出(不连接数据库)
  -V, --version             显示脚本版本并退出
  -u, --user <用户名>       要改密的账号,默认 SYS
  -p, --password <新密码>   新密码;不提供则交互式输入(推荐)
      --old-password <旧密码>
                            旧密码,仅当 OS 认证不可用时用于建立连接
  -s, --sid <实例名>        指定 ORACLE_SID
  -O, --oracle-home <路径>  指定 ORACLE_HOME
      --rebuild-pwfile      改密后重建密码文件(需要停库,谨慎使用)
      --entries <数量>      重建密码文件时的条目数,默认 10
      --no-verify           跳过改密后的登录验证
      --dry-run             只打印将要执行的语句,不实际执行
      --                    结束选项解析

环境变量:
  ORACLE_NEW_PASS           等价于 --password
  ORACLE_OLD_PASS           等价于 --old-password
  ORACLE_SID / ORACLE_HOME  未通过选项指定时使用

连接方式(按顺序自动尝试):
  1. 本地 OS 认证:sqlplus / as sysdba
  2. 密码文件认证:sqlplus <user>/<old> as sysdba(需提供 --old-password)
  3. 均失败时提示使用 --rebuild-pwfile 重建密码文件

版本兼容:
  Oracle 9i / 10g    密码大小写不敏感,密码文件由 orapwd 生成
  Oracle 11g         sec_case_sensitive_logon 默认 TRUE,密码区分大小写
  Oracle 12c 及以上  多租户,SYS 为公共用户,须在 CDB\$ROOT 下修改
  Oracle 18c / 19c   密码文件格式升级,主备库不一致会报 ORA-46952
  Oracle 21c / 23ai  密码策略更严格,弱口令可能被 profile 拒绝

执行前提:
  1. 以 oracle 用户或具备 dba 组权限的账号执行
  2. 改密本身不需要停库;仅 --rebuild-pwfile 需要停库
  3. Data Guard 环境改密后需同步密码文件到备库
  4. RAC 环境需将密码文件分发到所有节点

退出码:
  0  成功
  1  执行失败
  2  参数错误

示例:
  bash ${SCRIPT_NAME} --help
  bash ${SCRIPT_NAME} -u SYS
  bash ${SCRIPT_NAME} -u SYSTEM -s ORCL -p 'N3wP@ssw0rd'
  bash ${SCRIPT_NAME} -u SYS --old-password 'oldpass'
  bash ${SCRIPT_NAME} -u SYS --rebuild-pwfile --entries 20
  bash ${SCRIPT_NAME} --dry-run
EOF
}

err(){  echo "[错误] $*" >&2; }
info(){ echo "[信息] $*"; }
warn(){ echo "[警告] $*" >&2; }

# ---------- 参数解析 ----------
DB_USER="SYS"
NEW_PASS="${ORACLE_NEW_PASS:-}"
OLD_PASS="${ORACLE_OLD_PASS:-}"
ORACLE_SID_ARG=""
ORACLE_HOME_ARG=""
REBUILD_PW=0
ENTRIES=10
DO_VERIFY=1
DRY_RUN=0

while [ $# -gt 0 ]; do
  case "$1" in
    -h|--help)    usage; exit 0 ;;
    -V|--version) echo "${SCRIPT_NAME} ${SCRIPT_VERSION}"; exit 0 ;;
    -u|--user)      [ $# -ge 2 ] || { err "$1 需要参数"; exit 2; }; DB_USER="$2"; shift 2 ;;
    --user=*)       DB_USER="${1#*=}"; shift ;;
    -p|--password)  [ $# -ge 2 ] || { err "$1 需要参数"; exit 2; }; NEW_PASS="$2"; shift 2 ;;
    --password=*)   NEW_PASS="${1#*=}"; shift ;;
    --old-password) [ $# -ge 2 ] || { err "$1 需要参数"; exit 2; }; OLD_PASS="$2"; shift 2 ;;
    --old-password=*) OLD_PASS="${1#*=}"; shift ;;
    -s|--sid)       [ $# -ge 2 ] || { err "$1 需要参数"; exit 2; }; ORACLE_SID_ARG="$2"; shift 2 ;;
    --sid=*)        ORACLE_SID_ARG="${1#*=}"; shift ;;
    -O|--oracle-home) [ $# -ge 2 ] || { err "$1 需要参数"; exit 2; }; ORACLE_HOME_ARG="$2"; shift 2 ;;
    --oracle-home=*) ORACLE_HOME_ARG="${1#*=}"; shift ;;
    --rebuild-pwfile) REBUILD_PW=1; shift ;;
    --entries)      [ $# -ge 2 ] || { err "$1 需要参数"; exit 2; }; ENTRIES="$2"; shift 2 ;;
    --entries=*)    ENTRIES="${1#*=}"; shift ;;
    --no-verify)    DO_VERIFY=0; shift ;;
    --dry-run)      DRY_RUN=1; shift ;;
    --)             shift; break ;;
    -*)             err "未知选项:$1"; echo "使用 ${SCRIPT_NAME} --help 查看帮助" >&2; exit 2 ;;
    *)              err "多余的参数:$1"; echo "使用 ${SCRIPT_NAME} --help 查看帮助" >&2; exit 2 ;;
  esac
done

[ $# -gt 0 ] && { err "多余的参数:$*"; exit 2; }

# ---------- 环境准备 ----------
[ -n "$ORACLE_HOME_ARG" ] && export ORACLE_HOME="$ORACLE_HOME_ARG"
[ -n "$ORACLE_SID_ARG" ]  && export ORACLE_SID="$ORACLE_SID_ARG"

if [ -z "${ORACLE_HOME:-}" ]; then
  for h in /u01/app/oracle/product/*/dbhome* /opt/oracle/product/*/dbhome*; do
    [ -x "$h/bin/sqlplus" ] && export ORACLE_HOME="$h" && break
  done
fi
[ -z "${ORACLE_HOME:-}" ] && { err "未找到 ORACLE_HOME,请用 -O 指定"; exit 1; }
[ -x "$ORACLE_HOME/bin/sqlplus" ] || { err "$ORACLE_HOME/bin/sqlplus 不存在"; exit 1; }
export PATH="$ORACLE_HOME/bin:$PATH"
SQLPLUS="$ORACLE_HOME/bin/sqlplus"

if [ -z "${ORACLE_SID:-}" ]; then
  if [ -f /etc/oratab ]; then
    ORACLE_SID=$(awk -F: '$3=="Y"{print $1; exit}' /etc/oratab)
  fi
fi
[ -z "${ORACLE_SID:-}" ] && { err "未确定 ORACLE_SID,请用 -s 指定"; exit 1; }
export ORACLE_SID
info "ORACLE_HOME:$ORACLE_HOME"
info "ORACLE_SID :$ORACLE_SID"

# ---------- 密码输入与校验 ----------
if [ -z "$NEW_PASS" ]; then
  printf "请输入 %s 的新密码:" "$DB_USER"
  stty -echo 2>/dev/null
  read -r NEW_PASS
  stty echo 2>/dev/null
  echo
fi
[ -z "$NEW_PASS" ] && { err "新密码不能为空"; exit 2; }

case "$NEW_PASS" in
  *'"'*) err "密码不能包含双引号"; exit 2 ;;
  *"'"*) err "密码不能包含单引号"; exit 2 ;;
esac
case "$DB_USER" in
  *'"'*|*"'"*|*" "*) err "用户名不合法"; exit 2 ;;
esac

# ---------- 输出 SQL ----------
info "目标账号:$DB_USER"
if [ "$DRY_RUN" -eq 1 ]; then
  echo "----- 将执行(dry-run,未实际执行)-----"
  echo "ALTER USER \"$DB_USER\" IDENTIFIED BY \"******\";"
  [ "$REBUILD_PW" -eq 1 ] && \
    echo "orapwd file=\$ORACLE_HOME/dbs/orapw$ORACLE_SID password=\"******\" entries=$ENTRIES force=y"
  echo "---------------------------------------"
  exit 0
fi

# ---------- 连接方式探测 ----------
AUTH_MODE=""

probe_os_auth(){
  local out
  out=$("$SQLPLUS" -s /nolog 2>&1 <<'EOF'
SET HEADING OFF FEEDBACK OFF PAGESIZE 0 TRIMSPOOL ON
WHENEVER SQLERROR EXIT SQL.SQLCODE
CONNECT / AS SYSDBA
SELECT 'OSAUTH_OK' FROM dual;
EXIT
EOF
)
  case "$out" in *OSAUTH_OK*) return 0 ;; *) return 1 ;; esac
}

probe_pwfile_auth(){
  [ -z "$OLD_PASS" ] && return 1
  local out
  out=$("$SQLPLUS" -s /nolog 2>&1 <<EOF
SET HEADING OFF FEEDBACK OFF PAGESIZE 0 TRIMSPOOL ON
WHENEVER SQLERROR EXIT SQL.SQLCODE
CONNECT $DB_USER/"$OLD_PASS" AS SYSDBA
SELECT 'PWFILE_OK' FROM dual;
EXIT
EOF
)
  case "$out" in *PWFILE_OK*) return 0 ;; *) return 1 ;; esac
}

if probe_os_auth; then
  AUTH_MODE="os"
  info "连接方式:本地 OS 认证"
elif probe_pwfile_auth; then
  AUTH_MODE="pwfile"
  info "连接方式:密码文件认证"
else
  err "无法以 SYSDBA 身份连接数据库"
  err "OS 认证失败,且未提供可用的 --old-password"
  warn "如需在完全无法登录时重置,请使用 --rebuild-pwfile 重建密码文件"
  exit 1
fi

# ---------- 生成并执行改密语句 ----------
CONNECT_LINE="CONNECT / AS SYSDBA"
[ "$AUTH_MODE" = "pwfile" ] && CONNECT_LINE="CONNECT $DB_USER/\"$OLD_PASS\" AS SYSDBA"

run_alter(){
  "$SQLPLUS" -s /nolog 2>&1 <<EOF
SET HEADING OFF FEEDBACK OFF PAGESIZE 0 TRIMSPOOL ON ECHO OFF
WHENEVER SQLERROR EXIT SQL.SQLCODE
$CONNECT_LINE
ALTER USER "$DB_USER" IDENTIFIED BY "$NEW_PASS";
SELECT 'ALTER_OK' FROM dual;
EXIT
EOF
}

OUT=$(run_alter)
RC=$?
printf '%s\n' "$OUT"

if [ "$RC" -ne 0 ] || ! printf '%s\n' "$OUT" | grep -q 'ALTER_OK'; then
  err "改密失败,请根据上面的 ORA- 错误定位"
  case "$OUT" in
    *ORA-28003*|*ORA-28001*) warn "密码复杂度不满足 profile 的密码校验函数,请改用更复杂的密码" ;;
    *ORA-01031*)             warn "权限不足,确认执行用户属于 dba 组或具备 SYSDBA" ;;
    *ORA-01017*)             warn "旧密码错误或密码文件不可用" ;;
    *ORA-28000*)             warn "账号被锁定,可先 ALTER USER $DB_USER ACCOUNT UNLOCK" ;;
  esac
  exit 1
fi
info "ALTER USER 执行成功"

# ---------- 可选:重建密码文件 ----------
if [ "$REBUILD_PW" -eq 1 ]; then
  PWFILE="$ORACLE_HOME/dbs/orapw$ORACLE_SID"
  [ -f "$ORACLE_HOME/dbs/orapw${ORACLE_SID}" ] || \
    warn "未找到现有密码文件 $PWFILE,将直接创建"
  warn "重建密码文件需要停止实例,请确认已具备维护窗口"
  info "关闭实例..."
  "$SQLPLUS" -s /nolog >/dev/null 2>&1 <<EOF
CONNECT / AS SYSDBA
SHUTDOWN IMMEDIATE
EXIT
EOF
  sleep 3
  info "备份旧密码文件(如存在)..."
  [ -f "$PWFILE" ] && cp -a "$PWFILE" "${PWFILE}.bak.$(date +%Y%m%d%H%M%S)"
  info "执行 orapwd 重建..."
  if "$ORACLE_HOME/bin/orapwd" file="$PWFILE" password="$NEW_PASS" \
       entries="$ENTRIES" force=y; then
    info "密码文件重建成功:$PWFILE"
  else
    err "orapwd 执行失败"
  fi
  info "启动实例..."
  "$SQLPLUS" -s /nolog >/dev/null 2>&1 <<EOF
CONNECT / AS SYSDBA
STARTUP
EXIT
EOF
fi

# ---------- 验证 ----------
if [ "$DO_VERIFY" -eq 1 ]; then
  info "验证新密码登录..."
  VOUT=$("$SQLPLUS" -s /nolog 2>&1 <<EOF
SET HEADING OFF FEEDBACK OFF PAGESIZE 0 TRIMSPOOL ON
WHENEVER SQLERROR EXIT SQL.SQLCODE
CONNECT $DB_USER/"$NEW_PASS" AS SYSDBA
SELECT 'VERIFY_OK' FROM dual;
EXIT
EOF
)
  if printf '%s\n' "$VOUT" | grep -q 'VERIFY_OK'; then
    info "验证通过:新密码可以正常登录"
  else
    warn "新密码验证失败,输出如下:"
    printf '%s\n' "$VOUT"
    warn "若字典可登录但密码文件失败,请加 --rebuild-pwfile 重建密码文件"
    exit 1
  fi
fi

info "完成"
exit 0

使用示例

bash ora_chpass.sh --help
bash ora_chpass.sh -u SYS
bash ora_chpass.sh -u SYSTEM -s ORCL -p 'N3wP@ssw0rd'
bash ora_chpass.sh -u SYS --old-password 'oldpass'
bash ora_chpass.sh -u SYS --rebuild-pwfile --entries 20
bash ora_chpass.sh --dry-run

关键点说明

项目 说明
改密不需要停库 ALTER USER 在线执行,对业务无中断;只有重建密码文件才需要 SHUTDOWN IMMEDIATE
双密码源 数据字典与密码文件是两套凭证。字典改成功但远程 AS SYSDBA 仍报 ORA-01017 时,说明密码文件未同步,需 orapwd 重建
OS 认证优先 本地 sqlplus / as sysdba 不依赖任何密码,是最可靠的入口;要求执行用户在 dba 组或 ORA_DBA 组
密码文件位置 Linux 为 $ORACLE_HOME/dbs/orapw$ORACLE_SID;Windows 为 %ORACLE_HOME%\database\PWD%ORACLE_SID%.ora,两者不能混用
密码复杂度 12c 起 profile 常启用 ora12c_verify_function,弱口令会被 ORA-28003 拒绝
密码含特殊字符 必须用双引号包裹,脚本已拒绝含单双引号的密码以避免转义问题
12c 多租户 SYS 是公共用户,在 CDB$ROOT 下修改即可;PDB 内的本地用户需切到对应容器
密码文件条目上限 条目数不足会报 ORA-01996: GRANT failed: password file is full,重建时用 --entries 放大
密码文件参数 需 REMOTE_LOGIN_PASSWORDFILE=EXCLUSIVE(或 SHARED)才启用密码文件认证;为 NONE 时远程 SYSDBA 无法登录
权限核对 用 SELECT * FROM V$PWFILE_USERS; 确认账号具备 SYSDBA 权限

高风险场景提示

  1. Data Guard 主备库:主库改密后必须把密码文件同步到备库。12c 之后若主备密码文件格式不一致,备库 MRP 会因 ORA-46952 终止,Real Time Apply 停止。建议做法是主库改密后直接复制密码文件到备库,而不是在备库单独执行 orapwd。
  2. RAC 环境:密码文件通常放在共享存储(ASM 或集群文件系统)上,单节点修改即可;若各节点本地各有一份,必须逐节点同步,否则节点间 SYSDBA 登录行为不一致。
  3. ASM 实例:ASM 不支持直接 ALTER USER SYS IDENTIFIED BY,会触发 ORA-00600 [15051] 或 ORA-01109,必须停实例后用 orapwd 重建。
  4. 修改 SYSTEM 或其他 DBA 账号:与 SYS 不同,这些账号的密码只存在于数据字典,不影响密码文件;但若它们被授予了 SYSDBA,则密码文件中的条目需通过重建或重新 GRANT SYSDBA 保持一致。
  5. 改密后务必同步:应用连接串、监控采集、备份脚本(RMAN)、Data Pump 目录、自动化运维平台中的旧密码都要更新,否则会引发批量连接失败。

posted on 2026-10-27 10:26  刘子毅  阅读(24)  评论(0)    收藏  举报

导航