为什么我的 cookie 没有被发送? ReactJS front-end、Go back-end
Why is my cookie not being sent? ReactJS front-end, Go back-end
我正在使用 Go back-end(go-fiber 框架)和 ReactJS front-end.
开发个人理财应用程序
我的身份验证方法是 return 在用户登录时将 JWT 作为 cookie。
前端使用fetch
发送了一个sign-in请求,然后又跟进了另一个fetch
获取用户数据。 fetch
调用以及服务器处理函数可以在本问题末尾的附录中找到。
当我对此进行测试时,我获得了成功 sign-in。 Set-Cookie header 是 returned 并且我在响应中看到了 cookie,正如我所期望的那样。但是,JWT 未作为 header 包含在用户数据请求中。作为已解析 JWT 的处理程序 returns {"status": "unauthorized"}
是 nil
.
为什么 JWT 没有包含在用户数据请求中?
这是 Set-Cookie header,以及所有 Sign-In 响应 header 的屏幕截图。
jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDY1MTczOTMsImlzcyI6IjE3In0.SDKnxjsVImuVOHw_hnsPX1ZhtS7-_6s8Cqk79SwniCY; expires=Sat, 05 Mar 2022 21:56:33 GMT; path=/; HttpOnly; SameSite=Lax
Sign-In Response Headers
这是 return 在 sign-in 上编辑的 JWT cookie,以及来自 Chrome 开发人员工具的 cookie 的屏幕截图。
jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDY1MTczOTMsImlzcyI6IjE3In0.SDKnxjsVImuVOHw_hnsPX1ZhtS7-_6s8Cqk79SwniCY localhost / 2022-03-05T21:56:33.000Z 195 ✓ Lax Medium
Sign-In Response Cookie
我在“应用程序”选项卡的“Cookies”部分没有看到任何内容。但是,我在其他地方读到,我不应该期望在此处看到 httpOnly
设置为 true 的任何 cookie。
Application Cookies
我期待在用户数据请求中看到一个名为“Cookies”的 header。但我只看到这些:
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
User Data Request Headers
非常感谢任何提示或帮助。以下是 front-end 和 back-end 代码的 GitHub 页面的链接,以防它有助于解释问题:
附录
Sign-In 请求 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);
}
);
}
});
Sign-In 处理函数:
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,
})
}
默认情况下,fetch
不使用 cookie。您可以让 fetch
使用这样的 cookie:
fetch(url, {
credentials: "same-origin",
}).then(responseHandler).catch(errorHandler);
您可以查看文档了解更多详情:
https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters
我正在使用 Go back-end(go-fiber 框架)和 ReactJS front-end.
开发个人理财应用程序我的身份验证方法是 return 在用户登录时将 JWT 作为 cookie。
前端使用fetch
发送了一个sign-in请求,然后又跟进了另一个fetch
获取用户数据。 fetch
调用以及服务器处理函数可以在本问题末尾的附录中找到。
当我对此进行测试时,我获得了成功 sign-in。 Set-Cookie header 是 returned 并且我在响应中看到了 cookie,正如我所期望的那样。但是,JWT 未作为 header 包含在用户数据请求中。作为已解析 JWT 的处理程序 returns {"status": "unauthorized"}
是 nil
.
为什么 JWT 没有包含在用户数据请求中?
这是 Set-Cookie header,以及所有 Sign-In 响应 header 的屏幕截图。
jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDY1MTczOTMsImlzcyI6IjE3In0.SDKnxjsVImuVOHw_hnsPX1ZhtS7-_6s8Cqk79SwniCY; expires=Sat, 05 Mar 2022 21:56:33 GMT; path=/; HttpOnly; SameSite=Lax
Sign-In Response Headers
这是 return 在 sign-in 上编辑的 JWT cookie,以及来自 Chrome 开发人员工具的 cookie 的屏幕截图。
jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDY1MTczOTMsImlzcyI6IjE3In0.SDKnxjsVImuVOHw_hnsPX1ZhtS7-_6s8Cqk79SwniCY localhost / 2022-03-05T21:56:33.000Z 195 ✓ Lax Medium
Sign-In Response Cookie
我在“应用程序”选项卡的“Cookies”部分没有看到任何内容。但是,我在其他地方读到,我不应该期望在此处看到 httpOnly
设置为 true 的任何 cookie。
Application Cookies
我期待在用户数据请求中看到一个名为“Cookies”的 header。但我只看到这些:
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
User Data Request Headers
非常感谢任何提示或帮助。以下是 front-end 和 back-end 代码的 GitHub 页面的链接,以防它有助于解释问题:
附录
Sign-In 请求 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);
}
);
}
});
Sign-In 处理函数:
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,
})
}
默认情况下,fetch
不使用 cookie。您可以让 fetch
使用这样的 cookie:
fetch(url, {
credentials: "same-origin",
}).then(responseHandler).catch(errorHandler);
您可以查看文档了解更多详情: https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters