我已经用谷歌搜索得够多了。互联网上几乎所有与主题相关的指南都会在展示将令牌存储到 cookie 中的示例以及会话刷新示例后结束。但对我来说,最有趣(也是最困难)的部分是在之后 - 当我想向受令牌保护的路由发出 http 请求时。
在我的项目中发出http请求的背景下,主要代理是:
所需的身份验证流程:
{ cookies } from 'next/headers'
设置仅 http 的“user_token”cookie,并将响应发送回客户端;这是我的问题清单:
a)我注意到,对于使用 NextJS fetch() 函数从 NextJS 服务器端发出的 http 请求,
credentials: "include"
不包含我之前设置的 cookie。所以我想在每次请求之前我都需要使用 cookies().get("user_token")?.value
获取令牌。这是正确的还是有办法让 credentials: "include"
发挥作用?
b)如果(在生产中)NextJS 服务器和后端服务器将在同一域上运行,我的 NextJS 客户端请求会自动包含“user_token”cookie 吗?
c)在开发模式下,我可以做些什么来为 localhost:3000 (nextJS 服务器)和后端域设置“user_token”cookie?或者我应该在每次开发时手动设置它? (目前在开发模式下“user_token”设置为 localhost:3000,因此直接从客户端向后端发出请求时不会发送 cookie)。
备注:
我将包含一些代码清单,但是无需查看它们就可以理解我的问题的上下文。
登录服务器操作(简化代码):
export const loginAction = async (state: unknown, formData: unknown): Promise<LoginFormState> => {
if (!(formData instanceof FormData))
return {
message: "Invalid payload",
ok: false
};
// Validate form fields
const validatedFields = LoginFormSchema.safeParse({
email: formData.get('email'),
password: formData.get('password'),
})
// If any form fields are invalid, return early
if (!validatedFields.success) {
console.log('validation error');
return {
errors: validatedFields.error.flatten().fieldErrors,
} as LoginFormState
}
const loginResponse = await loginPost({
...validatedFields.data
});
if (!classicNextApiResponseValidator(loginResponse)) {
const error = await requestErrorFromNextResponse(loginResponse);
return {
message: error.description,
ok: false
}
}
const responseData: T.LoginResponse = await loginResponse.json();
await storeToCookies("user_token", responseData.accessToken);
if (responseData.emailVerified) {
await redirectAction("/dashboard", {});
} else {
await redirectAction("/emailVerification", {});
}
return {
message: "successfully",
ok: true
}
};
storeToCookies 函数
"use server";
import { cookies } from 'next/headers';
import { CookiesKey } from './types';
export const storeToCookies = async <T = any>(key: CookiesKey, value: T, options: {} = {}) => {
if (typeof key === "string")
cookies().set(
key,
typeof value === "string" ? value : JSON.stringify(value),
{
httpOnly: true,
secure: false,
sameSite: 'lax',
path: '/',
}
);
};
我尝试从nextJS服务器端向受保护的路由(配置文件)发出请求(
credentials: "include"
不起作用,但手动获取cookie并将其作为标头传递是有效的:
export const profileGet = async () => {
return fetch(`${CONFIG.BASE_URL}/profile`, {
method: "GET",
credentials: "include",
headers: DEFAULT_HEADERS,
});
};
我的用于客户端请求的 apisauce 实例:
const sauce = create({
withCredentials: true,
baseURL: CONFIG.BASE_URL,
headers: {
Accept: 'application/json',
}
});
默认情况下,出于安全原因,cookie 不会在服务器端获取调用中传递。
这就是为什么我们应该将标头传递给 fetch 调用。
import { headers } from "next/headers"
//...
//...
//...
async function getProfile() {
const response = fetch(`${CONFIG.BASE_URL}/profile`, {
method: "GET",
headers: headers(),
})
if (!response.ok) return null
return res.json()
}
const PageHome: React.FC<PageHomeProps> = async () => {
const profile = await getProfile()
//...
//...
//...
}
export default PageHome