免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
如何給自研 MCP 服務(wù),加上安全認(rèn)證?

作者:小傅哥
博客:https://bugstack.cn

?

沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!??

?

大家好,我是技術(shù)UP主小傅哥。

剛剛過(guò)去兩個(gè)月,市面的 MCP 服務(wù),如雨后春筍一般不斷涌現(xiàn)出來(lái),包括;百度、高德、網(wǎng)盤(pán)、支付寶。這些 MCP 服務(wù),可以讓我們基于 Spring AI 框架構(gòu)建的 Agent 具備非常豐富的使用功能。同時(shí)這也說(shuō)明,程序員???????,應(yīng)該具備開(kāi)發(fā) MCP 服務(wù)的能力,Spring AI 讓 Java 再次牛逼!

關(guān)于 RAG、MCP、Agent 是什么,這里小傅哥已經(jīng)編寫(xiě)過(guò)了全套的教程,可以進(jìn)入學(xué)習(xí);https://bugstack.cn/md/project/ai-knowledge/ai-knowledge.html

本節(jié)小傅哥主要給大家分享,關(guān)于市面上這些標(biāo)準(zhǔn)的帶有驗(yàn)證權(quán)限的 MCP 服務(wù),怎么使用 Spring AI 進(jìn)行對(duì)接。同時(shí)我們自己開(kāi)發(fā)的 MCP 服務(wù),怎么加上權(quán)限校驗(yàn)。

一、舉例,對(duì)接高德地圖 MCP

高德地圖 MCP Server;

{
  "mcpServers": {
    "amap-amap-sse": {
      "url""https://mcp.amap.com/sse?key=您在高德官網(wǎng)上申請(qǐng)的key"
    }
  }
}
  • 官網(wǎng):https://lbs.amap.com/api/mcp-server/gettingstarted  - 官網(wǎng)提供了創(chuàng)建對(duì)接 Key

1. 代碼使用示例

@Configuration
publicclass McpConfig {

    @Bean
    public List<NamedClientMcpTransport> mcpClientTransport() {
        McpClientTransport transport = HttpClientSseClientTransport
                .builder("https://mcp.amap.com")
                .sseEndpoint("/sse?key=<your_key>")
                .objectMapper(new ObjectMapper())
                .build();

        return Collections.singletonList(new NamedClientMcpTransport("amap", transport));
    }
    
}
  • 對(duì)接時(shí),需要設(shè)定 sseEndpoint 如果不設(shè)定個(gè),Spring AI 默認(rèn)是對(duì) builder 的 baseUrl 值添加 /sse 的。
  • 所以,如果你要對(duì)接外部帶有驗(yàn)證權(quán)限的 MCP 服務(wù),需要手動(dòng)設(shè)置下 sseEndpoint 值。

2. 項(xiàng)目中的配置

小傅哥,帶著大家做的 Ai Agent,也支持了外部的這些帶有權(quán)限校驗(yàn)的 MCP 服務(wù)。你可以,以多種方式進(jìn)行配置。如;

{
 "baseUri":"https://mcp.amap.com",
        "sseEndpoint":"/sse?key=801aabf79ed0ff78603cfe85****"
}
{
 "baseUri":"https://mcp.amap.com",
        "sseEndpoint":"/sse?key=801aabf79ed0ff78603cfe85****"
}
  • 以上兩種配置方式,在 ai-agent-station 都做了兼容處理。以下是兼容代碼,學(xué)習(xí)這部分項(xiàng)目的伙伴,可以直接閱讀課程代碼。
@Slf4j
@Component
publicclass AiClientToolMcpNode extends AbstractArmorySupport {

   // ... 省略部分代碼

    protected McpSyncClient createMcpSyncClient(AiClientToolMcpVO aiClientToolMcpVO) {
        String transportType = aiClientToolMcpVO.getTransportType();

        switch (transportType) {
            case"sse" -> {
                AiClientToolMcpVO.TransportConfigSse transportConfigSse = aiClientToolMcpVO.getTransportConfigSse();
                // http://127.0.0.1:9999/sse?apikey=DElk89iu8Ehhnbu
                String originalBaseUri = transportConfigSse.getBaseUri();
                String baseUri;
                String sseEndpoint;

                int queryParamStartIndex = originalBaseUri.indexOf("sse");
                if (queryParamStartIndex != -1) {
                    baseUri = originalBaseUri.substring(0, queryParamStartIndex - 1);
                    sseEndpoint = originalBaseUri.substring(queryParamStartIndex - 1);
                } else {
                    baseUri = originalBaseUri;
                    sseEndpoint = transportConfigSse.getSseEndpoint();
                }

                sseEndpoint = StringUtils.isBlank(sseEndpoint) ? "/sse" : sseEndpoint;

                HttpClientSseClientTransport sseClientTransport = HttpClientSseClientTransport
                        .builder(baseUri) // 使用截取后的 baseUri
                        .sseEndpoint(sseEndpoint) // 使用截取或默認(rèn)的 sseEndpoint
                        .build();

                McpSyncClient mcpSyncClient = McpClient.sync(sseClientTransport).requestTimeout(Duration.ofMinutes(aiClientToolMcpVO.getRequestTimeout())).build();
                var init_sse = mcpSyncClient.initialize();
                log.info("Tool SSE MCP Initialized {}", init_sse);
                return mcpSyncClient;
            }
            case"stdio" -> {
                AiClientToolMcpVO.TransportConfigStdio transportConfigStdio = aiClientToolMcpVO.getTransportConfigStdio();
                Map<String, AiClientToolMcpVO.TransportConfigStdio.Stdio> stdioMap = transportConfigStdio.getStdio();
                AiClientToolMcpVO.TransportConfigStdio.Stdio stdio = stdioMap.get(aiClientToolMcpVO.getMcpName());

                // https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem
                var stdioParams = ServerParameters.builder(stdio.getCommand())
                        .args(stdio.getArgs())
                        .build();
                var mcpClient = McpClient.sync(new StdioClientTransport(stdioParams))
                        .requestTimeout(Duration.ofSeconds(aiClientToolMcpVO.getRequestTimeout())).build();
                var init_stdio = mcpClient.initialize();
                log.info("Tool Stdio MCP Initialized {}", init_stdio);
                return mcpClient;
            }
        }

        thrownew RuntimeException("err! transportType " + transportType + " not exist!");
    }

}
  • 以上代碼,是為了自動(dòng)化構(gòu)建 MCP 服務(wù)的,其中 case sse 的部分,會(huì)對(duì) url 進(jìn)行拆分,如果本身 url 配置了校驗(yàn)權(quán)限,則不會(huì)從另外一個(gè)參數(shù)獲取,否則從另外參數(shù)拼接。這樣就可以很好的擴(kuò)展用戶(hù)配置時(shí)的多樣性問(wèn)題了。

以上是關(guān)于帶有權(quán)限校驗(yàn)的 MCP 服務(wù)配置的問(wèn)題,接下來(lái),我們要說(shuō)下怎么自己開(kāi)發(fā)一個(gè)帶有權(quán)限校驗(yàn)

二、實(shí)現(xiàn),帶有權(quán)限校驗(yàn)的 MCP 服務(wù)

首先,Spring AI 是有意提供基于自家的 OAuth2 框架,完成 MCP 服務(wù)的多樣性權(quán)限校驗(yàn)的。不過(guò)目前提供的方案能用,但不算成熟。

官網(wǎng):https://spring.io/blog/2025/05/19/spring-ai-mcp-client-oauth2

1. 基于 OAuth2 認(rèn)證

1.1 工程結(jié)構(gòu)

  • 工程:https://gitcode.net/KnowledgePlanet/mcp-server-auth - 面向于學(xué)習(xí) ai-agent-station 的伙伴
  • 使用 OAuth2 基于 Spring MVC 的方式到也簡(jiǎn)單,知道添加配置即可。

1.2 所需的 POM 文件

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>

1.3 測(cè)試驗(yàn)證

@Slf4j
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "server.shutdown=immediate")
publicclass ApiTest {

    @LocalServerPort
    privateint port;

    privatefinal ObjectMapper objectMapper = new ObjectMapper();

    @Test
    public void test_access_token() throws IOException, InterruptedException {
        String token = obtainAccessToken();
        log.info("token:{}", token);
        // eyJraWQiOiJiMWQ0MGIxNi1hOTYzLTQ2NmYtYTVkOC02NGRjMzg0ODljYWEiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJtY3AtY2xpZW50IiwiYXVkIjoibWNwLWNsaWVudCIsIm5iZiI6MTc0ODA1MTc1NiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1ODA5OCIsImV4cCI6MTc0ODA1MjA1NiwiaWF0IjoxNzQ4MDUxNzU2LCJqdGkiOiI5NjY4ZmZkMi0wNjQ2LTRiNmItODQ4Ni1jYzk3ZjMxNTdmOTEifQ.CG4GYai_NYkmfcqmNi-_HYG06Kan04uNSsC2ivn_eC9Ra6xMKYTs9KIT7k5lKxSFRUOPI7K0zJNVvNXrrIe0iFl-csrG2vGukNTGTPMxtUi2hheBMRbnvjvuojW4DeOEE8UOpdA6uow67ucwcymTlDXE-k7OjRZeyp7UdVz2WyoDFQhLB6ihLbDSj5puAZxfNocirRzo36gmW243aW9f1gugPUcpND-oobc2q8xyBG2cX2AlGXUSS-v9PLjHr2W2smFTKHHGwu7FpMMBnJLUT5gZD0llIg6yqro91nFaAFOpGHXjRZYgVjkRlzxx08Zuquva9PbStxbUl2j8hI43_Q

        var client = HttpClient.newHttpClient();

        var request = HttpRequest.newBuilder()
                .uri(URI.create("http://localhost:" + port + "/sse"))
                .header("Accept""text/event-stream")
                .header("Authorization""Bearer " + token)
                .GET()
                .build();

        var responseCode = new AtomicInteger(-1);
        var sseRequest = client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()).thenApply(response -> {
            responseCode.set(response.statusCode());
            if (response.statusCode() == 200) {
                log.info("response:{}", JSON.toJSONString(response));
                return response;
            }
            else {
                thrownew RuntimeException("Failed to connect to SSE endpoint: " + response.statusCode());
            }
        });

        await().atMost(Duration.ofSeconds(1)).until(sseRequest::isDone);
        assertThat(sseRequest).isCompleted();
        assertThat(responseCode).hasValue(200);
    }

    private String obtainAccessToken() throws IOException, InterruptedException {
        var client = HttpClient.newHttpClient();

        var clearTextCredentials = "mcp-client:secret".getBytes(StandardCharsets.UTF_8);
        var credentials = new String(Base64.getUrlEncoder().encode(clearTextCredentials));
        var request = HttpRequest.newBuilder()
                .uri(URI.create("http://localhost:" + port + "/oauth2/token"))
                .header("Authorization""Basic " + credentials)
                .header("Content-Type""application/x-www-form-urlencoded")
                .POST(ofString("grant_type=client_credentials"))
                .build();

        var rawResponse = client.send(request, HttpResponse.BodyHandlers.ofString()).body();

        Map<String, String> response = objectMapper.readValue(rawResponse, Map.class);
        return response.get("access_token");
    }
    
}
  • 加上 OAuth2 以后,就需要獲取并設(shè)置 accessToken 才能訪(fǎng)問(wèn) sse 服務(wù)了。

2. 基于網(wǎng)關(guān)實(shí)現(xiàn)

其實(shí)我們到不非得依賴(lài)于 Spring OAuth2 往 MCP 服務(wù)里在添加一些其他的東西。倒不如直接走網(wǎng)關(guān),讓網(wǎng)關(guān)來(lái)管理權(quán)限,MCP 服務(wù)只做服務(wù)的事情就好。

這里我們基于 Nginx 來(lái)配置驗(yàn)證功能,當(dāng)然你可以在學(xué)習(xí)本節(jié)的案例后,配置任何其他的網(wǎng)關(guān)來(lái)管理你的 MCP 服務(wù)。

注意,這里的前置條件為你已經(jīng)跟著小傅哥,至少完成了一個(gè) MCP 服務(wù)。課程;https://t.zsxq.com/GwNZp

當(dāng)我們有了一套基于 sse 形式訪(fǎng)問(wèn)的 mcp 后,我們是可以給這套 mcp 基于 nginx 轉(zhuǎn)發(fā)的形式進(jìn)行訪(fǎng)問(wèn)后面真實(shí)的 mcp 服務(wù)的。在轉(zhuǎn)發(fā)的過(guò)程中,拿到用戶(hù)在地址 http://127.0.0.1:9999/sse?apikey=DElk89iu8Ehhnbu mcp 服務(wù)后面拼接的 apikey,并對(duì) apikey 進(jìn)行驗(yàn)證。

2.1 配置工程

  • 在 ai-agent-station 項(xiàng)目下,提供了 dev-ops-v2 配置 mcp 服務(wù)轉(zhuǎn)發(fā)和驗(yàn)證能力。
  • 注意,部署的時(shí)候,要把 mcp.localhost.conf 轉(zhuǎn)發(fā)的 mcp 服務(wù)的地址,更換為你的地址。
  • 另外,每一個(gè) mcp.localhost.conf 還可以配置域名,這樣就達(dá)到了高德地圖 mcp 訪(fǎng)問(wèn)的效果。舉例;https://fatie.mcp.bugstack.cn/sse/apikey=*******

2.2 服務(wù)轉(zhuǎn)發(fā)&校驗(yàn)

# 可以負(fù)載服務(wù)
upstream backend_servers {
    server 192.168.1.108:8101;
}

server {
    listen 80;

    server_name 192.168.1.104;  # 修改為你的實(shí)際服務(wù)器 IP 或域名【域名需要備案】

    location /sse {
        # 驗(yàn)證apikey參數(shù),這個(gè)apikey也可以對(duì)接服務(wù)端接口來(lái)處理。
        if ($arg_apikey != "DElk89iu8Ehhnbu") {
            return403; # 如果apikey不正確,返回403禁止訪(fǎng)問(wèn)
        }

        # 重寫(xiě)URL,去掉apikey參數(shù)
        rewrite ^(/sse/)\?apikey=.* $1break;

        proxy_pass http://backend_servers;  # 將請(qǐng)求代理到上游服務(wù)器組
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        chunked_transfer_encoding off;
        proxy_buffering off;
        proxy_cache off;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /mcp/message {
        proxy_pass http://backend_servers;  # 將請(qǐng)求代理到上游服務(wù)器組
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

}
  • 特別注意,mcp 服務(wù)是有2個(gè)步驟的,一個(gè)是 sse 訪(fǎng)問(wèn),還有一個(gè) mcp/message 的處理。我們只需要對(duì) sse 的請(qǐng)求進(jìn)行驗(yàn)證即可。
  • /sse 請(qǐng)求路徑,需要會(huì)提取 apikey 與 nginx 配置的值進(jìn)行對(duì)比,如果不正確則會(huì)返回一個(gè) 403 禁止訪(fǎng)問(wèn),通過(guò)則放行。
  • 之后重寫(xiě) url 地址,讓轉(zhuǎn)發(fā)到本身 mcp 的地址是干凈的。從 http://127.0.0.1:9999/sse?apikey=DElk89iu8Ehhnbu 驗(yàn)證轉(zhuǎn)發(fā)后為 http://192.168.1.108:8101/sse

2.3 功能驗(yàn)證

首先,要確保你的 mcp 服務(wù)是可以使用的。如,訪(fǎng)問(wèn);http://192.168.1.108:8101/sse 可以獲得到結(jié)果。

- 如圖,驗(yàn)證成功。我們可以通過(guò)轉(zhuǎn)發(fā)的方式進(jìn)行驗(yàn)證和使用。 - 另外,有了轉(zhuǎn)發(fā)和驗(yàn)證,你原本的服務(wù),sse 8101 就不用對(duì)外了。只有你的網(wǎng)關(guān)(nginx)可以訪(fǎng)問(wèn)即可。這樣就可以控制權(quán)限了。

2.4 動(dòng)態(tài)驗(yàn)證

那么,目前我們配置的nginx 轉(zhuǎn)發(fā)這不是一個(gè)固定的權(quán)限賬號(hào)嗎,怎么讓不同的接入方都申請(qǐng)一個(gè)秘鑰key來(lái)使用呢?這里我們需要使用到 nginx 的 auth 認(rèn)證模塊。

# 可以負(fù)載服務(wù)
upstream backend_servers {
    server 192.168.1.108:8101;
}

server {
    listen 80;

    server_name 192.168.1.104;  # 修改為你的實(shí)際服務(wù)器 IP 或域名【域名需要備案】

    location /sse {
        auth_request /auth;

        # 重寫(xiě)URL,去掉apikey參數(shù)
        rewrite ^(/sse/)\?apikey=.* $1break;

        proxy_pass http://backend_servers;  # 將請(qǐng)求代理到上游服務(wù)器組
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        chunked_transfer_encoding off;
        proxy_buffering off;
        proxy_cache off;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /mcp/message {
        proxy_pass http://backend_servers;  # 將請(qǐng)求代理到上游服務(wù)器組
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

     location = /auth {
        # 發(fā)送子請(qǐng)求到HTTP服務(wù),驗(yàn)證客戶(hù)端的憑據(jù),返回響應(yīng)碼
        internal;
        # 設(shè)置參數(shù)
        set $query '';
        if ($request_uri ~* "[^\?]+\?(.*)$") {
            set $query $1;
        }
        # 驗(yàn)證成功,返回200 OK
        proxy_pass http://207.246.123.*:8090/auth/token?$query;
        # 發(fā)送原始請(qǐng)求
        proxy_pass_request_body off;
        # 清空 Content-Type
        proxy_set_header Content-Type "";
     }  

}

  • 在訪(fǎng)問(wèn) /sse 的時(shí)候,增加 auth 認(rèn)證,auth 來(lái)訪(fǎng)問(wèn)本地一個(gè) http 接口。你可以是 SpringBoot 實(shí)現(xiàn)的接口。這個(gè)接口負(fù)責(zé)驗(yàn)證你的秘鑰是否正確。同時(shí)你的 SpringBoot 服務(wù)還可以提供出一個(gè)創(chuàng)建秘鑰的平臺(tái),讓接入方使用。
  • 其實(shí)類(lèi)似這樣的場(chǎng)景,使用功能更加豐富的 api 網(wǎng)關(guān)都是自帶的,或者 github 一些專(zhuān)門(mén)為 mcp 做網(wǎng)關(guān)服務(wù)的也都有。

三、增強(qiáng),學(xué)習(xí) rag、mcp、agent

小傅哥,已經(jīng)為你準(zhǔn)備好了一套 AI RAG、MCP、Agent 實(shí)踐編程課程,使用 Java + Spring AI 框架,增強(qiáng)自己的 AI 應(yīng)用開(kāi)發(fā)能力,迅速囤積編程技能,滿(mǎn)足各個(gè)公司招聘時(shí)對(duì)AI應(yīng)用類(lèi)開(kāi)發(fā)的要求!如下,課程目錄,全程文檔小冊(cè) + 視頻帶著你從0到1學(xué)習(xí)。

第1期 RAG Spring AI 0.8.1 - 完結(jié)

  1. 【更】AI RAG 知識(shí)庫(kù),項(xiàng)目介紹&需求分析&環(huán)境說(shuō)明
  2. 【更】初始化知識(shí)庫(kù)工程&部署模型&提交代碼
  3. 【更】Ollama DeepSeek 流式應(yīng)答接口實(shí)現(xiàn)
  4. 【更】Ollama DeepSeek 流式應(yīng)答頁(yè)面對(duì)接
  5. 【更】Ollama RAG 知識(shí)庫(kù)上傳、解析和驗(yàn)證
  6. 【更】Ollama RAG 知識(shí)庫(kù)接口服務(wù)實(shí)現(xiàn)
  7. 【更】基于AI工具,設(shè)計(jì)前端UI和接口對(duì)接
  8. 【更】Git倉(cāng)庫(kù)代碼庫(kù)解析到知識(shí)庫(kù)并完善UI對(duì)接
  9. 【更】擴(kuò)展OpenAI模型對(duì)接,以及完整AI對(duì)接
  10. 【更】云服務(wù)器部署知識(shí)庫(kù)(Docker、Nginx)

第2期 MCP Spring AI 1.0.0 - 完結(jié)

  1. 【更】吃上細(xì)糠,升級(jí)SpringAI框架
  2. 【更】康莊大道,上手 AI MCP 工作流
  3. 【更】道山學(xué)海,實(shí)現(xiàn)MCP自動(dòng)發(fā)帖服務(wù)(stdio)
  4. 【更】海納百川,上線(xiàn)MCP自動(dòng)發(fā)帖服務(wù)
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶(hù)發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
反向代理 | Alist Document
Python調(diào)用百度API
cURL 學(xué)習(xí)筆記
高效接入:Suno API與主流編程語(yǔ)言的結(jié)合
使用SAP API portal進(jìn)行SAP SuccessFactors的API測(cè)試
長(zhǎng)連接網(wǎng)關(guān)技術(shù)專(zhuān)題(十二):大模型時(shí)代多模型AI網(wǎng)關(guān)的架構(gòu)設(shè)計(jì)與實(shí)現(xiàn)
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服