使用说明
MCP+AAzure APIM零信任架构全实践实验室
这个实验室被设计成一个故事 4法案。您将首先在云中部署一个简单的MCP服务器,并逐步发展您的架构,直到您达到具有身份验证和代表(OBO)流程的复杂企业场景。
目标(目标状态)
在本次旅程结束时,您将拥有一个安全的架构,其中AI代理代表登录用户访问敏感数据(Microsoft Graph),并受到MCP网关的保护。
场景
你是一名平台工程师,负责为公司的开发人员提供人工智能工具。
- 第1幕: 证明该概念有效(部署)。
- 第2幕: 管理对这些工具的访问(APIM MCP网关)。
- 第3幕: 保护周边(身份)。
- 第4幕: 确保AI使用用户自己的身份访问敏感数据(OBO Flow)。
先决条件
- Azure命令行界面 已安装并登录(
az login). - Azure功能核心工具 (
func). - Python 3.10+.
- Visual Studio Code 随着 GitHub Copilot 扩展(或兼容的MCP客户端)。
舞台准备(环境)
为了确保资源名称的唯一性和可重复性,我们将使用设置脚本。
-
导航到实验室文件夹:
cd mcp-lab -
配置环境变量(在Bash/WSL终端中运行):
source env-setup.sh此命令生成一个随机后缀,并设置以下变量
$GRP,$FUNC,$APIM,$AZURE_CLI_APP_ID等等让这个终端在整个实验室都保持开放。
第一幕:MCP服务器的诞生
在此阶段,您的目标很简单:让MCP服务器在云中运行(Azure Functions)并可公开访问。
1.1提供基础设施
# Create Resource Group
az group create --name $GRP --location $LOC
# Create Storage Account (Required for Functions)
az storage account create --name $STG --location $LOC --resource-group $GRP --sku Standard_LRS
# Create the Function App (Serverless Linux)
az functionapp create --name $FUNC --storage-account $STG --consumption-plan-location $LOC --resource-group $GRP --runtime python --runtime-version 3.11 --functions-version 4 --os-type Linux
1.2部署代码
当前代码有一个简单的工具(echo_message)以及一个安全的工具(get_my_profile_info).让我们发布它。
# Publish the Function App
func azure functionapp publish $FUNC
1.3烟雾测试
让我们通过测试这个简单的工具来验证MCP服务器是否处于活动状态。
-
在VS Code中,编辑
.vscode/mcp.json文件(在项目的根目录下创建lab-apim-mcp如果它不存在):{ "mcpServers": { "azure-func-direct": { "url": "https://<YOUR_FUNCTION_NAME>.azurewebsites.net/mcp", "type": "http" } } }(替换
<YOUR_FUNCTION_NAME>与价值$FUNC.快跑echo $FUNC在终端中查看)。 -
重新启动VS代码或重新加载窗口。
-
打开Copilot Chat并键入:
@azure-func-direct echo_message message="Hello Azure Function"
成功: 如果Copilot响应“来自Azure的回声:Hello Azure函数”,则动作1完成。
第二幕:守护者(Azure MCP网关)
直接访问该功能是不安全的,难以管理。让我们把 Azure API管理(APIM) 前端充当智能MCP网关。
2.1提供APIM
注意:创建“开发人员”层大约需要30-45分钟。喝咖啡的好时机。
az apim create --name $APIM --resource-group $GRP --location $LOC --publisher-name "Lab Admin" --publisher-email "admin@lab.com" --sku-name Developer
2.2将MCP服务器导入APIM
APIM对MCP服务器(GenAI网关)具有本机支持。我们将通过门户网站进行此操作。
-
转到 Azure门户 > API管理 (
$APIM). -
在左侧菜单中,选择 MCP服务器.
-
点击 +创建 > 暴露现有的MCP服务器.
-
按如下方式填写表单字段:
后端MCP服务器
- MCP服务器基本url:
https://<YOUR_FUNCTION_NAME>.azurewebsites.net/mcp(替换<YOUR_FUNCTION_NAME>随着$FUNC.
新建MCP服务器
- 显示名称:
MCP Lab Gateway - 名字:
mcp-lab - 基础路径:
lab - 描述:
Azure Function acting as MCP Server
- MCP服务器基本url:
-
点击 创建.
注意:为了简单起见,此设置使用公共后端端点。在生产环境中,始终使用专用端点或ExpressRoute限制隔离您的Function App,以防止直接访问。
2.3通过网关进行测试
现在让我们将VS代码指向APIM。
- 更新
.vscode/mcp.json:{ "mcpServers": { "azure-apim-gw": { "url": "https://<YOUR_APIM_NAME>.azure-api.net/lab/mcp", "type": "http" } } } - 重新启动VS代码。
- 再次测试:
@azure-apim-gw echo_message message="Hello via APIM"
成功: 现在的流程是:VS代码->APIM->函数。
第3幕:身份与安全
现在让我们锁上门。没有有效的徽章(JWT令牌),任何人都不应调用APIM。
3.1创建应用程序标识(后端)
我们需要在Microsoft Entra ID中注册我们的API。
# Create App Registration
BACKEND_APP_ID=$(az ad app create --display-name "mcp-lab-backend-$RND" --sign-in-audience AzureADMyOrg --query appId -o tsv)
# Create Service Principal
az ad sp create --id $BACKEND_APP_ID
# Define API URI (api://<client_id>)
az ad app update --id $BACKEND_APP_ID --identifier-uris "api://$BACKEND_APP_ID"
# Expose "MCP.Execute" scope (Using Python for safe JSON generation)
python3 -c "import sys, uuid, json; scope_id = str(uuid.uuid4()); print(scope_id)" > scope_id.txt
SCOPE_ID=$(cat scope_id.txt) && rm scope_id.txt
python3 -c "import sys, json; scope_id = sys.argv[1]; data = {'oauth2PermissionScopes': [{'adminConsentDescription': 'Access MCP', 'adminConsentDisplayName': 'Access MCP', 'id': scope_id, 'isEnabled': True, 'type': 'User', 'userConsentDescription': 'Access MCP', 'userConsentDisplayName': 'Access MCP', 'value': 'MCP.Execute'}]}; print(json.dumps(data))" "$SCOPE_ID" > scope.json
az ad app update --id $BACKEND_APP_ID --set api=@scope.json
rm scope.json
echo "Backend App ID: $BACKEND_APP_ID"
3.2在APIM中应用策略(验证JWT)
让我们配置APIM以拒绝没有令牌的调用。
- 在门户中,转到 MCP服务器 >点击 MCP实验室网关.
- 寻找 政策 选项(或
</>图标在 入站处理). - 在中插入验证策略
<inbound>块:
<inbound>
<base />
<!-- Allow CORS (No credentials/wildcard compatible) -->
<cors>
<allowed-origins><origin>*</origin></allowed-origins>
<allowed-methods><method>*</method></allowed-methods>
<allowed-headers><header>*</header></allowed-headers>
</cors>
<!-- Validate Entra ID Token -->
<validate-azure-ad-token tenant-id="{{YOUR_TENANT_ID}}" header-name="Authorization">
<audiences>
<audience>api://{{YOUR_BACKEND_APP_ID}}</audience>
</audiences>
</validate-azure-ad-token>
</inbound>
(替换 {{YOUR_TENANT_ID}} 和 {{YOUR_BACKEND_APP_ID}} 具有实际价值)。
如果你不知道你的租户ID,运行az account show来检索它。
3.3安全测试(预期失败)
尝试使用 @azure-apim-gw echo_message 再次在VS代码中。
预期结果: 您将收到 HTTP 401未经授权 立即出错。这意味着APIM策略正在发挥作用!
第四幕:代表(OBO)流动
“大结局”。我们将让VS Code发送一个令牌,APIM对其进行验证,然后函数将此令牌交换为另一个令牌以读取您的配置文件。
4.1配置机密和权限
该功能需要“代表用户发言”(OBO)的权限。
# 1. Generate Secret for the App
BACKEND_SECRET=$(az ad app credential reset --id $BACKEND_APP_ID --display-name "OBOSecret" --query password -o tsv)
# 2. Grant Graph permission (User.Read) using Global IDs
MS_GRAPH_ID="00000003-0000-0000-c000-000000000000" # Microsoft Graph API
USER_READ_ID="e1fe6dd8-ba31-4d61-89e7-88639da4683d" # User.Read Scope
az ad app permission add --id $BACKEND_APP_ID --api $MS_GRAPH_ID --api-permissions $USER_READ_ID=Scope
az ad app permission grant --id $BACKEND_APP_ID --api $MS_GRAPH_ID --scope User.Read
4.2配置功能应用程序
将凭据发送到云中的Function。
az functionapp config appsettings set --name $FUNC --resource-group $GRP --settings "AZURE_TENANT_ID=$(az account show --query tenantId -o tsv)" "BACKEND_CLIENT_ID=$BACKEND_APP_ID" "BACKEND_CLIENT_SECRET=$BACKEND_SECRET"
4.3配置客户端(VS代码)
我们需要一个真正的令牌。
-
预授权: 允许Azure CLI为您的API请求令牌。我们使用
az rest对CLI模式更改具有鲁棒性。# 1. Get Object ID BACKEND_OBJ_ID=$(az ad app show --id $BACKEND_APP_ID --query id -o tsv) # 2. Generate JSON Config (Python one-liner for safe copy-paste) python3 -c "import sys, json; scope_id = sys.argv[1]; cli_id = sys.argv[2]; data = {'api': {'oauth2PermissionScopes': [{'adminConsentDescription': 'Access MCP', 'adminConsentDisplayName': 'Access MCP', 'id': scope_id, 'isEnabled': True, 'type': 'User', 'userConsentDescription': 'Access MCP', 'userConsentDisplayName': 'Access MCP', 'value': 'MCP.Execute'}], 'preAuthorizedApplications': [{'appId': cli_id, 'delegatedPermissionIds': [scope_id]}]}}; print(json.dumps(data))" "$SCOPE_ID" "$AZURE_CLI_APP_ID" > patch.json # 3. Apply via Graph API az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$BACKEND_OBJ_ID" --headers "Content-Type=application/json" --body @patch.json rm patch.json -
生成令牌:
# Login (Refreshes session) az login --tenant $(az account show --query tenantId -o tsv) # Get Token az account get-access-token --resource "api://$BACKEND_APP_ID" --query accessToken -o tsv复制生成的令牌。
-
配置MCP客户端: 更新
.vscode/mcp.json包含您的令牌 直接. (注意:我们在这里粘贴令牌是因为一些VS Code扩展尚不支持交互式输入提示。){ "mcpServers": { "azure-obo-final": { "url": "https://<YOUR_APIM_NAME>.azure-api.net/lab/mcp", "type": "http", "headers": { "Authorization": "Bearer YOUR_LONG_TOKEN_HERE" } } } }
4.4最终测试
- 重新加载VS Code窗口。
- 打开Copilot Chat并调用安全工具:
@azure-obo-final get_my_profile_info
预期结果: 工具应作出响应 “成功!OBO Flow工作正常”,显示从Microsoft Graph安全检索到的您的姓名和职位。
祝贺 你已经完成了旅程。
清理
不要忘记删除此实验室期间创建的所有资源,以避免持续成本。
az group delete --name $GRP --yes --no-wait