gqlgen 从解析器设置 cookie

Mam*_*mzi 4 go go-gin gqlgen

我正在使用杜松子酒和 gqlgen。我需要从解析器设置 cookie,但我的解析器中只有上下文和来自 graphQL 的输入。这个问题已经在github中得到了解答。ctx.Writer.Write但这一个不同,因为当你尝试传递时我无法改变,也什么也做不了ctx.Next。因为gin不是那样工作的。

func (r *mutationResolver) Login(ctx context.Context, email string, password string) (bool, error) {
        // You need ctx.Writer to set a cookie and can't access that from here
}
Run Code Online (Sandbox Code Playgroud)

我已经解决了这个问题,我想在下面回答我自己的问题。

Mam*_*mzi 7

解释

服务器端 cookie 的工作原理:

服务器端 cookie 由服务器嵌入到响应标头中,向浏览器发出设置 cookie 的意图。该机制的目的是能够直接在解析器内使用ctx.Writer,从而绕过中间件干预的需要。

在中间件中,为您提供了一个ctx包含 的上下文 () ctx.Request.Context()。您还可以访问ctx.Writer,这是修改标头以设置 cookie 所必需的。要ctx.Writer在您的解析器中可用,您应该将其嵌入到ctx.Request.Context(). 这种方法很重要,因为只有 被ctx.Request传递到解析器,而不是整个上下文 ( ctx)。

代码


该结构将用于在中间件中创建并将其指针传递给解析器,在所有路由中您将知道用户是否经过身份验证,并且还能够使用http.ResponseWriter

结构体

type CookieAccess struct {
    Writer     http.ResponseWriter
    IsLoggedIn bool
    UserId     uint64
}

Run Code Online (Sandbox Code Playgroud)

中间件

func setValInCtx(ctx *gin.Context, val interface{}) {
    newCtx := context.WithValue(ctx.Request.Context(), "ctxCookieAccessKey", val)
    ctx.Request = ctx.Request.WithContext(newCtx)
}

func Middleware() gin.HandlerFunc {
    return func(ctx *gin.Context) {
        cookieA := CookieAccess{
            Writer: ctx.Writer,
            IsLoggedIn: false, // Assume not logged in by default
            UserId: 0,
        }

        setValInCtx(ctx, &cookieA)

        c, err := ctx.Request.Cookie("tokenKey")
        if err != nil {
            // If there's an error fetching the cookie, log it and proceed
            log.Printf("Error fetching 'tokenKey' cookie: %v", err)
            ctx.Next()
            return
        }

        // Proceed with token parsing only if the cookie is successfully fetched
        rawToken := c.Value
        userId, err := ParseToken(rawToken)
        if err != nil {
            // If there's an error parsing the token, log it and ensure user is not logged in
            log.Printf("Error parsing token: %v", err)
        } else {
            // If token is successfully parsed, mark user as logged in and set UserId
            cookieA.IsLoggedIn = true
            cookieA.UserId = userId
        }

        ctx.Next()
    }
}

Run Code Online (Sandbox Code Playgroud)

旋转变压器


func (r *MutationResolver) logginOrConsumerResolver(ctx context.Context, input Credentials) (*Response, error) {
    // ctx.Request.Context() from middleware is here
    ca := ctx.Value("ctxCookieAccessKey").(*CookieAccess)
    if ca.IsLoggedIn {
        // this is the way to get token proceed result in other resolvers
        return nil, fmt.Errorf("you are already have a valid token, userId: %v", ca.userId)
    }

    http.SetCookie(ca.Writer, &http.Cookie{
        Name:     "tokenKey",
        Value:    token, // make your token
        HttpOnly: true,
        Path:     "/",
        Expires:  time.Now().Add(24 * time.Hour),
    })

    return nil, nil
}

Run Code Online (Sandbox Code Playgroud)