
如果你是被org.postgresql.util.PSQLException这个报错带到这里的那我要先说一句你搜的 pg 和我这篇的 pg 大概率不是同一个。网上搜 pg能搜出来一堆游戏试玩、模拟器之类的乱七八糟内容但咱们今天只聊 PostgreSQL而且是 PostgreSQL 连接失败里最典型、最容易让人懵圈的那一个——报错末尾跟着一坨乱码仔细看里面藏着no pg_hba.conf entry for host。这周刚好有个同事踩了一模一样的坑测试环境的 Java 服务连数据库直接糊了一屏红。我过去一看报错是这种画风org.postgresql.util.PSQLException: FATAL: : û “192.168.1.50“, user appuser, database appdb, no encryption一长串乱码挡在前面后面 IP、用户名、数据库名倒是清清楚楚。很多人看到这里就慌了以为是数据库坏了、密码不对、或者服务挂了。其实都没猜中。这个报错翻译成正常中文就是PostgreSQL 在pg_hba.conf里找不到允许192.168.1.50这台机器访问的规则。说白了你的客户端 IP 不在数据库的“准入名单”里。这篇文章我就按实际排查的顺序把这个报错从头到尾拆一遍乱码怎么来的、pg_hba.conf到底怎么匹配、改完之后为什么还有可能连不上以及自建库和云数据库里“白名单”的正确配置方式。不管你是刚入门的后端还是被线上问题逼着看 PostgreSQL 的运维照着这条链路走完基本能解决九成以上同款问题。1. 报错拆解从 FATAL 到乱码这条异常到底在说什么1.1 一行报错里真正有用的三块信息先把报错里的无效信息剥掉。org.postgresql.util.PSQLException只是 PostgreSQL JDBC 驱动抛出的统一异常类不管是网络不通、认证失败、还是 SQL 写错外层包装都是它。真正有价值的是FATAL:后面的服务端返回信息。像我前面贴的这条把乱码还原成英文完整的服务端信息长这样FATAL: no pg_hba.conf entry for host 192.168.1.50, user appuser, database appdb, no encryption这里面有三个关键信息host 192.168.1.50发起连接的客户端 IP这是本次连接被拒绝的直接对象。user appuser你用的数据库角色名。database appdb你要访问的数据库名。no encryption客户端和服务端之间没有用 SSL 加密。这个信息在排查 hostssl 规则时会用到下面第 4 章会细说。看到no pg_hba.conf entry for host就意味着你的请求根本没有走到“验证密码”这一步而是刚过来就被挡在了门外。所以密码对不对、账号是否存在在这个阶段都不重要——先解决准入问题再去谈凭证。1.2 乱码 “” 是怎么来的先说结论这是显示问题不是数据库坏了。PostgreSQL 服务端返回的错误信息是 UTF-8 编码的文本。但很多 Java 进程跑在 Windows 上或者 JVM 默认使用 GBK 编码输出日志于是 UTF-8 的字节序列被按错误的字符集解码就变成了这种占位符和û这种莫名其妙的历史遗留字符。我之前还见过更夸张的一整条中文错误信息全变成??????但 IP 和数字永远是完好的因为 ASCII 字符在不同字符集之间转来转去不容易“碎掉”。遇到这种情况别盯着控制台猜测。三招解决临时在 Windows 控制台执行chcp 65001切到 UTF-8 再跑一次大概率能看到正常文字。如果服务已经跑起来去查看 PostgreSQL 服务端日志日志文件里存的原始内容一定是可读的。或者索性把报错里的 IP、user、database 摘出来这三个字段够你判断问题了。顺带一提Java 进程启动时加上-Dfile.encodingUTF-8能减少很多这种“日志乱码”造成的干扰。如果你用的 IDE把控制台编码也统一改成 UTF-8省得以后再被这种表面问题带偏。1.3 别把三类连接异常混为一谈PostgreSQL 连接失败其实有几种完全不同的表现我见过不少人把它们的排查方向搞反白白浪费时间。这里直接用表格区分报错片段含义优先排查方向no pg_hba.conf entry for host连接被准入规则拒绝密码都没机会验证pg_hba.conf规则、来源 IPpassword authentication failed准入通过了但认证方式或密码不对密码、认证算法、密码存储格式Connection refused服务没监听、端口不通、防火墙拦截listen_addresses、监听端口、安全组/防火墙这个表格建议收进你的排错脑图里。绝大多数no pg_hba.conf entry问题根源都集中在pg_hba.conf这一层。所以下一章我们把这个文件彻底讲透。2. pg_hba.conf 逐行匹配规则为什么你的 IP 被拒之门外2.1 pg_hba.conf 是连接准入名单不是密码文件pg_hba.conf全称是 PostgreSQL Host-Based Authentication Configuration直译过来就是“基于主机的认证配置”。它定义的是什么样的连接来源、用什么身份、访问哪个数据库、采用什么方式验证。很多人把它当成“密码文件”其实不对。密码存在 PostgreSQL 的系统表里pg_hba.conf只负责在连接建立时决定“要不要让你进入验证环节”。打个比方pg_hba.conf是小区门口的访客登记闸机只有闸机放行你才能走到住户门口按门铃而按门铃按的是什么密码那是另一道流程。在 Debian/Ubuntu 系的 PostgreSQL 安装包里默认的pg_hba.conf大概是这个画风local all postgres peer local all all peer host all all 127.0.0.1/32 scram-sha-256 host all all ::1/128 scram-sha-256看到没默认规则里只有本机回环地址127.0.0.1和::1有host类型的准入规则。所以你在服务器本机上用psql连没问题一旦换到另一台机器来源 IP 是192.168.1.50这种内网地址从头到尾扫一遍文件也找不到匹配的 host 规则于是直接抛no pg_hba.conf entry。2.2 一条规则的六个字段随便打开pg_hba.conf可以看到每一行有效规则都由以下字段组成# TYPE DATABASE USER ADDRESS METHOD拆开来看TYPE连接类型。local表示 Unix Sockethost表示 TCP/IP 连接hostssl表示必须使用 SSL 的 TCP 连接hostnossl表示不使用 SSL 的 TCP 连接。DATABASE允许访问的数据库名。可以写具体库名可以用all匹配所有库也可以用逗号分隔多个库。USER允许登录的数据库角色。all匹配所有角色逗号分隔多个角色带前缀表示角色组。ADDRESS来源 IP 或网段用 CIDR 格式。IPv4 写成192.168.1.50/32IPv6 写成::1/128网段写192.168.1.0/24。METHOD认证方式。trust是免密直接放行scram-sha-256是当前主流的强加密认证md5是老版本常见的弱认证peer是 Unix Socket 下校验操作系统用户名还有ldap、radius、cert等企业级方式。配置的时候最容易漏的是 ADDRESS 这一项。有的人只写了host all all all scram-sha-256然后发现怎么都不生效——因为host类型的规则必须有具体来源地址直接写all会导致规则解析失败。2.3 首条匹配生效新规则为什么会被“静默拦截”pg_hba.conf的匹配规则是逐行从上往下扫描命中最靠前的那一条就生效后面的规则不再看。这是整个排查过程中最容易被忽略的坑。举个例子。假设文件里有这么两行host all all 192.168.1.0/24 md5 host all appuser 192.168.1.50/32 scram-sha-256你的应用服务器 IP 是192.168.1.50本意是想让它走下面这条scram-sha-256规则。但因为第一条192.168.1.0/24已经包含了192.168.1.50而且位置靠前所以实际生效的是第一条。以后排查“为什么我改了配置还是不行”时别只看文件末尾加没加还得看前面有没有更宽的规则抢了先。想快速看清当前文件的解析结果可以使用 PostgreSQL 10 以上版本提供的视图SELECT line_number, type, database, user_name, address, auth_method FROM pg_hba_file_rules WHERE error IS NULL;这能把你编辑后真实生效的规则列表拉出来比用肉眼看文件舒服得多。如果把WHERE error IS NULL去掉还能看到哪一行解析报错了这也是排错利器。3. 手把手修复定位 hba_file、补规则、重载、验证一条龙3.1 第一步确认正在用的 pg_hba.conf 路径很多人凭经验猜配置文件位置这在多实例环境下特别容易翻车。不同发行版、不同版本、不同安装方式路径可能都不一样。与其猜不如直接问数据库。用任意能连上数据库的客户端执行SHOW hba_file;或者从命令行psql -U postgres -h 127.0.0.1 -p 5432 -c SHOW hba_file;执行完会返回类似这样的绝对路径/etc/postgresql/15/main/pg_hba.conf在修改任何内容之前先做个备份。这是排错最基本的操作避免改坏了连回滚都麻烦sudo cp /etc/postgresql/15/main/pg_hba.conf /etc/postgresql/15/main/pg_hba.conf.bak.$(date %F)3.2 第二步添加规则并选对认证方式找到了文件就把它打开在合适的位置加一行规则。这里的关键是范围一定要精确。比如应用服务器的 IP 是192.168.1.50访问的库是appdb用的角色是appuser那就写成host appdb appuser 192.168.1.50/32 scram-sha-256如果整个开发网段都能访问也可以写成192.168.1.0/24。但我的建议是能精确到单个 IP 就不要开整个网段能开网段就不要开0.0.0.0/0。白名单这玩意范围越大出事之后波及面越广。认证方式的选择也值得多说两句trust免密连接适合本地临时实验上了生产就是裸奔。scram-sha-256PostgreSQL 14 开始默认的密码认证方式安全性强推荐使用。md5老协议PostgreSQL 10 之前的年代比较常见现在能不用就不用。password密码明文传输千万别在生产环境用抓包就是裸奔。如果你拿不准服务器上的 PostgreSQL 版本用SELECT version();先看一下。14 及以上版本的默认密码加密基本都是scram-sha-256规则就写scram-sha-256。老版本习惯用 md5但我仍然建议趁现在把密码重新设置一遍统一切到 scram。3.3 第三步重载而不是重启很多人改完配置文件第一反应是重启 PostgreSQL 服务。其实完全没必要pg_hba.conf的修改只需要重载配置就能生效而且重载不会中断已有连接。方式一直接执行 SQLSELECT pg_reload_conf();方式二通过系统服务重载sudo systemctl reload postgresql这种方式比重启安全得多。重启会断开所有连接在线应用可能直接雪崩。重载只是让进程重新读取配置文件对新连接生效老连接照常运行。3.4 第四步从应用视角验证配置重载后别急着说解决了。回到那台报错的客户端机器上从它的视角重新连一次psql host192.168.1.100 port5432 dbnameappdb userappuser password你的密码如果你是在 Java 服务里复现那就重新启动服务再看日志。如果还是报no pg_hba.conf entry先别急按下面几步排查确认192.168.1.50真的是那台机器的出口 IP。有的服务器有多块网卡或者走了 NAT出口 IP 可能跟你以为的不一样。确认新增的规则没有被前面的宽规则拦截。用pg_hba_file_rules视图确认规则解析成功error列没有报错信息。同时开一个终端观察 PostgreSQL 日志sudo tail -f /var/log/postgresql/postgresql-15-main.log日志里会清清楚楚记录每个连接被拒绝的原因和来源 IP。只要这一步能对上号问题基本就水落石出了。4. 改完还是连不上的四种情况SSL、认证算法、监听、规则顺序4.1 hostssl 和客户端 sslmode 不对付有一类问题很隐蔽你的pg_hba.conf里写的规则是hostssl但客户端的 JDBC 连接串里并没有启用 SSL或者显式设置了sslmodedisable。这时候连接不会命中hostssl规则客户端看到的又是no pg_hba.conf entry让人误以为规则没生效。举个 JDBC 连接串的例子jdbc:postgresql://192.168.1.100:5432/appdb?sslmodedisable服务端规则却是hostssl appdb appuser 192.168.1.50/32 scram-sha-256这两个设置是矛盾的。解决办法有两个方向如果数据库对 SSL 没有那么强的要求把规则里的hostssl改成host兼容普通 TCP 连接。如果安全策略强制要求 SSL那就把 JDBC 连接串里的sslmodedisable改成sslmoderequire。在配置之前最好先和团队确认数据库是否已经开启了 SSL别随手改完反而暴露明文流量。4.2 md5 和 scram 打架还有一种情况也经常出现规则确实匹配上了但报错变成了password authentication failed。这时候问题从“准入”转移到了“认证”。PostgreSQL 14 开始默认password_encryption是scram-sha-256。但很多老库是从 PostgreSQL 10 之前升级过来的用户密码可能仍然是以 md5 哈希的形式存在系统表里的。如果你把pg_hba.conf里的认证方式从md5改成了scram-sha-256而密码还是旧格式PostgreSQL 就没有办法用 scram 的流程去校验最终报错就是密码认证失败。处理方式也比较直接重新设置一次密码让密码按新的加密方式存储SET password_encryption scram-sha-256; ALTER USER appuser WITH PASSWORD 新的强密码;改完之后再用新密码连接。这里提醒一句改密码之前先想清楚有多少个服务在用这个账号避免改完一堆应用跟着一起挂。4.3 卡在网络层listen_addresses、防火墙、安全组另一种“还连不上”的典型情况是问题根本不在pg_hba.conf而是网络层就没放行。这类问题的报错通常是Connection refused而不是no pg_hba.conf entry。如果你看到的是Connection refused检查顺序如下先看 PostgreSQL 监听地址。postgresql.conf里默认是listen_addresses localhost这意味着服务只监听本机回环地址外网 IP 根本连不进来。要允许远程连接得改成listen_addresses *或者明确指定某个内网 IP。改完同样需要重载配置。然后确认端口有没有在监听sudo ss -lntp | grep 5432如果没有任何输出说明服务没起来或者监听配置没生效。如果显示127.0.0.1:5432那就是监听地址没改对。接下来检查防火墙。CentOS 系先看 firewalldsudo firewall-cmd --list-ports sudo firewall-cmd --permanent --add-port5432/tcp sudo firewall-cmd --reload云服务器还要看安全组规则在控制台里把 5432 端口按需放行。这里的顺序很重要先解决网络层再解决准入层最后处理认证层。我排错这么多年这个顺序从来没变过。4.4 新规则被前面的宽泛规则挡了前面提过“首条匹配生效”的机制这里再补一个真实踩坑案例。有次帮同事排一个同样的问题他在pg_hba.conf末尾加了一行很精确的规则reload 之后还是连不上。我打开文件一看文件靠前的位置躺着一行host all all 0.0.0.0/0 reject这行规则把所有来源 IP 的 TCP 连接全部拒绝而且位置靠前。他后面不管加多少条白名单都排在 reject 后面永远不会被匹配到。遇到这种情况解决办法不是把那行reject删了而是要把精确的白名单规则放在宽泛规则之前。比如host appdb appuser 192.168.1.50/32 scram-sha-256 host all all 0.0.0.0/0 reject一行精确放行一行兜底拒绝。这个写法既能保证白名单优先又能挡住其他乱七八糟的来源。这也是我强烈建议在线上文件里保留的默认结构。5. 数据库白名单的正确配置方式自建库与云数据库通用5.1 云数据库的白名单和 pg_hba.conf 是什么关系很多人问我云数据库控制台里配“白名单”和自建库改pg_hba.conf是不是一回事。本质上是但操作上不一样。云数据库比如各种 RDS for PostgreSQL、托管数据库出于安全考虑通常不会把pg_hba.conf直接暴露给用户修改。控制台里的“白名单”或“安全组”其实就是在托管层帮你维护一份“允许访问的来源 IP 列表”底层逻辑和pg_hba.conf是类似的只是入口变成了 Web 页面。所以在云数据库上如果遇到类似no pg_hba.conf entry的报错第一反应应该去看白名单配置里有没有把你当前出口 IP 加进去。有些云厂商的白名单会区分内网地址和公网地址公网访问必须单独加公网 IP别加错了网段。自建库的问题里能直接改pg_hba.conf的一定要珍惜这个自由度也一定要敬畏这个自由度。自由意味着更容易犯错改错一行全网连不上那是深夜事故级别的刺激。5.2 最小化白名单和最小化账号权限白名单配置的核心原则就一句话范围做到最小权限做到最小。先看范围。不要一上来就0.0.0.0/0那是把所有入口都敞开。一个正常运行的项目需要访问数据库的来源其实就那么几类应用服务器内网 IP这是最核心的来源。跳板机或运维管理机的 IP这是管理员手工操作时的来源。办公网出口 IP这是开发人员连测试库的来源。每一类来源都精确到 IP 或网段并配上注释。比如# 生产应用服务器 host appdb appuser 10.10.2.10/32 scram-sha-256 # 跳板机 host appdb admin 10.0.0.5/32 scram-sha-256 # 测试环境办公网 host appdb devuser 192.168.8.0/24 scram-sha-256再看账号。生产环境里应用账号只给必要的权限别直接拿postgres超级用户跑业务。创建账号的 SQL 示例CREATE ROLE appuser LOGIN PASSWORD 换成强密码; GRANT CONNECT ON DATABASE appdb TO appuser;如果应用还需要读写表再按需授予SELECT、INSERT、UPDATE、DELETE权限。账号权限越小数据库被攻破后能造成的破坏就越小。5.3 变更风险控制与快速回滚pg_hba.conf的变更看着只是加几行字实际风险不小。配置错了轻则一个应用连不上重则整个实例的远程连接全断。所以我改这个文件的时候有一套固定的流程改动前备份。备份文件名带日期方便回滚时快速识别。改动后用视图校验。执行SELECT * FROM pg_hba_file_rules WHERE error IS NOT NULL;确保没有解析错误。保留一条“安全通道”。如果你是通过 SSH 在服务器上操作保持当前 SSH 连接不要断开确认新连接能建立后再关终端。万一改错了还能通过这条连接回滚。先小范围验证。先在测试环境把整套改动走一遍再上生产。确认后清理。问题解决后把不再需要的调试规则删掉比如临时加的0.0.0.0/0测试规则一定要记得清干净。排错过程中我还习惯把log_connections打开。这个参数开启后PostgreSQL 会在日志里记录每一次连接尝试包括来源 IP、用户名和连接结果。平时看不觉得有什么真正出问题的时候这是最可靠的现场证据。最后再分享一点个人体会。我处理过的绝大多数no pg_hba.conf entry报错最后都落在两个原因上要么规则没加要么规则加错了位置被前面的宽规则挡了。所以你现在遇到这个问题别慌先去SHOW hba_file;拿到文件路径再开一个pg_hba_file_rules视图确认解析结果中间结合 PostgreSQL 日志看来源 IP基本上十分钟内就能定位。还有一条经验必须强调别为了图省事直接上trust也别顺手放开0.0.0.0/0。这两个操作在开发环境用起来确实爽但在线上就是给自己埋雷。真正稳的方案永远是精确 IP 加scram-sha-256先把准入做好再谈后面的密码和权限。等这个问题解决之后建议把完整的报错信息和这次的处理步骤记到团队的运维文档里这种坑值得沉淀下次谁再遇到照着走一遍就能省下半夜加班的功夫。