1

我正在开发一个带有 Go 后端(go-fiber 框架)和 ReactJS 前端的个人理财应用程序。

我的身份验证方法是在用户登录时将 JWT 作为 cookie 返回。

前端使用 发送登录请求fetch,然后跟进另一个fetch获取用户数据。这些fetch调用以及服务器处理程序函数可以在本问题末尾的附录中找到。

当我对此进行测试时,我会成功登录。返回一个 Set-Cookie 标头,我在响应中看到了我所期望的 cookie。但是,JWT 并未作为标头包含在对用户数据的请求中。处理程序返回{"status": "unauthorized"}解析的 JWT 是nil.

为什么 JWT 未包含在用户数据请求中?

这是 Set-Cookie 标头,以及所有登录响应标头的屏幕截图。 jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDY1MTczOTMsImlzcyI6IjE3In0.SDKnxjsVImuVOHw_hnsPX1ZhtS7-_6s8Cqk79SwniCY; expires=Sat, 05 Mar 2022 21:56:33 GMT; path=/; HttpOnly; SameSite=Lax

登录响应标头

这是登录时返回的 JWT cookie,以及来自 Chrome 开发者工具的 cookie 的屏幕截图。 jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDY1MTczOTMsImlzcyI6IjE3In0.SDKnxjsVImuVOHw_hnsPX1ZhtS7-_6s8Cqk79SwniCY localhost / 2022-03-05T21:56:33.000Z 195 ✓ Lax Medium

登录响应 Cookie

我在“应用程序”选项卡的“Cookies”部分没有看到任何内容。但是,我在其他地方读到,我不应该期望在httpOnly这里看到任何设置为 true 的 cookie。

应用程序 Cookie

我希望在用户数据请求中看到一个名为“Cookies”的标头。但我只看到这些:

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: localhost:9000
Origin: http://localhost:3000
Referer: http://localhost:3000/
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="99", "Google Chrome";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36

用户数据请求标头

非常感谢任何轻推或帮助。以下是前端和后端代码的 GitHub 页面链接,以帮助解释问题:

ReactJS 前端

Go-Fiber 后端

附录

登录请求fetch

        fetch('http://localhost:9000/signIn', requestOptions)
            .then(res => {
                if (res.status === 200) {
                    res.json()
                        .then(
                            (result) => {
                                if (result.status && result.status === "success") {
                                    this.props.onRouteChange('home');
                                } else {
                                    // TO DO: display failure message on UI
                                    console.log('Failed to sign in');
                                }
                            },
                            (error) => {
                                this.setState({
                                    isLoaded: true,
                                    error
                                });
                            }
                        );
                } else {
                    console.log('Error signing in');
                    res.json()
                        .then(
                            (result) => {
                                console.log(result);
                            },
                            (error) => {
                                console.log('Error reading JSON of response with status !== 200');
                                console.log(error);
                            }
                        );
                }
            });

登录处理函数:

func handleSignIn(c *fiber.Ctx) error {
    // unmarshal received sign in data into User struct
    var signIn User
    if err := c.BodyParser(&signIn); err != nil {
        err = fmt.Errorf("failed to process HTTP request body to /signIn: %w", err)
        log.Println("Error:", err)
        c.Status(fiber.StatusBadRequest)
        return err
    }

    // look for the identified user in the users database
    usr, err := getUserByUsername(signIn.Username)
    if err != nil && err == sql.ErrNoRows {
        log.Println("Error: user", signIn.Username, "attempted to sign in but not found in users database")
        c.Status(fiber.StatusBadRequest)
        return fmt.Errorf("invalid username/password combination")
    }
    if err != nil {
        err = fmt.Errorf("failed to query database for user %s: %w", signIn.Username, err)
        log.Println("Error:", err)
        c.Status(fiber.StatusInternalServerError)
        return err
    }

    // hash the given password for comparison with the recorded password
    err = bcrypt.CompareHashAndPassword([]byte(usr.Password), []byte(signIn.Password))
    if err != nil {
        log.Println("CompareHashAndPassword returned error during sign in attempt:", err)
        c.Status(fiber.StatusBadRequest)
        return fmt.Errorf("invalid username/password combination")
    }

    // declare claims for the JWT that will be sent back
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
        Issuer:    strconv.Itoa(int(usr.Id)),
        ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), // 1 day
    })
    if token == nil {
        err = fmt.Errorf("failed to instantiate JWT")
        log.Println("Error:", err)
        c.Status(fiber.StatusInternalServerError)
        return err
    }

    // encrypt the JWT with the private key
    tokenString, err := token.SignedString([]byte(jwtPrivateKey))
    if err != nil {
        err = fmt.Errorf("failed to encrypt JWT: %w", err)
        log.Println("Error:", err)
        c.Status(fiber.StatusInternalServerError)
        return err
    }

    c.Cookie(&fiber.Cookie{
        Name:     "jwt",
        Value:    tokenString,
        Expires:  time.Now().Add(time.Hour * 24),
        HTTPOnly: true,
    })

    // send response
    return c.JSON(fiber.Map{
        "status": "success",
    })
}

用户数据请求获取:

    componentDidMount() {
        fetch("http://localhost:9000/getExpenses")
            .then(res => res.json())
            .then(
                (result) => {
                    if (result.status !== null && result.status === "unauthorized") {
                        console.log('Failed authorization when requesting expenses!');
                    } else if (result.expenses === null) {
                        console.log('Response did not contain expenses map');
                    } else {
                        this.setState({
                            isLoaded: true,
                            expenses: result.expenses
                        });
                    }
                },
                (error) => {
                    this.setState({
                        isLoaded: true,
                        error
                    });
                }
            );
    }

用户数据请求处理程序:

func handleGetExpenses(c *fiber.Ctx) error {
    // parse JWT from HTTP cookie
    token, err := parseCookie(c)
    if err != nil {
        c.Status(fiber.StatusUnauthorized)
        return c.JSON(fiber.Map{
            "status": "unauthorized",
        })
    }

    // check which user is getting their expenses
    claims := token.Claims.(*jwt.StandardClaims)
    userId, err := strconv.ParseInt(claims.Issuer, 10, 64)
    if err != nil {
        err = fmt.Errorf("invalid Issuer field in JWT")
        log.Println("Error:", err)
        c.Status(fiber.StatusUnauthorized)
        return err
    }

    // get all expenses from the database
    expenses, err := getAllExpenses(userId)
    if err != nil {
        err = fmt.Errorf("failed to get expenses from expense table: %w", err)
        log.Println("Error:", err)
        c.Status(fiber.StatusInternalServerError)
        return err
    }

    // send response
    return c.JSON(fiber.Map{
        "expenses": expenses,
    })
}
4

0 回答 0