欢迎光临
我们一直在努力

MCP 认证与授权完全指南:从 OAuth 2.1 到企业级实践

MCP 认证与授权完全指南:从 OAuth 2.1 到企业级实践

前言

Model Context Protocol(MCP)正迅速成为 AI 应用与外部工具、数据源之间的“通用连接层”。然而,当 AI 智能体开始调用退款审批 API、读写数据库、部署代码时,一个问题变得至关重要:如何确保只有授权的客户端和用户能够访问敏感资源?

本文将从架构基础到实战落地,系统讲解 MCP 的认证与授权机制,包括协议演进、核心角色、完整授权流程、不同传输场景的认证方式以及企业级实现方案,帮助你在实际项目中构建安全可控的 MCP 服务。

一、为什么 MCP 需要认证与授权?

1.1 从“能连”到“安全连”

早期的 MCP 版本(2024-11-05)并未内置授权支持。但随着开发者开始用 MCP 打通 AI 系统与生产环境 API,安全问题迅速浮现——AI 智能体可能被恶意提示词诱导调用危险工具,窃取敏感数据或执行未授权操作。

认证(Authentication)解决的是 “谁在发起请求” ,授权(Authorization)解决的是 “请求者能做什么” 。任何 MCP 客户端与服务器之间的请求,都必须经过这两个环节的校验。

1.2 协议版本演进

版本授权支持情况备注
2024-11-05 无内置支持 仅通过环境变量传递凭据
2025-03-26 引入 OAuth 2.1 授权 MCP 服务器作为资源服务器
2025-06-18 / 2025-11-25 授权机制持续优化 支持 OIDC Discovery、CIMD 等

MCP 选择 OAuth 2.1 作为授权协议基础,复用成熟的技术模式,让开发者不必从零发明新方案。

二、MCP 认证架构的核心角色

理解 MCP 的认证逻辑,首先需要理清四个核心角色的职责:

#mermaid-svg-0xNPa0AFzQ6nF0sp{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0xNPa0AFzQ6nF0sp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0xNPa0AFzQ6nF0sp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0xNPa0AFzQ6nF0sp .error-icon{fill:#552222;}#mermaid-svg-0xNPa0AFzQ6nF0sp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0xNPa0AFzQ6nF0sp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0xNPa0AFzQ6nF0sp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0xNPa0AFzQ6nF0sp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0xNPa0AFzQ6nF0sp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0xNPa0AFzQ6nF0sp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0xNPa0AFzQ6nF0sp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0xNPa0AFzQ6nF0sp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0xNPa0AFzQ6nF0sp .marker.cross{stroke:#333333;}#mermaid-svg-0xNPa0AFzQ6nF0sp svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0xNPa0AFzQ6nF0sp p{margin:0;}#mermaid-svg-0xNPa0AFzQ6nF0sp .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-0xNPa0AFzQ6nF0sp .cluster-label text{fill:#333;}#mermaid-svg-0xNPa0AFzQ6nF0sp .cluster-label span{color:#333;}#mermaid-svg-0xNPa0AFzQ6nF0sp .cluster-label span p{background-color:transparent;}#mermaid-svg-0xNPa0AFzQ6nF0sp .label text,#mermaid-svg-0xNPa0AFzQ6nF0sp span{fill:#333;color:#333;}#mermaid-svg-0xNPa0AFzQ6nF0sp .node rect,#mermaid-svg-0xNPa0AFzQ6nF0sp .node circle,#mermaid-svg-0xNPa0AFzQ6nF0sp .node ellipse,#mermaid-svg-0xNPa0AFzQ6nF0sp .node polygon,#mermaid-svg-0xNPa0AFzQ6nF0sp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0xNPa0AFzQ6nF0sp .rough-node .label text,#mermaid-svg-0xNPa0AFzQ6nF0sp .node .label text,#mermaid-svg-0xNPa0AFzQ6nF0sp .image-shape .label,#mermaid-svg-0xNPa0AFzQ6nF0sp .icon-shape .label{text-anchor:middle;}#mermaid-svg-0xNPa0AFzQ6nF0sp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0xNPa0AFzQ6nF0sp .rough-node .label,#mermaid-svg-0xNPa0AFzQ6nF0sp .node .label,#mermaid-svg-0xNPa0AFzQ6nF0sp .image-shape .label,#mermaid-svg-0xNPa0AFzQ6nF0sp .icon-shape .label{text-align:center;}#mermaid-svg-0xNPa0AFzQ6nF0sp .node.clickable{cursor:pointer;}#mermaid-svg-0xNPa0AFzQ6nF0sp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0xNPa0AFzQ6nF0sp .arrowheadPath{fill:#333333;}#mermaid-svg-0xNPa0AFzQ6nF0sp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0xNPa0AFzQ6nF0sp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0xNPa0AFzQ6nF0sp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0xNPa0AFzQ6nF0sp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0xNPa0AFzQ6nF0sp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0xNPa0AFzQ6nF0sp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0xNPa0AFzQ6nF0sp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0xNPa0AFzQ6nF0sp .cluster text{fill:#333;}#mermaid-svg-0xNPa0AFzQ6nF0sp .cluster span{color:#333;}#mermaid-svg-0xNPa0AFzQ6nF0sp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0xNPa0AFzQ6nF0sp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0xNPa0AFzQ6nF0sp rect.text{fill:none;stroke-width:0;}#mermaid-svg-0xNPa0AFzQ6nF0sp .icon-shape,#mermaid-svg-0xNPa0AFzQ6nF0sp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0xNPa0AFzQ6nF0sp .icon-shape p,#mermaid-svg-0xNPa0AFzQ6nF0sp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0xNPa0AFzQ6nF0sp .icon-shape .label rect,#mermaid-svg-0xNPa0AFzQ6nF0sp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0xNPa0AFzQ6nF0sp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0xNPa0AFzQ6nF0sp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0xNPa0AFzQ6nF0sp :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

MCP 认证架构

1. 获取访问令牌

2. 携带令牌请求

3. 验证令牌

用户/资源所有者

MCP 宿主应用如 Claude Desktop

MCP 客户端

MCP 服务器资源服务器

授权服务器独立/外置

角色详解:

角色职责
资源所有者 授权客户端访问其受保护资源的实体,通常是终端用户
MCP 客户端 代表资源所有者发起请求的应用程序
MCP 服务器 托管受保护资源的服务器,作为 OAuth 2.1 资源服务器
授权服务器 负责验证用户身份并颁发访问令牌

在标准 MCP 授权流程中,MCP 服务器不颁发令牌,只负责消耗(验证)外部授权服务器颁发的访问令牌。

三、完整授权流程:从 401 到连接建立

MCP 的授权流程遵循 OAuth 2.1 规范,下面以流程图和文字结合的方式拆解每个步骤。

3.1 整体授权流程

用户/浏览器

授权服务器

MCP 服务器

MCP 客户端

用户/浏览器

授权服务器

MCP 服务器

MCP 客户端

#mermaid-svg-9qfJQof7LNPaZ0vw{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9qfJQof7LNPaZ0vw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9qfJQof7LNPaZ0vw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9qfJQof7LNPaZ0vw .error-icon{fill:#552222;}#mermaid-svg-9qfJQof7LNPaZ0vw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9qfJQof7LNPaZ0vw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9qfJQof7LNPaZ0vw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9qfJQof7LNPaZ0vw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9qfJQof7LNPaZ0vw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9qfJQof7LNPaZ0vw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9qfJQof7LNPaZ0vw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9qfJQof7LNPaZ0vw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9qfJQof7LNPaZ0vw .marker.cross{stroke:#333333;}#mermaid-svg-9qfJQof7LNPaZ0vw svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9qfJQof7LNPaZ0vw p{margin:0;}#mermaid-svg-9qfJQof7LNPaZ0vw .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9qfJQof7LNPaZ0vw text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-9qfJQof7LNPaZ0vw .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-9qfJQof7LNPaZ0vw .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-9qfJQof7LNPaZ0vw .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-9qfJQof7LNPaZ0vw .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-9qfJQof7LNPaZ0vw #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-9qfJQof7LNPaZ0vw .sequenceNumber{fill:white;}#mermaid-svg-9qfJQof7LNPaZ0vw #sequencenumber{fill:#333;}#mermaid-svg-9qfJQof7LNPaZ0vw #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-9qfJQof7LNPaZ0vw .messageText{fill:#333;stroke:none;}#mermaid-svg-9qfJQof7LNPaZ0vw .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9qfJQof7LNPaZ0vw .labelText,#mermaid-svg-9qfJQof7LNPaZ0vw .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-9qfJQof7LNPaZ0vw .loopText,#mermaid-svg-9qfJQof7LNPaZ0vw .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-9qfJQof7LNPaZ0vw .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-9qfJQof7LNPaZ0vw .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-9qfJQof7LNPaZ0vw .noteText,#mermaid-svg-9qfJQof7LNPaZ0vw .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-9qfJQof7LNPaZ0vw .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9qfJQof7LNPaZ0vw .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9qfJQof7LNPaZ0vw .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9qfJQof7LNPaZ0vw .actorPopupMenu{position:absolute;}#mermaid-svg-9qfJQof7LNPaZ0vw .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-9qfJQof7LNPaZ0vw .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9qfJQof7LNPaZ0vw .actor-man circle,#mermaid-svg-9qfJQof7LNPaZ0vw line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-9qfJQof7LNPaZ0vw :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

1. 初始握手

2. 受保护资源元数据发现

3. 授权服务器发现

4. OAuth 2.1 with PKCE 流程

5. 访问受保护资源

MCP 请求(无令牌)

401 Unauthorized + WWW-Authenticate header

resource_metadata URL

GET /.well-known/oauth-protected-resource

返回 PRM 文档

{resource, authorization_servers, scopes_supported}

GET /.well-known/oauth-authorization-server

返回 ASM 文档

{authorization_endpoint, token_endpoint, jwks_uri}

打开浏览器跳转 authorization_endpoint

输入凭证,授权

回调并携带授权码

用授权码 + code_verifier 换 access_token

颁发 access_token

MCP 请求 + Bearer token

验证 JWT 签名、iss、aud、exp

返回响应

3.2 关键步骤详解

第一步:初始握手(401 质询)

客户端发起无令牌的请求后,MCP 服务器返回 HTTP 401,并通过 WWW-Authenticate 头部告知受保护资源元数据的地址:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp", resource_metadata="https://server.com/.well-known/oauth-protected-resource"

第二步:受保护资源元数据发现

客户端从该地址获取 PRM 文档,内容示例如下:

{
"resource": "https://server.com/mcp",
"authorization_servers": ["https://auth.server.com"],
"scopes_supported": ["mcp:tools", "mcp:resources", "calc.read", "calc.write"]
}

第三步:授权服务器发现

客户端根据 authorization_servers 字段,通过 OAuth 2.0 授权服务器元数据(RFC 8414)获取 authorization_endpoint、token_endpoint、jwks_uri 等信息。

第四步:OAuth 2.1 + PKCE 授权

这是最关键的环节。MCP 强制使用 PKCE(Proof Key for Code Exchange) 来保护授权码交换,无需客户端存储 client_secret:

客户端生成 code_verifier → 计算 code_challenge → 发送带 code_challenge 的授权请求
→ 用户登录并授权 → 返回授权码 → 用 code_verifier 换取 access_token

第五步:携带令牌访问资源

客户端将获取的 access_token 放入请求头:Authorization: Bearer <token>,MCP 服务器验证令牌后处理请求。

四、认证与授权方式分类

4.1 两种传输协议的不同认证策略

MCP 支持两种传输技术,认证策略差异显著:

传输类型认证方式适用场景
Stdio(本地进程) 无显式认证;通过环境变量传递凭据 本地 MCP 服务器,作为客户端子进程运行
Streamable HTTP(远程) OAuth 2.1 + PKCE,遵循 RFC 9728 远程托管的 MCP 服务器,企业部署

对于 Stdio 传输,MCP 服务器作为客户端子进程启动,两者之间无需额外认证。后端服务的授权可通过环境变量配置 API Key 等方式实现。

4.2 两种认证模式对比

认证模式适用场景安全级别主要风险
API Key 快速原型、本地部署、Demo 演示 ⚠️ 低 权限无限制、轮换麻烦、无过期机制
OAuth 2.1 + PKCE 生产环境、多用户场景 ✅ 高 需搭建授权服务器,复杂度高

API Key 虽然简单直接,但存在权限无限制、难以轮换、无法绑定具体用户等风险,生产环境强烈建议使用 OAuth 2.1。

五、企业级实现方案

5.1 架构模式:外部 IdP + JWKS 验证

在企业环境中,MCP 服务器通常不自己颁发令牌,而是委托给现有的身份提供商(IdP),仅负责验证令牌:

#mermaid-svg-VgyKVigiBWDdIvJf{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VgyKVigiBWDdIvJf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VgyKVigiBWDdIvJf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VgyKVigiBWDdIvJf .error-icon{fill:#552222;}#mermaid-svg-VgyKVigiBWDdIvJf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VgyKVigiBWDdIvJf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VgyKVigiBWDdIvJf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VgyKVigiBWDdIvJf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VgyKVigiBWDdIvJf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VgyKVigiBWDdIvJf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VgyKVigiBWDdIvJf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VgyKVigiBWDdIvJf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VgyKVigiBWDdIvJf .marker.cross{stroke:#333333;}#mermaid-svg-VgyKVigiBWDdIvJf svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VgyKVigiBWDdIvJf p{margin:0;}#mermaid-svg-VgyKVigiBWDdIvJf .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-VgyKVigiBWDdIvJf .cluster-label text{fill:#333;}#mermaid-svg-VgyKVigiBWDdIvJf .cluster-label span{color:#333;}#mermaid-svg-VgyKVigiBWDdIvJf .cluster-label span p{background-color:transparent;}#mermaid-svg-VgyKVigiBWDdIvJf .label text,#mermaid-svg-VgyKVigiBWDdIvJf span{fill:#333;color:#333;}#mermaid-svg-VgyKVigiBWDdIvJf .node rect,#mermaid-svg-VgyKVigiBWDdIvJf .node circle,#mermaid-svg-VgyKVigiBWDdIvJf .node ellipse,#mermaid-svg-VgyKVigiBWDdIvJf .node polygon,#mermaid-svg-VgyKVigiBWDdIvJf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VgyKVigiBWDdIvJf .rough-node .label text,#mermaid-svg-VgyKVigiBWDdIvJf .node .label text,#mermaid-svg-VgyKVigiBWDdIvJf .image-shape .label,#mermaid-svg-VgyKVigiBWDdIvJf .icon-shape .label{text-anchor:middle;}#mermaid-svg-VgyKVigiBWDdIvJf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VgyKVigiBWDdIvJf .rough-node .label,#mermaid-svg-VgyKVigiBWDdIvJf .node .label,#mermaid-svg-VgyKVigiBWDdIvJf .image-shape .label,#mermaid-svg-VgyKVigiBWDdIvJf .icon-shape .label{text-align:center;}#mermaid-svg-VgyKVigiBWDdIvJf .node.clickable{cursor:pointer;}#mermaid-svg-VgyKVigiBWDdIvJf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VgyKVigiBWDdIvJf .arrowheadPath{fill:#333333;}#mermaid-svg-VgyKVigiBWDdIvJf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VgyKVigiBWDdIvJf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VgyKVigiBWDdIvJf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VgyKVigiBWDdIvJf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VgyKVigiBWDdIvJf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VgyKVigiBWDdIvJf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VgyKVigiBWDdIvJf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VgyKVigiBWDdIvJf .cluster text{fill:#333;}#mermaid-svg-VgyKVigiBWDdIvJf .cluster span{color:#333;}#mermaid-svg-VgyKVigiBWDdIvJf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VgyKVigiBWDdIvJf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VgyKVigiBWDdIvJf rect.text{fill:none;stroke-width:0;}#mermaid-svg-VgyKVigiBWDdIvJf .icon-shape,#mermaid-svg-VgyKVigiBWDdIvJf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VgyKVigiBWDdIvJf .icon-shape p,#mermaid-svg-VgyKVigiBWDdIvJf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VgyKVigiBWDdIvJf .icon-shape .label rect,#mermaid-svg-VgyKVigiBWDdIvJf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VgyKVigiBWDdIvJf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VgyKVigiBWDdIvJf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VgyKVigiBWDdIvJf :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

企业认证架构

JWT token

Bearer JWT

获取 JWKS

验证签名

响应

用户

IdPOkta/Auth0/Entra ID

MCP 客户端

MCP 服务器

MCP 服务器的工作流程:

  • 从请求头 Authorization: Bearer 中提取 JWT
  • 从 IdP 的 /.well-known/jwks.json 获取公钥(建议缓存并定期刷新)
  • 验证 JWT 签名、iss(颁发者)、aud(受众)和 exp(过期时间)
  • 从 sub 等声明中提取用户身份
  • 5.2 精细授权:基于 Scope 的 RBAC

    OAuth 2.0 的 scope 机制天然支持 RBAC。以下是一个典型配置:

    Scope权限含义适用角色
    calc.read 只读计算器工具 普通用户
    calc.write 创建/修改计算 高级用户
    admin 管理权限 系统管理员

    MCP 客户端获取令牌时请求所需 scope,服务器验证 token 中的 scope 是否符合操作要求。

    5.3 机器对机器场景:Client Credentials 扩展

    对于 CI/CD 流水线、后台服务等无人值守场景,标准 OAuth 授权流程(需要用户浏览器交互)不再适用。MCP 提供了 OAuth Client Credentials 扩展,支持应用层凭据(client_id + client_secret 或 JWT 断言)直接换取令牌。

    # 示例:使用 client_credentials 获取 token
    curl -X POST https://auth.server.com/token \\
    -d "grant_type=client_credentials" \\
    -d "client_id=mcp-client" \\
    -d "client_secret=your-secret" \\
    -d "scope=calc.read"

    六、安全最佳实践(OWASP 标准)

    6.1 原则一:最小权限原则

    每个 MCP 服务器只应获得完成其功能所必需的最小权限集,绝不授予超出需要的 scope。

    6.2 原则二:端到端加密与短生命周期令牌

    • 对所有通信启用 TLS 加密
    • 使用短生命周期的访问令牌(如 1 小时内过期),配合刷新令牌续期
    • 定期轮换 JWT 签名密钥和 client_secret

    6.3 原则三:Token Audience(受众)校验

    每个 token 都必须验证 aud 是否匹配预期的 MCP 服务器,防止 token 被重用到其他服务:

    if (!tokenClaims.getAudience().contains("mcp-server")) {
    throw new SecurityException("Invalid audience");
    }

    6.4 原则四:强制用户明确 Consent

    每一次授权都需获取用户的明确确认,防止攻击者通过伪造授权请求骗取令牌。

    6.5 其他安全要点

    • 本地运行时应仅绑定到 localhost(127.0.0.1),防止外部访问
    • 使用 OpenID Connect Discovery 1.0 增强授权服务器发现
    • 对所有工具输入进行验证、实施速率限制、清理输出内容

    七、实操指南:从零为 MCP 服务配置认证

    7.1 准备工作

    确认你的 MCP 服务器需要满足以下要求:

    • 使用 Streamable HTTP 传输(非 stdio)
    • 能够暴露 /.well-known/oauth-protected-resource 和 /.well-known/oauth-authorization-server 端点

    7.2 步骤一:为 MCP 服务器配置受保护资源元数据

    在服务器根路径创建 /.well-known/oauth-protected-resource 端点,返回以下内容:

    {
    "resource": "https://your-mcp-server.com/mcp",
    "authorization_servers": ["https://auth.your-company.com"],
    "scopes_supported": ["mcp:tools", "calc.read", "calc.write"],
    "bearer_methods_supported": ["header"]
    }

    7.3 步骤二:配置授权服务器支持 PKCE

    确保授权服务器:

    • 支持 OAuth 2.1 授权码流程
    • 要求并验证 PKCE(code_challenge 和 code_verifier)
    • 支持 OpenID Connect Discovery 或 RFC 8414 元数据

    7.4 步骤三:在 MCP 服务器中实现令牌验证

    选择适合你技术栈的实现方案:

    方案一:使用 FastAPI-MCP(Python)

    from fastapi_mcp import MCPMiddleware
    app.add_middleware(MCPMiddleware, auth_url="https://auth.server.com")

    方案二:Spring AI + OAuth2 Resource Server(Java)

    # application.yml
    spring:
    security:
    oauth2:
    resourceserver:
    jwt:
    issuer-uri: https://auth.server.com

    方案三:Dapr Bearer 中间件(云原生)

    apiVersion: dapr.io/v1alpha1
    kind: Component
    metadata:
    name: bearer
    spec:
    type: middleware.http.bearer
    version: v1
    metadata:
    name: issuer
    value: "https://auth.server.com"

    7.5 步骤四:测试验证

  • 启动 MCP 服务器
  • 使用 MCP Inspector 连接,验证 401 质询是否返回正确的 WWW-Authenticate 头
  • 完成 OAuth 授权流程,获取 access_token
  • 使用该 token 访问受保护资源,验证是否成功
  • 八、总结与选型建议

    场景推荐方案关键考量
    本地开发/原型验证 API Key + 环境变量 快速上手,成本低
    远程生产部署 OAuth 2.1 + PKCE 安全性高,支持多用户
    企业内部已有 IdP JWKS 令牌验证 复用现有身份体系,无状态验证
    CI/CD / 机器访问 OAuth Client Credentials 支持无人值守自动化
    多团队多租户 CIMD(Client ID Metadata Documents) 更强的客户端隔离和治理

    核心要点回顾:

    • MCP 将认证与授权职责分离给外部授权服务器,自身专注于作为资源服务器验证令牌
    • 强制使用 PKCE 的 OAuth 2.1 授权码流程,无需硬编码客户端密钥
    • 本地 stdio 传输无需认证,远程 HTTP 传输必须遵循完整的 OAuth 授权流程
    • 生产环境应结合 JWKS、scope RBAC、令牌受众校验,构建多层防御

    随着 MCP 规范持续演进(如 CIMD 逐步替代动态客户端注册、OIDC Discovery 增强),开发者应密切关注官方更新,确保安全策略与时俱进。

    参考资料

    • MCP Specification 2025-11-25 – Authorization
    • OWASP MCP Security Cheat Sheet
    • Baeldung – MCP Authorization With Spring AI and OAuth2
    • A Developer‘s Guide to MCP Authentication
    • Building Enterprise-Ready MCP Servers with JWKS
    • OAuth 2.1 Draft Specification

    赞(0)
    未经允许不得转载:171主机测评 » MCP 认证与授权完全指南:从 OAuth 2.1 到企业级实践
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址