导航菜单

  • 1.什么是MCP
  • 2.MCP架构
  • 3.MCP服务器
  • 4.MCP客户端
  • 5.版本控制
  • 6.连接MCP服务器
  • 7.SDKs
  • 8.Inspector
  • 9.规范
  • 10.架构
  • 11.协议
  • 12.生命周期
  • 13.工具
  • 14.资源
  • 15.提示
  • 16.日志
  • 17.进度
  • 18.传输
  • 19.补全
  • 20.引导
  • 21.采样
  • 22.任务
  • 23.取消
  • 24.Ping
  • 25.根
  • 26.分页
  • 27.授权
  • 28.初始化
  • 29.工具
  • 30.资源
  • 31.结构化输出
  • 32.提示词
  • 33.上下文
  • 34.StreamableHTTP
  • 35.参数补全
  • 36.引导
  • 37.采样
  • 38.LowLevel
  • 39.任务
  • 40.取消
  • 41.ping
  • 42.根
  • 43.分页
  • 44.授权
  • 45.FunctionCalling
  • starlette
  • FastAPI
  • Keycloak
  • asyncio
  • contextlib
  • httpx
  • pathlib
  • pydantic
  • queue
  • subprocess
  • threading
  • uvicorn
  • JSON-RPC
  • LiteLLM
  • pydantic-settings
  • ai_agent
  • format
  • diff
  • mcp_server
  • 1. 前置知识:认证与授权是什么?
    • 1.1 认证(Authentication)和授权(Authorization)的区别
    • 1.2 OAuth2 和 OpenID Connect 是什么?
  • 2. Keycloak 是什么?能解决什么问题?
    • 2.1 通俗解释
    • 2.2 核心能解决的问题
    • 2.3 会用到的主要功能
  • 3. 核心概念
    • 3.1 Realm(领域/租户)
    • 3.2 Client(客户端)
    • 3.3 User(用户)
    • 3.4 Token(令牌)
  • 4. 环境准备
    • 4.1 安装 Python 依赖
    • 4.2 用 Docker 启动 Keycloak
  • 5. 在 Keycloak 中完成基础配置
    • 5.1 创建 Realm(可选,也可用 master)
    • 5.2 创建 Client(客户端)
    • 5.3 创建用户
  • 6. 获取 Token(密码模式)
    • 6.1 可运行代码
    • 6.2 运行与预期输出
    • 6.3 常见错误排查
  • 7.用 Token 获取用户信息
    • 7.1 代码
    • 7.2 运行与预期输出
  • 8. 使用 python-keycloak 管理用户
    • 8.1 前置说明
    • 8.2 代码
    • 8.3 运行与验证
  • 9. 登录并调用"受保护 API"
    • 9.1 代码
    • 9.2 你的 API 如何验证 Token?
  • 10. 常见问题与排查
    • 10.1 获取 Token 时报 401
    • 10.2 获取 Token 时报 "Invalid client"
    • 10.3 Redirect URI 相关错误
    • 10.4 连接被拒绝 (Connection refused)

1. 前置知识:认证与授权是什么? #

在开始学习 Keycloak 之前,需要先理解两个核心概念,否则后续内容会难以理解。

1.1 认证(Authentication)和授权(Authorization)的区别 #

认证:证明"你是谁"。比如你输入用户名和密码,系统验证后确认你是张三。

授权:证明"你能做什么"。比如张三登录后,系统判断他是否有权限删除某条数据。

生活化类比:进小区门禁是认证(证明你是业主),进自家房门是授权(证明你有这间房的钥匙)。

1.2 OAuth2 和 OpenID Connect 是什么? #

OAuth2:一种"授权"的标准协议。通俗讲:你的应用想访问用户在微信里的好友列表,但用户不想把微信密码给你。OAuth2 让用户去微信那里授权,微信给你一个"临时通行证"(Token),你拿这个通行证去取数据,用户密码始终不经过你的手。

OpenID Connect(OIDC):在 OAuth2 基础上加了一层"认证"。除了给你 Token,还告诉你"这个用户是谁"(用户 ID、昵称、邮箱等)。Keycloak 主要支持 OIDC 协议。

为什么需要了解这些? Keycloak 就是一个"认证授权中心",你的 Python 应用通过 OIDC 协议和它打交道。知道这些概念后,后面配置"客户端""重定向 URI"时就不会一头雾水。

2. Keycloak 是什么?能解决什么问题? #

2.1 通俗解释 #

Keycloak 是一个开源的统一登录系统。你可以把它想象成"小区的门卫室":

  • 用户只需在门卫室登记一次(登录一次)
  • 之后访问小区里的多个楼栋(多个应用),门卫都认识你,不用重复登记
  • 门卫还会根据你的身份,决定你能进哪些楼、哪些房间(权限控制)

2.2 核心能解决的问题 #

问题 Keycloak 的解决方案
每个应用都要自己写登录逻辑 统一交给 Keycloak,应用只验证 Token
用户要在多个系统重复登录 单点登录(SSO):登录一次,到处通行
想接微信、GitHub 等第三方登录 内置社交登录集成,配置即可
用户、角色、权限管理混乱 提供管理界面,集中管理用户和角色

2.3 会用到的主要功能 #

  • 单点登录(SSO):用户登录一次,可访问多个应用
  • OIDC 协议:通过标准协议获取用户身份和 Token
  • 集中式用户管理:在 Keycloak 里创建用户、分配角色

3. 核心概念 #

3.1 Realm(领域/租户) #

是什么:一个独立的"用户王国"。每个 Realm 有自己的一套用户、客户端、角色,互不干扰。

类比:公司 A 和公司 B 都用同一套 Keycloak,但各自一个 Realm,A 的用户看不到 B 的用户。

建议:先用默认的 master Realm 练手,熟练后再创建自己的 Realm。

3.2 Client(客户端) #

是什么:你的应用程序在 Keycloak 里的"注册身份"。每个要接入 Keycloak 的应用(网站、API、移动端)都需要创建一个 Client。

关键配置:

  • Client ID:唯一标识,如 my-python-app
  • Client Type:public(前端、移动端)或 confidential(后端服务,需要密钥)
  • Valid Redirect URIs:登录成功后跳转的地址,必须精确配置,否则会报错

3.3 User(用户) #

是什么:可以登录的账号。在 Keycloak 管理界面创建,设置用户名、密码、邮箱等。

3.4 Token(令牌) #

是什么:登录成功后,Keycloak 颁发的一串字符串(JWT 格式)。你的应用拿到 Token 后,每次请求 API 时带上它,服务端就能知道"是谁在访问"。

4. 环境准备 #

4.1 安装 Python 依赖 #

本次使用 requests 和 python-keycloak 两个库。requests 用于发 HTTP 请求,python-keycloak 是 Keycloak 的 Python 客户端,可简化操作。

创建一个虚拟环境并安装依赖(推荐):

# 创建虚拟环境(可选,但推荐)
python -m venv venv

# 激活虚拟环境(Windows PowerShell)
venv\Scripts\activate

# 安装依赖
pip install requests python-keycloak

4.2 用 Docker 启动 Keycloak #

如果你没有 Docker,可到 Keycloak 官网 下载并运行。本以 Docker 为例。

# 启动 Keycloak 容器,映射到本机 8080 端口
# Windows PowerShell 中可复制整段运行
docker run -d --name keycloak -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:latest start-dev

说明:start-dev 是开发模式,方便本地学习。生产环境请用 start 并配置数据库等。

启动后访问:http://localhost:8080/admin,用 admin / admin 登录管理控制台。

5. 在 Keycloak 中完成基础配置 #

在写代码之前,需要先在 Keycloak 管理界面完成以下配置。请按顺序操作。

5.1 创建 Realm(可选,也可用 master) #

  1. 登录管理控制台
  2. 左上角下拉选择 master,点击 "Create realm"
  3. Realm name 填 myrealm,点击 Create
  4. 创建后会自动进入该 Realm

如果暂时不想创建,可直接用 master Realm,后续代码中的 myrealm 改为 master 即可。

5.2 创建 Client(客户端) #

  1. 左侧菜单点击 "Clients" → "Create client"
  2. Client ID:my-python-app(必填,唯一)
  3. 点击 Next
  4. Client authentication:选择 "ON"(即 confidential,需要密钥)
  5. Authorization:保持 OFF
  6. 认证流程选择标准流程和直接访问授权
  7. 点击 Save
  8. 在客户端详情页:
    • Valid redirect URIs:填 http://localhost:8000/*(本地回调地址,可按需改端口)
    • Web origins:填 http://localhost:8000 或 *(开发时可填 *)
  9. 点击 Save
  10. 切换到 "Credentials" 标签,复制 Secret,后面代码会用到

5.3 创建用户 #

  1. 左侧菜单 "Users" → "Add user"
  2. Username:testuser
  3. Email:可填 `test@example.com`(可选)
  4. 点击 Create
  5. 进入用户详情,点击 "Credentials" 标签
  6. 设置密码,如 test123,关闭 "Temporary"(否则首次登录会强制改密码)
  7. 点击 Set password

6. 获取 Token(密码模式) #

场景:你的 Python 脚本代表一个用户,用用户名和密码直接向 Keycloak 换取 Token。适合后端服务、脚本、命令行工具。

注意:前端网页不应使用密码模式(会暴露密码),应使用授权码模式。

6.1 可运行代码 #

将下面的代码保存为 get_token.py,修改其中的 KEYCLOAK_URL、REALM、CLIENT_ID、CLIENT_SECRET、USERNAME、PASSWORD 后即可运行。

# 导入 requests 库,用于发送 HTTP 请求
import requests

# Keycloak 服务器地址(不要加末尾斜杠)
KEYCLOAK_URL = "http://localhost:8080"
# Realm 名称(你创建的领域,如 myrealm 或 master)
REALM = "master"
# 客户端 ID(在 Keycloak Clients 中创建的)
CLIENT_ID = "mcp-server"
# 客户端密钥(在 Client 的 Credentials 标签页获取)
CLIENT_SECRET = "OXg1z6OunGbAVoeZXbqaf5JeRLBVcptg"
# 要登录的用户名
USERNAME = "testuser"
# 要登录的用户密码
PASSWORD = "test123"

# 构建 Token 接口的完整 URL,格式为 {KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/token
token_url = f"{KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/token"

# 定义 POST 请求的数据,采用 OAuth2 密码模式
data = {
    # 指定授权类型为密码模式
    "grant_type": "password",
    # 客户端标识
    "client_id": CLIENT_ID,
    # 客户端密钥(confidential 类型必须)
    "client_secret": CLIENT_SECRET,
    # 用户名
    "username": USERNAME,
    # 密码
    "password": PASSWORD,
}

# 向 token_url 发送 POST 请求,提交 data,默认 Content-Type 为 application/x-www-form-urlencoded
response = requests.post(token_url, data=data)

# 判断请求是否成功(状态码为 200 时成功)
if response.status_code == 200:
    # 解析返回结果为 JSON 格式,并保存到 token_data 字典
    token_data = response.json()
    # 输出 access_token,用于访问受保护资源
    print("Access Token:", token_data.get("access_token"))
    # 输出 refresh_token,用于在 access_token 过期后刷新
    print("Refresh Token:", token_data.get("refresh_token"))
    # 输出 access_token 的过期时间,单位为秒
    print("过期时间(秒):", token_data.get("expires_in"))
else:
    # 如果获取 Token 失败,则输出错误状态码
    print("获取 Token 失败:", response.status_code)
    # 输出服务器返回的错误信息
    print(response.text)

6.2 运行与预期输出 #

python get_token.py

成功时类似输出:

Access Token: eyJhbGciOiJSUzI1NiIsInR5c
Refresh Token: eyJhbGciOiJIUzUxMiIsInR5
过期时间(秒): 60

6.3 常见错误排查 #

错误信息 可能原因 解决方法
401 Unauthorized 用户名/密码错误,或 Client Secret 错误 检查 Keycloak 中的用户和客户端配置
Invalid redirect uri 未配置或配置错误 在 Client 的 Valid redirect URIs 中添加正确地址
Connection refused Keycloak 未启动 确认 Docker 容器在运行:docker ps

7.用 Token 获取用户信息 #

场景:已经拿到 access_token,想获取当前登录用户的基本信息(用户名、邮箱等)。Keycloak 提供了 /userinfo 接口。

7.1 代码 #

保存为 get_userinfo.py。可先运行示例一获取 Token,把输出的 Token 填到下面;或直接复用示例一的逻辑,在同一个脚本里先取 Token 再调 userinfo。

# 导入 requests 库,用于发送 HTTP 请求
import requests

# ========== 配置区域 ==========
# Keycloak 服务器地址,不要带斜杠
KEYCLOAK_URL = "http://localhost:8080"
# Realm 名称
REALM = "master"
# 可手动填写 access_token;如留空则自动获取
ACCESS_TOKEN = ""

# 如果 ACCESS_TOKEN 为空,则使用下面的客户端信息和用户信息自动获取
CLIENT_ID = "mcp-server"
CLIENT_SECRET = "OXg1z6OunGbAVoeZXbqaf5JeRLBVcptg"
USERNAME = "admin"
PASSWORD = "admin"

# ========== 若未提供 Token,则先获取 ==========
# 如果 ACCESS_TOKEN 为空,调用 Keycloak 获取 Token
if not ACCESS_TOKEN:
    # 构造获取 token 的 URL
    token_url = f"{KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/token"
    # 发送 POST 请求获取 Token,需要提供用户名、密码、client_id 等信息
    token_resp = requests.post(token_url, data={
        "grant_type": "password",
        "client_id": CLIENT_ID,
        "client_secret": CLIENT_SECRET,
        "username": USERNAME,
        "password": PASSWORD,
        "scope": "openid",  # userinfo 需要 openid scope
    })
    # 判断请求是否成功
    if token_resp.status_code != 200:
        # 获取失败打印错误信息并退出
        print("获取 Token 失败:", token_resp.text)
        exit(1)
    # 获取成功则取出 access_token
    ACCESS_TOKEN = token_resp.json().get("access_token")

# ========== 调用 userinfo 接口获取用户信息 ==========
# 构造 userinfo 接口的 URL
userinfo_url = f"{KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/userinfo"

# 构造请求头,带 Authorization: Bearer {token}
headers = {
    "Authorization": f"Bearer {ACCESS_TOKEN}"
}

# 发送 GET 请求获取用户信息
response = requests.get(userinfo_url, headers=headers)

# 判断接口请求是否成功
if response.status_code == 200:
    # 解析用户信息响应为 JSON
    user_info = response.json()
    # 打印用户信息(标准 OIDC 字段)
    print("用户信息:")
    print("  用户名:", user_info.get("preferred_username"))
    print("  邮箱:", user_info.get("email"))
    print("  姓名:", user_info.get("name"))
    print("  完整数据:", user_info)
else:
    # 获取用户信息失败时打印状态码和错误内容
    print("获取用户信息失败:", response.status_code)
    print("响应内容:", response.text)

7.2 运行与预期输出 #

python get_userinfo.py

成功时输出类似:

用户信息:
  用户名: testuser
  邮箱: test@example.com
  姓名: 
  完整数据: {'sub': 'xxx', 'preferred_username': 'testuser', ...}

8. 使用 python-keycloak 管理用户 #

场景:用 Python 代码创建用户、查询用户等管理操作。需要管理员账号。适合做自动化运维或批量导入用户。

8.1 前置说明 #

python-keycloak 通过 Keycloak 的 Admin REST API 进行操作,需要管理员的账号密码或 Service Account。本示例使用管理员账号。

8.2 代码 #

保存为 admin_create_user.py。会创建一个新用户 newuser,密码 newpass123。

# 导入 KeycloakAdmin 类,用于操作 Keycloak 管理接口
from keycloak import KeycloakAdmin

# 设置 Keycloak 服务器地址
KEYCLOAK_URL = "http://localhost:8080"
# 设置管理员用户名
ADMIN_USERNAME = "admin"
# 设置管理员密码
ADMIN_PASSWORD = "admin"
# 设置需要操作的 Realm 名称
REALM = "master"

# 创建 KeycloakAdmin 实例,连接到 Keycloak 管理端口
keycloak_admin = KeycloakAdmin(
    # 指定 Keycloak 的服务器 URL
    server_url=KEYCLOAK_URL,
    # 指定管理员账号
    username=ADMIN_USERNAME,
    # 指定管理员密码
    password=ADMIN_PASSWORD,
    # 管理员初始连接 master 域
    realm_name="master",
    # 设置是否校验证书(生产环境为 True,测试可为 False)
    verify=True,
)

# 切换 KeycloakAdmin 实例的 realm_name 属性到目标 Realm
keycloak_admin.realm_name = REALM

# 构造要创建的新用户基本信息
new_user = {
    # 指定新用户的用户名
    "username": "newuser2",
    # 指定新用户的邮箱
    "email": "newuser2@example.com",
    # 指定新用户的名
    "firstName": "New",
    # 指定新用户的姓
    "lastName": "User",
    # 指定用户是否启用
    "enabled": True,
}

# 调用 create_user 方法创建新用户,返回用户的唯一标识 ID
user_id = keycloak_admin.create_user(new_user)
# 输出用户创建成功及其 ID
print(f"用户创建成功,ID: {user_id}")

# 设置新用户的初始密码,temporary=False 表示非临时密码
keycloak_admin.set_user_password(user_id, "newpass123", temporary=False)
# 输出密码设置完成信息
print("密码已设置为: newpass123")

# 获取刚刚创建的用户详细信息
user = keycloak_admin.get_user(user_id)
# 输出查询到的用户详情
print("用户详情:", user)

8.3 运行与验证 #

python admin_create_user.py

成功后会输出用户 ID 和详情。之后可用 testuser 换成 newuser,密码 newpass123,在示例一、二中测试登录。

9. 登录并调用"受保护 API" #

场景:模拟一个完整流程:用户登录 → 获取 Token → 用 Token 调用你的后端 API。你的 API 需要验证 Token 是否有效。

为简化演示,这里用 Keycloak 自带的 userinfo 接口模拟"受保护的 API"(实际项目中是你的业务接口)。

9.1 代码 #

# 导入 requests 库,用于发送 http 请求
import requests

# ========== 配置区 ==========
# 设置 Keycloak 服务器地址
KEYCLOAK_URL = "http://localhost:8080"
# 设置使用的 Realm 名称
REALM = "master"
# 设置 client_id(在 Keycloak 的 Clients 配置中找到)
CLIENT_ID = "mcp-server"
# 设置 client_secret(在 Clients 的 Credentials 标签页获取)
CLIENT_SECRET = "OXg1z6OunGbAVoeZXbqaf5JeRLBVcptg"
# 设置登录用户名
USERNAME = "admin"
# 设置登录用户密码
PASSWORD = "admin"

# ========== 步骤 1:获取 Token ==========
# 构造获取 token 的接口 URL
token_url = f"{KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/token"
# 以密码模式携带相关数据请求 token
token_resp = requests.post(token_url, data={
    "grant_type": "password",  # 授权类型为密码模式
    "client_id": CLIENT_ID,    # 客户端 ID
    "client_secret": CLIENT_SECRET,  # 客户端密钥
    "username": USERNAME,      # 用户名
    "password": PASSWORD,      # 密码
    "scope": "openid",         # 获取 userinfo 需要 openid scope
})

# 判断 token 获取是否成功
if token_resp.status_code != 200:
    # 获取失败输出错误信息并退出
    print("登录失败:", token_resp.text)
    exit(1)

# 提取返回的 token 数据
token_data = token_resp.json()
# 获取 access_token
access_token = token_data["access_token"]
# 输出获取成功提示
print("步骤 1 完成:已获取 Access Token")

# ========== 步骤 2:用 Token 调用受保护接口 ==========
# 以 userinfo 接口为示例,实际可替换你自己的 API 地址
api_url = f"{KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/userinfo"
# 构造请求头,带上 Bearer token
headers = {"Authorization": f"Bearer {access_token}"}
# 调用 userinfo 接口获取用户信息
api_resp = requests.get(api_url, headers=headers)

# 判断接口调用是否成功
if api_resp.status_code != 200:
    # 失败则输出错误信息并退出
    print("调用 API 失败:", api_resp.text)
    exit(1)

# 获取用户信息的 JSON 数据
user_info = api_resp.json()
# 输出用户信息获取成功提示
print("步骤 2 完成:已获取用户信息")
# 打印当前用户的用户名
print("当前用户:", user_info.get("preferred_username"))
# 流程成功结束提示
print("完整流程演示成功!")

9.2 你的 API 如何验证 Token? #

在实际项目中,你的后端 API(如 Flask、FastAPI)需要验证客户端传来的 Authorization: Bearer <token>。两种常见方式:

  1. 调用 Keycloak 的 Introspection 接口:把 Token 发给 Keycloak,让它告诉你是否有效。
  2. 本地验证 JWT:Keycloak 提供公钥,你的服务用公钥验证 JWT 签名,无需每次请求 Keycloak。

10. 常见问题与排查 #

10.1 获取 Token 时报 401 #

  • 检查用户名、密码是否正确
  • 检查 Client Secret 是否复制正确(注意没有多余空格)
  • 确认用户已启用(Users → 用户 → Details → Enabled 为 ON)

10.2 获取 Token 时报 "Invalid client" #

  • 确认 Client ID 拼写正确
  • 若 Client 为 confidential,必须传 client_secret
  • 确认 Client 的 "Client authentication" 已开启(需要 Secret 时)

10.3 Redirect URI 相关错误 #

  • 在 Client 的 "Valid redirect URIs" 中添加你实际使用的回调地址
  • 开发环境可用 http://localhost:端口/* 或 http://127.0.0.1:端口/*

10.4 连接被拒绝 (Connection refused) #

  • 确认 Keycloak 已启动:docker ps 查看容器
  • 确认端口正确:默认 8080,检查是否被占用
← 上一节 JSON-RPC 下一节 LiteLLM →

访问验证

请输入访问令牌

Token不正确,请重新输入