Dubbo-go-Pixiu MCP Authorization 调研

Authorization Flow 授权流程 1. 背景(Background) 随着模型上下文协议 (Model Context Protocol, MCP) 的应用场景从本地环境扩展到开放的互联网,对服务进行访问控制和身份验证变得至关重要。这直接关系到 MCP Server 的核心安全性。当前,Pixiu 网关已经具备将后端 API 包装为 MCP Server 的能力,下一步的核心任务是利用网关的现有能力,集成一套标准的鉴权机制,以完整实现 MCP 规范中的授权 (Authorization) 要求。 MCP 规范明确指出: Implementations using an HTTP-based transport **SHOULD** conform to this specification. (基于 HTTP 的传输应该实现此规范。) 本方案旨在设计一个健壮、可扩展且符合行业最佳实践的授权流程。 2. 核心原则与角色定位 (Core Principles & Role) 2.1 Pixiu 的角色抉择:资源服务器 A protected MCP server acts as an OAuth 2.1 resource server, capable of accepting and responding to protected resource requests using access tokens. ...

July 26, 2025 · 6 min · 1111 words · Similarityoung

go 设计哲学

Go 的一些理解 如何解决循环依赖(Circular Dependency) 什么是循环依赖 package a 导入了 package b package b 又反过来导入了 package a 1 2 3 4 5 6 a.go b.go +--------+ +--------+ | package| --> | package| | a | | b | | | <-- | | +--------+ +--------+ 依赖循环是设计的问题,如果遇到依赖的情况,需要重新思考该如何对项目进行设计。 解决循环依赖 核心思想是 打破循环。Go 社区推崇的最佳实践是利用 接口(Interface) 和 依赖倒置原则(Dependency Inversion Principle, DIP)。 方案一:使用接口(最推荐、最优雅的 “Go Way”) 依赖倒置原则的核心是 “依赖于抽象,而不是依赖于具体实现”。在 Go 中,这个“抽象”就是接口。 方案二:提取公共依赖到新包 如果循环仅仅是因为共享了某个数据结构(struct),那么最简单的办法就是把这个公共的数据结构提取到一个新的、更底层的包里。 警告:不要滥用这个模式,避免创建一个什么都往里扔的“垃圾桶”(common, utils)包。这个新包应该只包含稳定、底层、被广泛依赖的数据结构或常量。 Pixiu 里不是什么东西都能放进 model 和 common 里的,这点需要注意,希望最后不用重构。 ...

June 20, 2025 · 3 min · 584 words · Similarityoung

Zinx 框架学习笔记

函数返回接口而不是具体的结构体 Q: 为什么返回接口 IServer 而不是 server 1 2 3 4 5 6 /* 创建一个服务器句柄 */ func NewServer (name string) ziface.IServer { s:= &Server { Name :name, IPVersion:"tcp4", IP:"0.0.0.0", Port:7777, } return s } A : NewServer 函数返回一个 ziface.IServer 类型的接口而不是直接返回 Server 类型的结构体,这种设计方式具有几个优点: 抽象化:返回一个接口而不是具体的结构体可以抽象化组件之间的交互。使用接口可以隐藏具体实现的细节,使得调用者只需要关心接口提供的方法。这样,如果未来需要改变具体的实现,只要新的实现符合接口的定义,调用代码不需要做任何修改。 灵活性和扩展性:使用接口使得代码更加灵活和易于扩展。比如,如果你想实现一个具有不同特性的服务器,只需创建一个新的结构体,实现相同的接口。这样你可以在不修改现有代码的情况下,为系统引入新的功能。 依赖倒置原则:这是面向对象设计原则之一,主张高层模块不应该依赖低层模块,它们都应该依赖于抽象。接口作为一种高级的抽象,确保了系统各个部分的低耦合性,增加了代码的稳定性。 测试和模拟:使用接口可以更容易地进行单元测试。你可以创建一个或多个模拟(mock)对象来实现同一接口,这样就可以在不涉及具体服务器实现的情况下测试系统的其他部分。 因此,返回 ziface.IServer 接口而不是具体的 Server 结构体,在软件工程中是一种常见且有益的做法,特别是在构建需要高度维护和扩展性的大型软件系统时。这种方式提供了更好的解耦和更灵活的代码管理策略。 函数定义的使用 函数类型的定义在编程中非常有用,尤其在Go语言这样的类型安全语言中,它提供了一系列的好处,这些好处涵盖了代码的可维护性、灵活性和可重用性等方面: 代码复用: 通过定义一种函数签名,可以创建多个遵循相同模式的函数,这有助于统一接口和减少重复代码。这意味着你可以编写高度模块化的代码,其中特定的功能可以由符合同一签名的不同函数实现。 实现抽象和封装: 函数类型的定义允许程序员封装复杂的逻辑,并通过简单的接口暴露功能,使得其他部分的代码不必了解背后的细节就能使用该功能。这有助于降低程序各部分之间的耦合度。 提高灵活性: 函数类型作为参数传递或作为返回类型使用时,可以使代码更加灵活。开发者可以根据具体的需要传入不同的函数实现,例如在处理不同类型的网络请求时,可以根据请求的类型动态选择合适的处理函数。 便于维护和扩展: 函数类型定义使得未来的修改和扩展变得更容易。如果需要修改功能,只需替换或修改实现了该函数类型的具体函数,而不需修改依赖于该类型的代码。这样做减少了对现有代码的干扰,降低了引入新错误的风险。 支持回调和高阶函数: 函数类型的定义是实现回调机制的基础。在Go中,经常会用到回调来处理异步事件、定时任务或在框架中允许用户代码介入框架运行。同时,函数类型的定义是实现高阶函数(接受函数为参数或返回函数的函数)的关键,这在功能编程风格中非常有用。 类型安全: 函数类型提供了类型安全的好处,确保函数的使用者传递正确类型的参数,返回预期类型的数据。这在编译时就能捕捉到许多可能的错误,提高程序的健壮性。 panic 具体使用方法 以下是一个使用 ProcessFunc 类型函数作为参数的示例,该函数将遍历一个字符串切片,并使用传入的 ProcessFunc 类型函数处理每个字符串,然后将结果收集并返回。 首先,我们有一个 ProcessFunc 类型定义,如之前所述: 1 type ProcessFunc func(input string) int 现在,我们将创建一个函数 applyToStrings,它接受一个 ProcessFunc 类型的函数和一个字符串切片,应用该函数到每个字符串,并收集结果: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package main import ( "fmt" ) // 定义ProcessFunc类型 type ProcessFunc func(input string) int // 实现一个计算字符串长度的ProcessFunc func countCharacters(s string) int { return len(s) } // 实现一个返回固定值的ProcessFunc func fixedValue(s string) int { return 42 } // applyToStrings 接受一个ProcessFunc和字符串切片,应用函数到每个字符串 func applyToStrings(f ProcessFunc, inputs []string) []int { results := make([]int, len(inputs)) for i, s := range inputs { results[i] = f(s) // 应用ProcessFunc到每个字符串 } return results } func main() { strings := []string{"hello", "world", "go", "programming"} // 使用countCharacters函数 lengths := applyToStrings(countCharacters, strings) fmt.Println("字符串长度:", lengths) // 使用fixedValue函数 values := applyToStrings(fixedValue, strings) fmt.Println("固定值:", values) } 在这个例子中: ...

September 24, 2024 · 2 min · 285 words · Similarityoung

go 学习笔记

go 学习路线 GoLang语法新奇 golang 中的表达式,加";“与不加都可以,建议不加 另外函数方法中的{},符合 java 中的标准,需要放在函数名后面 变量声明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package main import "fmt" /* 四种变量声明方式 */ var x, y int var ( //这种分解的写法,一般用于声明全局变量 a int b bool ) func main() { //声明变量 默认为 0 var a int fmt.Println("a = ", a) // 方法二 声明变量,并初始化 var b int = 100 fmt.Println("b = ", b) //方法三 (不推荐) 初始化省去数据类型,通过值来自动匹配数据类型 var c = 100 fmt.Println("c = ", c) // 方法四:(最常用的方法),只能用在函数体内 e := 100 fmt.Println("e = ", e) fmt.Printf("type of e = %T", e) } 常量声明 1 2 3 4 5 6 package main import "unsafe" const ( a = "abc" b = len(a) c = unsafe.Sizeof(a) ) func main(){ println(a, b, c) } 可以使用关键字iota在 const() 里,用来进行累加的 ...

August 29, 2024 · 10 min · 2066 words · Similarityoung