本文档给出一条从零开始的完整使用路径,覆盖:
- 准备配置、数据库、Redis 和 RSA 密钥
- 启动 Keylo 并确认管理客户端可用
- 获取管理 Token,管理管理客户端
- 配置 RBAC:权限、角色、角色权限关系
- 创建/开通用户并绑定角色
- 用户登录、自助接口与受保护接口验证
- 注册服务客户端并申请
service_accessToken - 使用服务 Token 做用户 Token 内省
建议阅读顺序:
- 这份文档负责“怎么一步步用起来”
- API_REFERENCE.md 负责完整接口定义
- MULTI_CLIENT_RBAC_INTEGRATION.md 负责多客户端权限建模建议
从 .env.example 复制:
cp .env.example .envWindows PowerShell:
Copy-Item .env.example .env确保以下关键配置可用(示例):
DATABASE_URL=postgres://keylo_user:keylo_password@localhost:5432/keylo
REDIS_URL=redis://localhost:6379
JWT_KEY_ID=keylo-rs256-1
JWT_PRIVATE_KEY_PATH=./keys/private.pem
JWT_PUBLIC_KEY_PATH=./keys/public.pem
ALLOW_IN_MEMORY_FALLBACK=false
# 管理客户端(用于 /v1/admin/token)
ADMIN_CLIENT_ID=cli-admin-root
ADMIN_CLIENT_SECRET=replace-with-strong-admin-secret如果不显式提供 RSA 密钥,开发环境会使用内置开发密钥;生产环境必须自己生成:
mkdir -p keys
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out keys/private.pem
openssl rsa -pubout -in keys/private.pem -out keys/public.pemLinux 服务器推荐进一步限制权限:
chmod 600 keys/private.pem
chmod 644 keys/public.pemdocker compose up -d postgres rediscargo run启动后默认地址:http://127.0.0.1:2345
如果使用 Docker Compose 直接运行 Keylo:
docker compose up -d --build
docker compose logs -f keylo-serviceKeylo 默认在数据库或密钥初始化失败时直接启动失败。只有本地临时调试且明确接受认证/管理接口不可用时,才设置 ALLOW_IN_MEMORY_FALLBACK=true。
至少确认日志中出现:
Database migrations completedDefault clients seededDatabase initialized successfully
如出现:
ADMIN_CLIENT_ID or ADMIN_CLIENT_SECRET is not set, skipping admin client seed
说明管理客户端没有初始化成功,后续 /v1/admin/token 将不可用。
POST /v1/auth/token是用户登录。 管理客户端必须使用POST /v1/admin/token。
curl -s -X POST http://127.0.0.1:2345/v1/admin/token \
-H "Content-Type: application/json" \
-d '{
"client_id":"cli-admin-root",
"client_secret":"replace-with-strong-admin-secret"
}'响应会返回:
access_token(管理接口调用使用)refresh_token(用于/v1/auth/refresh)
后续命令统一使用:
export ADMIN_TOKEN="<上一步 access_token>"
export ADMIN_REFRESH_TOKEN="<上一步 refresh_token>"PowerShell:
$env:ADMIN_TOKEN = "<上一步 access_token>"
$env:ADMIN_REFRESH_TOKEN = "<上一步 refresh_token>"如需刷新管理 Token:
curl -s -X POST http://127.0.0.1:2345/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token":"'"${ADMIN_REFRESH_TOKEN}"'"}'刷新成功后旧 ADMIN_REFRESH_TOKEN 会被原子消费,不能再次使用。请用响应中的新 refresh_token 替换本地保存值。
系统启动时会根据 .env 中的 ADMIN_CLIENT_ID / ADMIN_CLIENT_SECRET 自动创建一个管理客户端。
如果需要给其他后台系统单独分配管理客户端,可以继续通过管理接口维护。
curl -s -X POST http://127.0.0.1:2345/v1/admin/clients \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"client_id":"ops-console",
"client_secret":"OpsConsole#123",
"name":"Ops Console",
"description":"operations admin console",
"active":true
}'curl -s -H "Authorization: Bearer ${ADMIN_TOKEN}" \
http://127.0.0.1:2345/v1/admin/clients
curl -s -X POST http://127.0.0.1:2345/v1/admin/clients/ops-console/rotate-secret \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"new_secret":"OpsConsole#456"}'如果省略 new_secret,Keylo 会生成新密钥并在响应的 new_secret 字段中一次性返回;如果请求体提供了 new_secret,响应不会回显明文。
curl -s -X POST http://127.0.0.1:2345/api/rbac/permissions \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name":"ssc.camera.read","description":"读取摄像头"}'
curl -s -X POST http://127.0.0.1:2345/api/rbac/permissions \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name":"ssc.camera.write","description":"编辑摄像头"}'
curl -s -X POST http://127.0.0.1:2345/api/rbac/permissions \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name":"ssc.user.read","description":"读取用户信息"}'curl -s -X POST http://127.0.0.1:2345/api/rbac/roles \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name":"ssc_dispatcher","description":"调度角色"}'
curl -s -X POST http://127.0.0.1:2345/api/rbac/roles \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name":"ssc_viewer","description":"只读查看角色"}'先查询权限与角色 ID:
curl -s -H "Authorization: Bearer ${ADMIN_TOKEN}" \
http://127.0.0.1:2345/api/rbac/permissions
curl -s -H "Authorization: Bearer ${ADMIN_TOKEN}" \
http://127.0.0.1:2345/api/rbac/roles然后批量绑定,例如把 ssc_dispatcher 绑定读写权限,把 ssc_viewer 绑定只读权限:
curl -s -X POST http://127.0.0.1:2345/api/rbac/roles/<ROLE_ID>/permissions/batch \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"permission_ids":["<PERM_ID_READ>","<PERM_ID_WRITE>"]}'curl -s -H "Authorization: Bearer ${ADMIN_TOKEN}" \
http://127.0.0.1:2345/api/rbac/roles/<ROLE_ID>/permissions推荐使用原子开通接口:/v1/admin/users/provision
curl -s -X POST http://127.0.0.1:2345/v1/admin/users/provision \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"username":"alice",
"email":"alice@example.com",
"password":"Alice#12345",
"role_names":["ssc_dispatcher"]
}'如果你已经有用户,也可以拆分成“创建用户 + 绑定角色”两步:
curl -s -X POST http://127.0.0.1:2345/v1/admin/users \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"username":"bob",
"email":"bob@example.com",
"password":"Bob#12345",
"active":true
}'
curl -s -X POST http://127.0.0.1:2345/api/rbac/users/<USER_ID>/roles/batch \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"role_ids":["<ROLE_ID>"]}'查看用户最终权限并集:
curl -s -H "Authorization: Bearer ${ADMIN_TOKEN}" \
http://127.0.0.1:2345/v1/admin/users/<USER_ID>/effective-permissionscurl -s -X POST http://127.0.0.1:2345/v1/auth/token \
-H "Content-Type: application/json" \
-d '{
"client_id":"alice",
"client_secret":"Alice#12345"
}'export USER_TOKEN="<用户 access_token>"curl -s -H "Authorization: Bearer ${USER_TOKEN}" \
http://127.0.0.1:2345/v1/auth/mecurl -s -X POST http://127.0.0.1:2345/v1/user/change-password \
-H "Authorization: Bearer ${USER_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"old_password":"Alice#12345",
"new_password":"Alice#12345-New"
}'curl -s -H "Authorization: Bearer ${USER_TOKEN}" \
http://127.0.0.1:2345/protectedcurl -s -X POST http://127.0.0.1:2345/v1/admin/services \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"service_id":"agileboot-admin",
"service_secret":"AgileBootSvc#123",
"name":"AgileBoot Admin",
"description":"agileboot backend service client",
"allowed_scopes":["read","user.read"],
"allowed_audiences":["admin-backend"]
}'curl -s -H "Authorization: Bearer ${ADMIN_TOKEN}" \
http://127.0.0.1:2345/v1/admin/services
curl -s -X POST http://127.0.0.1:2345/v1/admin/services/agileboot-admin/rotate-secret \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"new_secret":"AgileBootSvc#456"}'服务密钥轮换同样遵循一次性返回规则:只有省略 new_secret 且由服务端生成时,响应才包含明文 new_secret。
curl -s -X POST http://127.0.0.1:2345/v1/service/token \
-H "Content-Type: application/json" \
-d '{
"service_id":"agileboot-admin",
"service_secret":"AgileBootSvc#123",
"audience":"admin-backend",
"scope":"read"
}'export SERVICE_TOKEN="<service_access_token>"curl -s -X POST http://127.0.0.1:2345/v1/auth/introspect \
-H "Authorization: Bearer ${SERVICE_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"token":"'"${USER_TOKEN}"'"}'curl -s -X POST http://127.0.0.1:2345/v1/service/introspect \
-H "Authorization: Bearer ${SERVICE_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"token":"'"${SERVICE_TOKEN}"'"}'建议至少做以下验证:
- 管理客户端可以成功调用
/v1/admin/token - 可以创建权限、角色,并看到角色权限列表
- 可以通过
/v1/admin/users/provision创建用户并分配角色 - 普通用户可以通过
/v1/auth/token登录并调用/v1/auth/me - 服务客户端可以成功申请
service_accesstoken /v1/auth/introspect能使用服务 token 正常内省用户 token- JWKS 接口
/.well-known/jwks.json可正常访问
-
wrong_credentials+ 日志User not found: cli- 原因:把管理客户端拿去调用了
/v1/auth/token。 - 处理:改用
/v1/admin/token。
- 原因:把管理客户端拿去调用了
-
启动告警
No active admin client found ...- 检查
.env是否有ADMIN_CLIENT_ID/ADMIN_CLIENT_SECRET。 - 检查
clients表里目标客户端是否active=true且is_admin_client=true。
- 检查
-
migration 校验失败(
previously applied but has been modified)- 不要修改已执行 migration;请恢复原文件或重置数据库后重新迁移。
-
Docker 中日志显示
Environment: development- 检查 Compose 实际传入的
ENVIRONMENT。 - 检查服务器是否用了另一份
docker-compose.yml或.env覆盖。
- 检查 Compose 实际传入的
-
容器启动后
skipping admin client seed- 说明容器环境变量中没有
ADMIN_CLIENT_ID/ADMIN_CLIENT_SECRET。 - 检查
docker compose config输出,确认最终渲染后的环境变量是否正确。
- 说明容器环境变量中没有