我是Next.js的新手,我正在使用jwt令牌与身份验证系统进行斗争。我想知道使用身份验证系统存储jwt令牌和路由的最佳/标准方法是什么。我一直在尝试不同的方法,从不同的教程/文章,但不太明白。这是我尝试过的。
当用户登录时,它会将用户名/密码发送到分离的 api服务器(例如处理后端内容的新项目),服务器将使用access-token
进行响应,然后在Next.js项目中,我使用收到的令牌设置cookie。在Next.js项目中,受保护的路由将使用withAuth
hoc包装,它将检查cookie中的令牌。这种方法的问题是它容易受到XSS的攻击,因为cookie没有httpOnly标志。
这类似于1.)但是使用localStorage
,问题是access-token
无法在第一次请求时发送到服务器。 (这个我不确定,但根据我的理解,在每个http请求中,我必须手动粘贴access-token
,那么我无法控制的请求呢?例如,首先请求或使用{{1} }标签)。
我在Next.js服务器(自定义快速服务器)中编写了身份验证后端。用户登录时,服务器将验证它,然后设置 httpOnly cookie。然后问题是,使用客户端路由(使用Next.js路由器转到url),它无法检查令牌。例如,如果页面用<a>
hoc包装,但它无法使用javascript访问cookie中的令牌。
我见过很多人,在withAuth
受保护的路由中,他们只检查cookie / localStorage中的存在令牌,然后如果令牌被撤销或列入黑名单,他们如何处理它因为他们没有将令牌发送到服务器?或者我是否必须在更改每个客户端页面时将令牌发送到服务器?
答案 0 :(得分:11)
由于我们正在隔离,所以我有足够的时间来回答这个问题。这将是一个很长的答案。
Next.js使用App组件初始化页面。 _app页面负责呈现我们的页面。我们在_app.js上对用户进行身份验证,因为我们从getInitialProps返回的任何内容都可以被所有其他页面访问。我们在这里对用户进行身份验证,身份验证决定将传递到页面,从页面到页眉,因此每个页面都可以确定用户是否已通过身份验证。 (请注意,可以使用redux来完成,而无需进行支撑钻孔,但这会使答案更加复杂)
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {};
const user = process.browser
? await auth0.clientAuth()
: await auth0.serverAuth(ctx.req); // I explain down below
//this will be sent to all the components
const auth = { user, isAuthenticated: !!user };
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps, auth };
}
render() {
const { Component, pageProps, auth } = this.props;
return <Component {...pageProps} auth={auth} />;
}
}
如果我们在浏览器上并且需要检查用户是否已通过身份验证,我们只需从浏览器中检索cookie,这很容易。但是我们总是必须验证令牌。这与浏览器和服务器使用的过程相同。我将在下面解释。但是,如果我们在服务器上。我们无权访问浏览器中的cookie。但是我们可以从“ req”对象中读取信息,因为cookie附加到 req.header.cookie。。这就是我们访问服务器上cookie的方式。
async serverAuth(req) {
// console.log(req.headers.cookie) to check
if (req.headers.cookie) {
const token = getCookieFromReq(req, "jwt");
const verifiedToken = await this.verifyToken(token);
return verifiedToken;
}
return undefined;
}
这是getCookieFromReq()。记住我们必须考虑功能性。
const getCookieFromReq = (req, cookieKey) => {
const cookie = req.headers.cookie
.split(";")
.find((c) => c.trim().startsWith(`${cookieKey}=`));
if (!cookie) return undefined;
return cookie.split("=")[1];
};
一旦获得cookie,我们必须对其进行解码,提取到期时间以查看其是否有效。这部分很容易。我们必须检查的另一件事是jwt的签名是否有效。对称或非对称算法用于对jwt进行签名。您必须使用私钥来验证对称算法的签名。 RS256是API的默认非对称算法。使用RS256的服务器为您提供了一个链接,以使jwt使用密钥来验证签名。您可以使用[jwks-rsa] [1],也可以自行执行。您必须创建一个证书,然后验证令牌是否有效。
假设我们的用户现在已通过身份验证。您说:“而且我看到很多人在受保护路线的getInitialProps中只检查cookie / localStorage中的存在令牌。”我们使用受保护的路由仅将访问权限授予授权用户。为了访问这些路由,用户必须显示其jwt令牌,express.js使用中间件检查用户的令牌是否有效。由于您已经看到了很多示例,因此我将跳过这一部分。
”然后,如果令牌被吊销或列入黑名单,又怎么办呢,因为它们没有将令牌发送到服务器?或者我必须在每次更改客户端页面时都将令牌发送到服务器? “
通过验证令牌过程,我们100%确定令牌是否有效。当客户端要求服务器访问某些机密数据时,客户端必须将令牌发送到服务器。想象一下,当您安装组件时,组件会要求服务器从受保护的路由中获取一些数据。服务器将提取 req 对象,获取jwt并将其用于从受保护的路由中获取数据。为浏览器和服务器获取数据的实现方式不同。如果浏览器发出请求,则只需要相对路径,而服务器则需要绝对路径。如您所知,获取数据是通过组件的getInitialProps()完成的,并且此函数在客户端和服务器上均执行。这是您应如何实施。我只是附加了getInitialProps()部分。
MyComponent.getInitialProps = async (ctx) => {
const another = await getSecretData(ctx.req);
//reuslt of fetching data is passed to component as props
return { superValue: another };
};
const getCookieFromReq = (req, cookieKey) => {
const cookie = req.headers.cookie
.split(";")
.find((c) => c.trim().startsWith(`${cookieKey}=`));
if (!cookie) return undefined;
return cookie.split("=")[1];
};
const setAuthHeader = (req) => {
const token = req ? getCookieFromReq(req, "jwt") : Cookies.getJSON("jwt");
if (token) {
return {
headers: { authorization: `Bearer ${token}` },
};
}
return undefined;
};
export const getSecretData = async (req) => {
const url = req ? "http://localhost:3000/api/v1/secret" : "/api/v1/secret";
return await axios.get(url, setAuthHeader(req)).then((res) => res.data);
};
[1]: https://www.npmjs.com/package/jwks-rsa
答案 1 :(得分:1)
随着Next.JS v8的引入,在NextJS example page中也放置了一些示例。要遵循的基本思想是:
JWT
OAuth