Kratos+Hydra快速开始:Docker一键跑通身份认证与OAuth2服务 1. 项目全景Kratos管身份Hydra管授权分工别搞混如果你最近在考虑自建账号体系应该绕不开 Ory 家的两个名字Kratos 和 Hydra。我第一次看到这套东西的文档时第一反应是怎么又是 Kratos 又是 Hydra这不都是做登录的吗。后来真正跑一遍才发现这两个项目干的完全是两件事。Kratos 管用户身份相当于你系统的“人事科”Hydra 管授权协议只负责按标准给第三方应用发令牌。这篇文章是快速开始系列的第一篇目标是让你用最短时间把最基础的邮箱注册和登录流程跑通并且确认 Hydra 这个 OAuth2 服务端是活着的。整个过程不写业务代码完全基于官方 Quickstart 的 Docker 环境。适合正在评估自建账号体系、被重复登录注册逻辑折磨过、或者想把身份认证和业务系统拆开的人参考。1.1 Kratos替你管好注册、登录、Session这一摊Ory Kratos 是一个开源的身份认证系统。它不提供登录页面本身而是把注册、登录、登出、忘记密码、邮箱验证、多因素认证、Session 管理这些能力全部做成了标准 API。你可以理解为它把账号体系里最脏最累的那部分——比如密码哈希、验证邮件模板、会话过期、CSRF 防护——都提前做好了你只需要在前后端调它提供的接口。Kratos 里的一个用户被称为 Identity数据分两部分traits 是用户自愿填写的资料比如邮箱、昵称、姓名credentials 是系统用来验证身份的东西比如密码哈希、WebAuthn 公钥。这个拆分很有用后面操作时会遇到。实际自建过账号体系的都知道这两个东西搅在一起最难受每次要改用户资料字段都要动到认证核心Kratos 这种数据模型能把业务资料和认证凭据彻底分开。1.2 Hydra只管发Token不管用户是谁Ory Hydra 是 OAuth2 和 OpenID Connect 的服务端实现。它不关心你的用户叫什么名字不关心密码对不对只负责一件事按照标准协议签发 Access Token、刷新 Token、授权码。类似一个独立的发证窗口只要满足协议条件它就给你发一张有效的通行证至于你有没有资格进来那是认证系统说了算。顺带提一句如果你因为搜 Hydra 看到一个下载管理器那是另一个桌面下载软件跟 Ory Hydra 完全是两码事别搞混。在完整架构里Kratos 负责确认“你是谁”Hydra 负责确认“你能授权哪个应用访问什么数据”。两者不冲突反而互补——密码验证交给 KratosToken 签发交给 Hydra这就是 Ory 官方推荐的组合姿势。1.3 这套Quickstart里到底有哪些组件官方 Quickstart 已经把两者拼好还包括了数据库、本地邮件服务和一个示例前端。启动起来以后你会得到这些关键服务。服务容器名Public端口Admin端口作用Ory Kratoskratos44334434身份认证、Session、邮箱验证Ory Hydrahydra44444445OAuth2/OIDC、Token签发Oathkeeperoathkeeper44554456反向代理、访问策略Mailslurpermailslurper44364437本地测试邮件收件箱PostgreSQLpostgres-kratos未映射5432数据存储示例应用ui3000无官方演示前端这里 Postgres 被两个服务共用一个数据库实例多个独立库。快速开始阶段你不用关心细节启动时它会自动迁移建表。看到这个组件列表你就明白Kratos 和 Hydra 不是“二选一”的关系而是整个鉴权链路里两个不同环节这篇先集中把 Kratos 跑通Hydra 只需要确认它能正常发 Token。2. 环境搭建十分钟把全套服务跑起来2.1 准备工具拉取官方Quickstart先确保本机有 Docker 和 Docker Compose。我用的是 Docker DesktopLinux 上直接命令行安装 docker-compose-plugin 就行。然后拉取官方 Quickstart 仓库git clone https://github.com/ory/quickstart.git cd quickstart仓库根目录有一份 docker-compose.yml里面包含了上面表格里的所有服务。如果你不想 clone直接把这份 compose 文件下载到空目录也可以。接下来不急着改先启动docker compose up --build -d第一次启动会拉镜像并构建示例应用耗时看网络情况5 到 15 分钟都算正常。启动过程中会有 kratos-migrate 容器自动执行数据库迁移迁移完它就退出你看到某个容器状态是 exited 而且退出码是 0不要慌那是预期行为。2.2 配置文件里面有什么改哪几处重点看 docker-compose.yml 里几个地方。Kratos 和 Hydra 都配置了 PostgreSQL 连接串默认账号密码是 secretKratos 的配置文件挂载的是 kratos.yml里面写死了 secrets.cookie 和 secrets.cipher。开发环境用默认值没问题生产环境必须换不然任何人拿到配置都能伪造 Session。Kratos 配置目录还挂载了 identity.schema.json这是定义注册表单字段的地方下一章会细讲。启动过程中如果数据库连接失败通常是 Postgres 容器没准备好因为 Compose 不一定保证依赖顺序这时候重启一遍 kratos-migrate 就行docker compose run --rm kratos-migrate migrate sql -e --yes这个命令会强制把数据库 schema 刷到最新版本。跑完以后 Kratos 主进程才能正常对外服务不然你会看到 500 错误因为表还没建好。2.3 启动服务并做一轮体检服务全部起来后先别急着调接口确认大家都在线docker compose ps正常状态下kratos、hydra、oathkeeper、mailslurper、postgres 都在 Up示例应用也在 Up。然后再验证 HTTP 端点curl -s http://127.0.0.1:4433/health/alive curl -s http://127.0.0.1:4444/health/alive两个地址分别对应 Kratos 和 Hydra 的健康检查接口返回 200 并且能看到 JSON 状态响应就说明基础服务是活的。另一个值得看的地方是 Postgres 是否已经生成了 kratos 和 hydra 两个库的表这个用 docker exec 进去 psql 查看比较直观docker compose exec postgres-kratos psql -U kratos -d kratos -c \dt | head看到一堆 identity_ 开头和 hydra_ 开头的表就代表迁移成功了。到这里环境就算就绪下面开始进入正题搞懂它在干什么然后手动走一遍注册登录。3. 核心概念Identity、Flow、Session三个关键词3.1 Schema决定用户长什么样快速开始状态下注册页面能提交的字段由 identity.schema.json 控制。默认 schema 大致长这样{ $id: https://schemas.ory.sh/presets/kratos/identity.email.schema.json, $schema: http://json-schema.org/draft-07/schema#, title: Person, type: object, properties: { traits: { type: object, properties: { email: { type: string, format: email }, name: { type: string } }, required: [email] } } }注册接口接收的 traits.email 会同时被 Kratos 识别为可验证邮箱。你想增加字段只需要在这里加定义。但注意字段一旦有用户使用之后修改 schema 会触发兼容性校验不是随便改的。我第一次想当然地给 traits 加了一个手机号字段然后重启服务结果已有用户直接验不过 schema花了不少时间才搞清楚是版本化的问题。这块后面踩坑记录里我会单独说。3.2 Flow机制为什么不直接给一个POST /registerKratos 没有采用传统的一锤子 POST /register。它把所有交互动作建模成 Flow流程。你访问一个流程端点服务端创建一个 flow 对象包含唯一 id、过期时间、允许的方法、当前步骤状态以及 CSRF 令牌。后续请求通过 ?flowid 指定继续哪个流程。这个设计好处很明显能防 CSRF、能支持多步交互、能安全保存中间状态。代价就是调试略微啰嗦。首次接触 Kratos 的人90% 的不适应都来自这里。你在浏览器里打开注册页看到的每一步跳转背后其实都是一个个 flow 的状态流转。另外要区分 Browser Flow 和 API Flow。Quickstart 默认开的是 Browser Flow它依赖 Cookie所以需要 CSRF纯 API Flow 面向 SPA 和移动端走的是另一种凭证方式需要在配置里单独开启。这篇快速开始只围绕 Browser Flow 展开调接口时一定带上 Cookie 和 CSRF 令牌。3.3 Session、Cookie与CSRF怎么配合Kratos 登录成功后会在浏览器种一个 Session Cookie名称通常是 ory_kratos_session。后续请求带上它Kratos 就知道你是谁了。判断当前会话最简单的方式是请求 /sessions/whoami返回 200 就有身份信息返回 401 就未登录。为了防 CSRFBrowser Flow 里每个请求都要带双重校验CSRF Cookie 和表单或 JSON 里的 csrf_token 字段。用 curl 模拟时cookie 要全程共用同一个 jar 文件不要分开存。很多人在这一步卡住以为是自己密码错了其实是 cookie jar 没带上Kratos 返回 403。4. 手把手实操邮箱注册、验证、登录全流程4.1 申请注册Flow拿到CSRF令牌我们用 curl 模拟浏览器走一遍。首先向注册流程端点发起请求要求返回 JSON。这里选择保存到文件方便后面从响应里提取字段mkdir -p /tmp/ory curl -s -c /tmp/ory/cookies.txt -H Accept: application/json \ http://127.0.0.1:4433/self-service/registration/browser /tmp/ory/registration_flow.json然后查看这个文件内容你会得到一个 RegistrationFlow JSON。重点看两个字段id 是流程标识csrf_token 是后续请求回传的防 CSRF 令牌。同时响应里已经种下了 CSRF Cookie存在 /tmp/ory/cookies.txt 里。把这两个值提取出来备用FLOW_ID$(jq -r .id /tmp/ory/registration_flow.json) CSRF_TOKEN$(jq -r .csrf_token /tmp/ory/registration_flow.json) echo $FLOW_ID $CSRF_TOKEN如果你在浏览器里直接打开同一个地址会被重定向到示例 UI 的注册页显示一个真正的表单但后端流程机制和 curl 完全一样。建议你开两个窗口对照看一个用浏览器点一个用 curl 调这样对 Flow 的理解会快很多。4.2 提交邮箱与密码完成注册接下来提交注册信息。注意方法名是 password密码和 traits 是平级字段不要嵌套进 traits 里curl -i -s -b /tmp/ory/cookies.txt \ -H Accept: application/json \ -H Content-Type: application/json \ -X POST \ -d {\csrf_token\:\$CSRF_TOKEN\,\method\:\password\,\traits\:{\email\:\userexample.com\,\name\:\zhang san\},\password\:\a-strong-password\} \ http://127.0.0.1:4433/self-service/registration?flow$FLOW_ID如果一切正常响应会设置 ory_kratos_session Cookie说明 Quickstart 默认在注册成功后自动创建会话也就是自动登录你不用再做一次登录。这一步常见错误是 traits 里把邮箱字段写成 emailAddressKratos 不认或者忘记把 csrf_token 放进 body结果返回 400。还有一个容易忽略的点Kratos 并不强制要求用户名登录时的 identifier 默认就是邮箱地址。所以注册时填的 email 就是后续登录的账号名要记清楚。4.3 去Mailslurper收验证邮件注册成功后打开 Mailslurper 管理界面http://127.0.0.1:4436/能看到一封主题为 Please verify your email address 的邮件。点开邮件里面有一个验证链接指向 Kratos 的验证入口。在浏览器里打开这个链接Kratos 会把邮箱标记为已验证然后重定向回设置页。验证邮件不是瞬发的Kratos 的 courier 组件按轮询周期扫描数据库中的待发送邮件本地 SMTP 一般几秒到十几秒内就能收到。如果一直没邮件优先去查 Kratos 容器日志而不是怀疑 Mailslurperdocker compose logs -f kratos | grep -i courier\|smtp\|mail日志里能看到邮件投递记录和报错信息。我第一次跑的时候一直没收到邮件最后发现是提前改了 smtp 配置端口指向错了改回 mailslurper:1025 就好了。4.4 登录并核对会话和邮箱验证状态为了演示完整登录流程先模拟一次登出直接删掉本地的 cookie 文件即可生产环境当然是调正式登出接口。然后申请登录流程curl -s -c /tmp/ory/login-cookies.txt -H Accept: application/json \ http://127.0.0.1:4433/self-service/login/browser /tmp/ory/login_flow.json LOGIN_FLOW_ID$(jq -r .id /tmp/ory/login_flow.json) LOGIN_CSRF$(jq -r .csrf_token /tmp/ory/login_flow.json) echo $LOGIN_FLOW_ID $LOGIN_CSRF登录流程的调试方法和注册如出一辙只是换一个端点、换一组字段。注意登录状态下请求体里标识账号的字段名是 identifier而不是 email这一点和注册表单不一样很容易看错curl -i -s -b /tmp/ory/login-cookies.txt \ -H Accept: application/json \ -H Content-Type: application/json \ -X POST \ -d {\csrf_token\:\$LOGIN_CSRF\,\method\:\password\,\identifier\:\userexample.com\,\password\:\a-strong-password\} \ http://127.0.0.1:4433/self-service/login?flow$LOGIN_FLOW_ID登录成功后用 whoami 接口确认当前会话和用户信息curl -i -s -b /tmp/ory/login-cookies.txt \ http://127.0.0.1:4433/sessions/whoami返回的 JSON 里能看到 identity 的全部 traits还有 verifiable_addresses 数组。如果刚才邮箱验证成功对应 address 的 status 字段就是 verified这说明整个注册、验证、登录链路已经全部打通。5. Hydra初探先把OAuth2服务端跑起来5.1 Hydra在这里扮演什么角色先搞清楚边界现在把注意力切到 Hydra。它和 Kratos 各自独立虽然部署在同一套环境里但业务边界非常清楚。Hydra 不参与用户登录它只负责暴露 /oauth2/token、/oauth2/auth 这类 OAuth2 端点。用户登录那一步Hydra 会交给一个 Login Provider 去做而这个 Provider 可以根据 Kratos 的 Session 自动判断用户是否已登录这是下一期的重点。在国内做后端的人对 OAuth2 应该不陌生但很多人误以为部署了 Hydra 就等于有用户系统了不是的。Hydra 的代码里根本不会出现“密码校验”这个动作它只是标准协议的执行者。真正校验密码、建立用户会话还是 Kratos 在干。5.2 用OIDC发现端点确认服务活着先确认 Hydra 活着。OIDC 发现端点是最直观的入口几乎所有 OAuth2 客户端 SDK 都会先请求它curl -s http://127.0.0.1:4444/.well-known/openid-configuration | jq返回内容里能看到 issuer、authorization_endpoint、token_endpoint、jwks_uri 等字段。issuer 是 http://127.0.0.1:4444/ 说明 dev 配置生效。这个文件是所有 OAuth2 客户端的起点也是一个服务端是否符合 OIDC 标准的声明。5.3 创建一个测试客户端拿到第一个Access Token接下来创建一个测试客户端授予它 client_credentials 权限让它能以“应用身份”直接拿一个客户端凭证令牌。注意 Hydra Admin API 的路径带 /admin 前缀而 Kratos 的 Admin 端口不带两个项目的接口风格不完全一致容易记混curl -s -X POST http://127.0.0.1:4445/admin/clients \ -H Content-Type: application/json \ -d { client_id: demo-client, client_secret: demo-secret, grant_types: [client_credentials], scope: openid profile email } | jq创建成功后会返回客户端信息client_secret 会以明文形式出现在响应里这是开发模式下的行为。然后换取 Access Tokencurl -s -X POST http://127.0.0.1:4444/oauth2/token \ -u demo-client:demo-secret \ -d grant_typeclient_credentials \ -d scopeopenid profile email | jq返回的 access_token 就是标准 JWT 形式。在这个模式下Hydra 没有询问任何用户名密码因为 client_credentials 代表的是“应用自己的身份”而不是“用户身份”。这正好说明 Hydra 的职责边界它不关心你是谁只负责验证凭证、发 Token。5.4 下一期预告真正的login与consent流程等你能熟练收发 Token 之后下一步就是真正的用户授权码流程客户端跳转到 Hydra 的授权端点Hydra 发现用户未登录就跳转到你的登录服务登录服务拿着 Kratos 的 Session 判断身份调用 Hydra 的 Login API 接受登录再走 Consent 同意页面。逻辑链路会比今天长一截但基础就是你已经跑通的这版 Kratos 注册登录流程。那个流程里Kratos 和 Hydra 才会真正开始协作你会看到 Session、Flow、授权请求三者如何串联。下一期我会专门拆解 login 请求和 consent 请求两个子流程配合一个最简单的 demo 客户端把授权码流程完整跑一遍。6. 踩坑记录CSRF、验证邮件、Schema变更等高频问题6.1 CSRF 403八成是Cookie和令牌没配对调试期遇到 403基本都是 CSRF 没配对。Browser Flow 有三个条件缺一不可全程用一个 cookie jar、提交时带 flow 里的 csrf_token、请求的 Content-Type 是 application/json 或表单。最容易犯的错就是新开一个 curl 请求忘了用 -b 指定 cookie 文件Kratos 找不到 CSRF Cookie直接拒绝。如果你觉得 CSRF 很烦说明你在用 API 调试工具调 Browser Flow。可以考虑开 API Flow那是另一套凭证体系不需要 CSRF但调试 Browser Flow 还是老老实实带 cookie。记住一个原则浏览器里能跑通的流程用 curl 模拟时每一步都要把 Cookie 带上。6.2 验证邮件迟迟不来怎么办邮箱验证走 courier 组件它不是实时的通常有轮询间隔等 10 到 30 秒是正常的。先看 Mailslurper 4436 端口有没有信没有再看 kratos 日志。如果日志里出现 SMTP 连接失败检查 kratos.yml 里 courier.smtp.connection_uri 是否指向 mailslurper 容器的 1025 端口。另一个隐藏很深的坑是 serve.public.base_url 和实际访问地址不一致。如果你把 base_url 配成某个自定义域名但本地通过 127.0.0.1:4433 访问那么验证邮件里的链接会指向配置里的域名点开自然访问不到行为表现就是“验证链接 404”。开发环境检查一遍 public.base_url确保和浏览器地址栏一致。6.3 改了Schema之后注册报错或验证404改了 identity.schema.json 后如果已有用户存在再注册新用户时可能直接报 schema 兼容性错误。这不是代码 bug是 Kratos 对 schema 做了版本化管理。快速开始阶段最简单粗暴的办法是重置数据卷docker compose down -v docker compose up --build -d这会清空所有用户数据适合刚上手练习阶段。生产环境要做数据库迁移方案不要在线上随便改 schema改之前一定要评估对存量 Identity 的影响。6.4 跨端口Cookie和SameSite的本地坑本地调试时Kratos 的 public 端口是 4433示例前端走 3000 和 4455。Cookie 不区分端口但区分域名localhost 下跨端口没问题。真正容易踩的是如果你把 public_url 配成了 https 域名但本地用 http 地址访问那么带 Secure 属性的 Cookie 不会在 http 下发送登录状态会莫名丢失。开发环境不要开 Secure保持 http 直连。另外Kratos 默认对 Session Cookie 设置了合理的 SameSite 策略但你如果在本地用两个不同域名测试会碰到 Cookie 作用域问题。建议所有本地测试都在 localhost 这一个域名下进行减少变量。6.5 Browser Flow与API Flow别在模式上犯迷糊很多人在网上找 Kratos 示例的时候会看到两种完全不一样的数据格式然后互相套结果当然是各种报错。其实它们本来就是两套模式用一张表可以看得很清楚对比项Browser FlowAPI Flow主要入口/self-service/.../browser/self-service/.../api凭证方式Session CookieBearer TokenCSRF 校验需要不需要典型场景传统网页、服务端渲染单页应用、移动端Quickstart 默认状态开启需额外配置如果你用的是 Postman 这类工具又不想碰 CSRF可以考虑走 API Flow。但 API Flow 需要你在 kratos.yml 里显式开启对应流程之后 SPA 才能直接用 Bearer Token 调注册登录接口。快速开始阶段我建议你还是先跑通 Browser Flow因为官方示例、示例 UI、邮件验证整个链路都是围绕它设计的。这轮实操下来Kratos 的注册登录其实没有想象中复杂但也要承认如果不理解 Flow 模型确实容易被它来回跳转的机制绕晕。我自己的体会是第一遍务必打开浏览器去点一遍表单把每一步重定向、每个 Cookie 变化都看一遍再回来看 curl 示例很多疑问会瞬间消失。下一期进入 Hydra 的授权码流程届时我们用 Kratos 做 Login Provider真正把第三方登录的完整链路串起来。