身份验证是web应用程序开发中的关键问题之一。在传统的基于session的验证中,每个用户在访问应用程序时都会为他们创建一个新的session。但是使用JWT(JSON Web Token)可以大大简化身份验证过程并提高应用程序的安全性。
这篇文章将介绍如何在Express.js中使用JWT进行身份验证。我们将详细讲解JWT、在Express中如何使用JWT、JWT如何工作以及如何保护JWT免受攻击。
什么是 JSON Web Token (JWT)?
JSON Web Token(JWT)是一种开放标准(RFC 7519),定义了一种紧凑且自包含的方式,在各方之间作为JSON对象共享信息。它可以用于身份验证和授权。
JWT包含三个部分:头部、载荷和签名。头部包含指定算法和令牌类型(例如,JWT)的信息。载荷包含应用程序需要使用的有关用户的信息。这些信息是像名称、电子邮件地址和角色等信息。签名用于验证令牌的完整性。
JWT的优点包括:
JWT是自包含的,因此可以完全按照开发人员的意愿定制它。
JWT包含有关用户的信息(称为载荷)。大多数情况下,您不需要查询数据库来验证用户的身份。
由于JWT是独立于服务器的,因此您可以跨多个域框架使用JWT。
现在我们已经了解了JWT的基本知识,让我们进入如何在Express.js中使用它。
在 Express.js 中使用 JWT 进行身份验证
我们将按步骤演示如何在Express.js中使用JWT进行身份验证。
安装依赖项
首先,您需要安装下面两个依赖项:
npm install jsonwebtoken npm install express-jwt
第一个依赖项将帮助您创建和验证JWT,而第二个依赖项将验证JWT并将载荷附加到请求对象上。
创建 JWT
让我们从使用JSON Web Token(JWT)为认证创建令牌开始。
以下代码显示如何在Node.js中创建一个JWT:
const jwt = require('jsonwebtoken'); const createToken = (user) => { const accessToken = jwt.sign({ username: user.username, email: user.email }, 'your-secret-key', { expiresIn: '30m' }); return accessToken; };
接下来,继续使用Express.js:
// javascriptcn.com 代码示例 const express = require('express'); const app = express(); const port = 3000; app.use(express.json()); app.get('/', (req, res) => { res.send('Hello World!'); }); app.post('/login', (req, res) => { const user = req.body; // 这里应该验证用户 const accessToken = createToken(user); res.json({ accessToken }); }); app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`); });
该代码从JSON Web Token(JWT)生成身份验证令牌(access token)。在这种情况下,我们使用了createToken
函数生成令牌。但是,您可以使用任何其他标准库(例如,Passport.js)来创建和验证JWT。
访问受保护的路由
现在我们已经生成了JWT,请考虑如何保护Express.js应用程序中的路由以确保只有经过身份验证的用户才能访问它们。
下面是如何在路由中检查令牌并启用访问控制:
// javascriptcn.com 代码示例 const express = require('express'); const jwt = require('jsonwebtoken'); const expressJwt = require('express-jwt'); const app = express(); const port = 3000; const secret = 'your-secret-key'; app.use(express.json()); app.get('/', (req, res) => { res.send('Hello World!'); }); app.post('/login', (req, res) => { const user = req.body; // 这里应该验证用户 const accessToken = jwt.sign({ username: user.username, email: user.email }, secret, { expiresIn: '30m' }); res.json({ accessToken }); }); // 定义需要验证Token的API app.get('/api/posts', expressJwt({secret: secret}), (req, res) => { const posts = [ { id: 1, title: 'Post 1' }, { id: 2, title: 'Post 2' } ]; res.json(posts); }); app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`); });
在这个示例中,我们使用React.js实现了一个客户端应用程序。该应用程序发送POST请求来进行身份验证和接收access token。如果用户成功登录,则我们将为用户提供一个JWT。
JWT 保护
让我们讨论安全性方面的一些事情以及恶意用户可能会如何访问我们的API。
避免 XSS 攻击
在使用JWT时,请注意避免发生跨站脚本漏洞(XSS)。要做到这一点,请将HTML转义并在输出到浏览器之前从HTML标记中删除输入。
避免 CSRF 攻击
您可以通过以下方式通过CSRF(跨站请求伪造)保护您的应用程序:
- 使用cookie作为JWT
- 将JWT作为请求头
使用Cookie作为JWT时,此方法会为Cookie设置'same-site'功能。
以下是一些Express.js的代码片段,可用于设置Cookie,并将它们发送给客户端:
// javascriptcn.com 代码示例 const jwt = require('jsonwebtoken'); app.set('trust proxy', 1); app.use(cors({ origin: ['http://localhost:3000'], credentials: true })); app.get('/set-cookie', (req, res) => { const token = jwt.sign({ user: 'mark' }, 'my-secret', { expiresIn: '60s' }); res.cookie('access_token', token, { maxAge: 60 * 1000, httpOnly: true, secure: true, sameSite: 'none' // set this to none when sending the JWT from React }); res.json({ success: true }); });
在上面的代码中,我们使用jsonwebtoken
库创建了JWT(access_token)。我们还将Cookie设置为:
- maxAge:设置Cookie有效期(以毫秒为单位)
- httpOnly:确保Cookie不是通过脚本访问的。
- secure:仅在安全连接上发送Cookie
- sameSite:设置此选项,以确保只有在客户端访问此应用程序时才会将cookie发送到站点。
以下是获取Cookie的示例代码,以及如何将Cookie作为JWT发送到请求头:
// javascriptcn.com 代码示例 app.get('/api/books', (req, res) => { const token = req.cookies.access_token; if (!token) { res.status(401).json({ error: 'Unauthorized' }); } else { try { const decoded = jwt.verify(token, 'my-secret'); // do something with the response res.json(data); } catch (error) { console.error(error); res.status(500).json({ error: 'Internal server error' }); } } });
总结
本文中,我们讨论了如何在Node.js应用程序中实施JWT身份验证。JWT是一种简单有效的身份验证方法,并可用于各种不同的语言和框架中。
在下一篇文章中,我们将深入了解更多安全方面的事情以及如何防止一些基本安全漏洞。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/652cac737d4982a6ebe4d773