Apache HTTP Server 的认证模块默认依赖文件存储用户信息,例如 htpasswd 生成的密码文件。当账号数量变多、需要由业务系统动态维护,或者希望把用户认证和若干应用共用一个账户库时,文件方案就显得笨重。SQLite 是一个轻量级关系数据库,数据保存在单个文件中,不需要额外数据库服务,非常适合作为 Apache 的认证后端。

要让 Apache 直接使用 SQLite 做认证,核心依赖两个模块:mod_dbd 负责数据库连接管理和 SQL 执行,mod_authn_dbd 则利用数据库查询结果完成 Basic 认证。认证流程中,浏览器弹出 Basic 登录框,用户输入账号密码后,Apache 将用户名作为参数传入配置好的 SQL 查询语句,再将返回的密码哈希与请求中的密码进行比较,最终决定是否允许访问。
一、SQLite 认证模块的安装与加载
在 Debian 或 Ubuntu 系统上,通常先启用模块:sudo a2enmod dbd authn_dbd auth_basic authz_core。在 RHEL 或 CentOS 系统上,则需要在 httpd.conf 中确认对应的 LoadModule 行存在。mod_dbd 和 mod_authn_dbd 通常包含在 Apache 发行版或 httpd 模块包中,不一定需要额外安装数据库驱动包,但必须启用。
需要注意,模块加载顺序不影响认证流程,但 mod_authn_dbd 必须在运行时能找到 mod_dbd 提供的 DBD 连接池。如果只加载 mod_authn_dbd 而未加载 mod_dbd,认证时会报 DBD 未配置或驱动不可用错误。检查模块是否成功加载可以使用 apachectl -M,在列表中找到 dbd_module 和 authn_dbd_module。
二、准备 SQLite 用户数据库
SQLite 数据库文件适合放在 Apache 用户可读但外部不可通过 URL 访问的目录中,例如 /etc/apache2/auth/。创建数据库可以直接使用 sqlite3 命令行工具,如果服务器没有安装,可以先执行 sudo apt install sqlite3 或 sudo yum install sqlite。数据库文件权限建议设为 640,owner 为 root,group 为 www-data,这样 Apache 子进程可读但不能随意修改。
用户表至少包含用户名和密码哈希两列,建议增加 active 字段便于停用账号。下面创建 auth_users 表并插入一个测试用户。密码字段尽量存储不可逆哈希,而不是明文。SQLite 本身不提供加密函数,因此需要在应用层或脚本中先计算好哈希再写入。
CREATE TABLE auth_users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
active INTEGER DEFAULT 1 CHECK(active IN (0,1)),
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO auth_users(username, password_hash, active)
VALUES ('alice', '{SHA}qUqP5cyxm6YcTAhz05Hph5gvu9M=', 1);
上面的示例使用了 Apache 可识别的 {SHA} 前缀,表示该值是密码的 SHA-1 哈希。Apache 2.4 中 mod_authn_dbd 支持多种密码格式,常见的有 {PLAIN}、{SHA}、{SSHA}、$2y$ 开头的 bcrypt 哈希,以及 $apr1$ 开头的 Apache 专用 MD5 哈希。如果密码字段直接存储明文,需要写成 {PLAIN}password 形式,但生产环境不建议这样做。
生成 bcrypt 哈希最方便的方法是使用 htpasswd -B -n alice,它会输出类似 alice:$2y$10$... 的结果,只取冒号后的哈希部分写入 SQLite。也可以使用 Python 的 passlib 库、PHP 的 password_hash 函数或 Go 的 bcrypt 包生成。关键是写入数据库后不要把明码密码留在命令行历史或脚本日志中。
三、配置 mod_dbd 与认证查询
数据库准备好后,需要在 Apache 配置中先定义 mod_dbd 连接。SQLite 驱动的连接字符串比较特殊,不像 MySQL 或 PostgreSQL 那样需要主机、端口、用户名和密码,而是直接指定数据库文件的完整路径。下面这段配置通常放在虚拟主机或全局配置中。
<IfModule mod_dbd.c>
DBDriver sqlite3
DBDParams "/etc/apache2/auth/users.sqlite"
DBDMin 1
DBDMax 8
DBDKeep 4
DBDExptime 300
</IfModule>
<Directory "/var/www/html/protected">
AuthType Basic
AuthName "Restricted Area"
AuthBasicProvider dbd
Require valid-user
AuthDBDUserPWQuery "SELECT password_hash FROM auth_users WHERE username = %s AND active = 1"
AuthDBDUserRealmQuery "SELECT 'Restricted Area' FROM auth_users WHERE username = %s AND active = 1"
</Directory>
这里 DBDriver sqlite3 告诉 mod_dbd 使用 SQLite 驱动,DBDParams 后的字符串就是 SQLite 文件路径。DBDMin、DBDMax、DBDKeep 和 DBDExptime 控制连接池的初始连接数、最大连接数、空闲连接数和过期时间。SQLite 维护连接池时开销很低,但也不要设置过大,一般 DBDMin 1、DBDMax 8 足够中小站点使用。
AuthDBDUserPWQuery 是最关键的指令,它定义验证密码时执行的 SQL。用户名会被传入 %s 占位符,例如用户输入 alice,实际执行的 SQL 就是查询 alice 这条记录的 password_hash。如果查询返回空结果,认证直接失败;如果返回多行,通常只取第一行。SQL 中建议使用 AND active = 1 限制禁用账号,这样停用账号不需要删除记录。
AuthDBDUserRealmQuery 用来校验领域名称,虽然 Basic 认证中有时可以省略,但保留它能避免某些客户端缓存错误凭证。该查询必须返回一个与 AuthName 一致的值,示例中直接返回固定字符串 Restricted Area。若两个查询的字段名或占位符数量不匹配,Apache 会在错误日志中记录详细原因。
四、使用脚本维护用户与密码
SQLite 认证后端的一个重要好处是账号维护方便,不需要修改 Apache 配置,也不必担心 htpasswd 文件并发写入异常。可以写一个 Python 脚本,将 bcrypt 哈希写入数据库,同时处理新增、更新和停用逻辑。下面示例展示了如何更新 alice 用户的密码哈希。
import sqlite3
import bcrypt
DB_PATH = '/etc/apache2/auth/users.sqlite'
def set_password(username, plain_password):
password_hash = bcrypt.hashpw(plain_password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
conn = sqlite3.connect(DB_PATH)
conn.execute('UPDATE auth_users SET password_hash = ?, active = 1 WHERE username = ?',
(password_hash, username))
conn.commit()
conn.close()
set_password('alice', 'S3curePass!')
这段代码使用 bcrypt 库生成带盐哈希,把结果存入 password_hash 列。bcrypt.hashpw 返回 bytes,因此需要 decode 成字符串再写入 SQLite。执行脚本时建议在 root 权限下运行,因为数据库文件对 apache 用户只读。实际业务中也可以让一个管理后台服务来操作这个 SQLite 文件,但要注意文件锁和路径一致性,避免多个进程同时写入造成 database is locked 错误。
除了 Python,也可以使用 PHP 脚本、Shell 命令或 Go 服务维护用户表。无论哪种方式,都必须保证写入的哈希格式能被 Apache 识别。若是 bcrypt,确保不是较老的 $2a$ 或其他不兼容前缀;建议统一使用 openssl passwd -6 生成 SHA-512 crypt 哈希,或使用 htpasswd -B 统一生成 bcrypt 哈希,保持所有账号格式一致。
五、常见错误与性能注意事项
首次配置完成后,访问受保护目录如果一直返回 500 Internal Server Error,通常先去 /var/log/apache2/error.log 或 /var/log/httpd/error_log 查看具体信息。常见错误包括:DBDParams 路径不可读、SQLite 驱动程序不可用、SQL 查询字段数量不对、占位符写法错误。SQLite 文件所在目录必须对 Apache 运行用户可执行,文件本身必须可读,否则 mod_dbd 打开数据库会失败。
如果认证始终失败但浏览器一直弹出登录框,可以先确认用户表里是否有 active = 1 的记录,以及密码哈希是否带正确前缀。可以使用 sqlite3 /etc/apache2/auth/users.sqlite "select username, active from auth_users;" 快速检查。另一个容易忽略的问题是 AuthName 与 AuthDBDUserRealmQuery 返回的值不一致,或者客户端发送的请求并没有带 Authorization 头,这常见于反向代理没有正确转发 Authorization 的情况。
性能方面,SQLite 适合并发不高的中小规模认证。Apache 的每个数据库连接在请求结束后由 mod_dbd 回收复用,不必每次请求都打开数据库文件,因此连接池参数能显著降低开销。对于高并发或需要多台服务器共享账户库的场景,应迁移到 MySQL 或 PostgreSQL,并将 DBDParams 改成对应的主机、端口、用户和库名。SQLite 文件只建议单机使用,放置在网络文件系统上会引入锁和一致性问题。
安全方面,不要把 SQLite 文件放在 DocumentRoot 下,否则可能被下载。Basic 认证只使用 Base64 编码传输密码,并非加密,因此在公网环境中应配合 TLS 使用。生产环境还建议设置 AuthBasicAuthoritative Off 或组合其他认证提供者,但本例中保持简单配置即可。
Apache认证SQLitemod_authn_dbd修改时间:2026-09-24 21:07:00