要把 OAuth 2.0 授权服务放进 Ubuntu 服务器,比较省心的做法是让 Hydra 专注协议端点,把用户登录和授权页留给现有系统。Hydra 本身不存用户密码,只管理客户端、授权请求、令牌和同意记录。下面从 PostgreSQL 开始搭建一套可运行的本地授权服务。

准备 PostgreSQL 与安装 Hydra
Hydra 支持 PostgreSQL、MySQL、SQLite 等存储后端,生产环境推荐 PostgreSQL。先安装数据库并创建独立库和用户,避免使用超级用户直接连接。Ubuntu 22.04 软件源中的 PostgreSQL 版本足够满足 OAuth 2.0 令牌存储需求,安装完成后需要确认服务已经启动。
sudo apt update sudo apt install -y postgresql sudo systemctl enable --now postgresql sudo -u postgres psql -c "CREATE USER hydra WITH PASSWORD 'hydra_secret';" sudo -u postgres psql -c "CREATE DATABASE hydra OWNER hydra;" sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE hydra TO hydra;"
注意这里密码只是本地演示使用,生产环境应当替换为足够复杂的随机字符串。接下来获取 Hydra 二进制文件。官方在 GitHub 发布页提供 Linux amd64 包,下载后解压并移动到 /usr/local/bin,然后用 hydra version 检查是否可用。
curl -LO https://github.com/ory/hydra/releases/latest/download/hydra_2.x.x-linux_64bit.tar.gz mkdir -p hydra-install tar -xzf hydra_2.x.x-linux_64bit.tar.gz -C hydra-install sudo mv hydra-install/hydra /usr/local/bin/ hydra version
也可以使用 Docker 运行 Hydra,这样不需要手动下载二进制文件。Docker 方式适合快速验证,但要注意容器网络与宿主机端口映射。下面两种方式都会用到相同的环境变量,所以先理解配置项更重要。
docker pull oryd/hydra:v2.2.0
配置系统密钥与 URL 参数
Hydra 启动前必须设置至少两个关键参数:系统密钥 secrets.system 和数据库连接串 dsn。系统密钥用于签名和加密内部数据,长度至少 16 字节,推荐 32 字节并用 base64 编码。可以使用 openssl rand -base64 32 生成。数据库连接串采用 URL 形式,Ubuntu 本地 PostgreSQL 的写法通常是 postgres://hydra:hydra_secret@localhost:5432/hydra?sslmode=disable。
export SECRETS_SYSTEM=$(openssl rand -base64 32) export DSN="postgres://hydra:hydra_secret@localhost:5432/hydra?sslmode=disable" export URLS_SELF_ISSUER="http://localhost:4444" export URLS_CONSENT="http://localhost:3000/consent" export URLS_LOGIN="http://localhost:3000/login" export URLS_LOGOUT="http://localhost:3000/logout"
URLS_SELF_ISSUER 是 OAuth 2.0 发行方标识,必须与外部访问地址一致。开发环境使用 localhost 即可,如果通过反向代理映射到公网域名,这里必须改为 https 域名。URLS_LOGIN 和 URLS_CONSENT 指向你准备自己实现的两个页面端点,Hydra 在用户未登录或需要授权时会携带 login_challenge 或 consent_challenge 跳转过去。
还可以把配置写入 YAML 文件,避免每次启动都设置大量环境变量。下面是一个最小配置,适合放在 /etc/ory-hydra/hydra.yml。
serve:
cookies:
same_site_mode: Lax
urls:
self:
issuer: http://localhost:4444
login: http://localhost:3000/login
consent: http://localhost:3000/consent
logout: http://localhost:3000/logout
secrets:
system:
- "use-a-long-base64-secret-here"
dsn: postgres://hydra:hydra_secret@localhost:5432/hydra?sslmode=disable
使用 YAML 文件时,secrets.system 是一个数组,Hydra 会使用第一个值来签名。生产环境建议定期轮换,把新密钥放在列表前面,旧密钥保留用于校验先前签发的令牌。
执行数据库迁移并启动服务
安装完成并准备好配置后,第一步是执行数据库迁移。迁移过程会创建客户端表、授权请求表、同意表、刷新令牌表等。使用二进制文件直接运行 hydra migrate sql,如果使用 Docker 则需要挂载配置或传入环境变量。
hydra migrate sql --config /etc/ory-hydra/hydra.yml --yes
--yes 参数表示自动确认迁移,不需要交互输入。迁移成功后,可以启动 hydra serve all。这个子命令会同时启动公开端口和管理端口,默认公开端口 4444,管理端口 4445。管理接口只应监听内网地址,比如绑定 127.0.0.1。
hydra serve all --config /etc/ory-hydra/hydra.yml --dangerous-force-http
--dangerous-force-http 仅用于本地开发,生产环境必须使用 HTTPS 反向代理。启动日志中如果看到 Setting up http server on :4444 和 Setting up http server on :4445,说明服务正常监听。
Docker 方式启动时,需要把宿主机的 4444 和 4445 端口映射到容器,并把 YAML 文件挂载进容器。示例命令如下。
docker run -d \ --name hydra \ -p 4444:4444 \ -p 4445:4445 \ -v /etc/ory-hydra:/etc/ory-hydra \ oryd/hydra:v2.2.0 \ serve all --config /etc/ory-hydra/hydra.yml --dangerous-force-http
注意反斜杠在 shell 命令中表示换行,在 Windows 的 CMD 中不能这样写,但在 Ubuntu 的 bash 下可以直接复制。
注册 OAuth 客户端与授权码流程验证
Hydra 启动后不会自动创建客户端,需要调用管理 API 或 hydra create client 命令注册。客户端需要指定 grant_types、response_types、redirect_uris 和 scope。授权码模式必须包含 authorization_code 和 refresh_token,响应类型为 code。
hydra create client \ --endpoint http://127.0.0.1:4445 \ --id demo-client \ --secret demo-client-secret \ --grant-types authorization_code,refresh_token \ --response-types code \ --scope openid,offline_access \ --callbacks http://localhost:8080/callback
注册成功后,可以发起授权请求。浏览器访问 http://localhost:4444/oauth2/auth?client_id=demo-client&redirect_uri=http://localhost:8080/callback&response_type=code&scope=openid&state=random123。Hydra 会检查用户是否登录,未登录则跳转到 URLS_LOGIN 指定的地址,并附加 login_challenge。你需要在这个登录端点实现自己的认证逻辑,认证成功后调用管理 API 接受登录请求。
Hydra v2 没有直接跳过登录确认的开关,需要自己提供一个登录确认接口。下面用 Python 和 Flask 给出一个极简示例,只做演示,不要直接用于生产。
from flask import Flask, request, redirect
import requests, urllib.parse
app = Flask(__name__)
HYDRA_ADMIN = "http://127.0.0.1:4445"
@app.route("/login")
def login():
challenge = request.args.get("login_challenge")
# 演示:直接接受任意用户
r = requests.put(
f"{HYDRA_ADMIN}/admin/oauth2/auth/requests/login/accept",
params={"login_challenge": challenge},
json={"subject": "user-001", "remember": True}
)
return redirect(r.json()["redirect_to"])
if __name__ == "__main__":
app.run(port=3000)
真正使用时,登录页面需要校验用户名密码,然后把认证结果作为 subject 提交给 Hydra。上面的 redirect_to 是 Hydra 返回的下一步地址,用户会被带回授权端点。如果请求的 scope 包含需要同意的权限,还会跳转到 URLS_CONSENT,同意接口要调用 /admin/oauth2/auth/requests/consent/accept。
# 在浏览器中完成授权后,回调地址会附带 code # 用 code 交换 access_token 和 refresh_token curl -X POST http://localhost:4444/oauth2/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -u demo-client:demo-client-secret \ -d "grant_type=authorization_code" \ -d "code=回显的授权码" \ -d "redirect_uri=http://localhost:8080/callback"
响应中会包含 access_token、refresh_token、expires_in 等字段。访问令牌默认是短字符串而不是 JWT,Hydra 默认签发 opaque token,可以调用内省端点验证。
curl -X POST http://localhost:4444/admin/oauth2/introspect \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "token=替换为access_token"
刷新令牌可以使用 grant_type=refresh_token 重新获取访问令牌,如果客户端注册时包含 offline_access scope,刷新令牌会长期有效。整个链路验证完成后,建议进一步配置 HTTPS、客户端身份验证方式和令牌有效期,这些参数可以在注册客户端时通过 --token-endpoint-auth-method 等选项调整。