如何在Go中使用context实现请求授权

使用context包可以在您的应用程序中构建请求链。context.Context包含请求的截止时间、取消信号和请求的元数据。请求处理程序应该向其所使用的任何函数或方法传递上下文,以便这些函数或方法可以访问请求的元数据。在本文中,我们将探讨如何在Go中使用context实现请求授权。

什么是上下文?

您可以将上下文视为请求级别的存储库,可以在整个请求处理过程中存储请求范围的值。这些值可以是键/值对,其中键是字符串,值可以是任何类型。在处理请求期间,任何组件都可以检索上下文中的值。

使用context传递元数据

为了使用上下文,请从context包导入Context类型。在此示例中,我们创建一个新的上下文并将元数据key1设置为“value1”:

import (

"context"

"fmt"

)

func main() {

ctx := context.WithValue(context.Background(), "key1", "value1")

value := ctx.Value("key1")

fmt.Println(value) // value1

}

在这个示例中,我们将上下文与值“value1”一起设置,并将上下文存储在ctx变量中。然后我们可以通过使用ctx.Value("key1")来检索值。

使用context传递截止时间

除了元数据之外,上下文还可以用于存储请求的截止时间。例如,在以下示例中,我们创建一个由3个协程组成的链,每个协程都打印自己的名称,并在1秒后传递请求:

import (

"context"

"fmt"

"time"

)

func doTask(ctx context.Context, name string) {

select {

case <-ctx.Done():

fmt.Printf("%s was canceled\n", name)

return

case <-time.After(time.Second):

fmt.Printf("%s is done\n", name)

}

}

func main() {

ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)

defer cancel()

go doTask(ctx, "Task1")

go doTask(ctx, "Task2")

go doTask(ctx, "Task3")

time.Sleep(time.Second * 3)

}

在此示例中,我们使用WithTimeout方法创建了一个新的上下文。这个上下文将在2秒钟后过期,如果在这个时候没有调用cancel函数,它将取消所有正在进行的任务并终止应用程序。例如,如果我们运行这个程序,我们可以看到以下结果:

Task1 is done

Task2 was canceled

Task3 was canceled

在这个示例中,由于第一个任务完成得比其余的任务早,所以只有它打印出“is done”。另外两个任务被取消了,因为它们没有在截止时间之前完成。

授权请求

现在我们已经了解了上下文的工作方式,我们可以开始使用它来授权请求。在我们的示例中,我们将模拟一个需要授权的请求。为了保持简单,我们将模拟两个用户,“alice”和“bob”,他们需要执行某些任务。我们想要授予“alice”用户访问这些任务的权限,而“bob”用户则没有任何权限。

创建中间件

在本例中,我们将使用中间件来授权请求。中间件是一个函数,它接受http.Handler并返回一个新的http.Handler。它使用http.Handler来处理请求,并在处理请求之前或之后添加某些行为。

我们将创建一个authorizeMiddleware函数,它将接受一个上下文和一个http.Handler。它将检查上下文中的元数据以确定是否授权请求。如果请求被授权,它将调用http.Handler。否则,它将向用户返回401 Unauthorized响应。

func authorizeMiddleware(ctx context.Context, next http.Handler) http.Handler {

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

userValue := ctx.Value("user")

if userValue == nil {

w.WriteHeader(http.StatusUnauthorized)

return

}

user := userValue.(string)

if user != "alice" {

w.WriteHeader(http.StatusUnauthorized)

return

}

next.ServeHTTP(w, r)

})

}

在这个函数中,我们检查上下文中的"user"元数据。如果user是“alice”,我们调用next.ServeHTTP(),这应该是我们的请求处理程序。否则,我们返回401 Unauthorized响应。

创建请求处理程序

在本例中,我们将使用http.HandlerFunc创建请求处理程序。它将简单地响应请求,并在响应中包含“Hello, world!”消息。

func helloHandler(w http.ResponseWriter, r *http.Request) {

w.Write([]byte("Hello, world!"))

}

将中间件添加到处理程序链中

我们已经创建了授权中间件和请求处理程序,现在我们需要将它们放在一起。我们将使用http.NewServeMux()创建一个新的ServeMux,然后使用http.Handle()和http.HandlerFunc()将处理程序函数添加到它中。

func main() {

mux := http.NewServeMux()

mux.Handle("/", authorizeMiddleware(context.WithValue(context.Background(), "user", "alice"), http.HandlerFunc(helloHandler)))

http.ListenAndServe(":8000", mux)

}

在这个示例中,我们使用context.WithValue()创建了一个具有"user"元数据的上下文。我们将这个上下文传递给authorizeMiddleware()函数,它返回一个新的http.Handler。我们使用http.HandlerFunc()将helloHandler()插入中间件链中。

测试代码

为了测试我们的代码,我们将使用curl发送HTTP GET请求:

$ curl http://localhost:8000/

Hello, world!

在这个示例中,请求被授权,因为上下文中的"user"元数据包含值“alice”。helloHandler()函数向客户端发送“Hello, world!”消息。

如果我们使用curl发送HTTP GET请求,并指定不正确的用户:

$ curl -u bob http://localhost:8000/

Unauthorized

在这个示例中,请求未被授权,因为上下文中的"user"元数据包含值“bob”,而不是“alice”。authorizeMiddleware()函数向客户端返回401 Unauthorized响应。

结论

在本文中,我们探讨了如何在Go中使用context实现请求授权。我们通过使用上下文中的元数据存储从而授权请求。我们还创建了一个中间件,它检查请求是否包含正确的用户数据。授权请求可以在各种场景下使用,可以保护您的应用程序免受恶意访问。

后端开发标签