目录
[JWT 使用场景](#JWT 使用场景)
[JWT 基本概念](#JWT 基本概念)
[1. 生成 JWT](#1. 生成 JWT)
[2. 验证 JWT](#2. 验证 JWT)
[3. 中间件](#3. 中间件)
[后端登录接口生成token 设置为响应头](#后端登录接口生成token 设置为响应头)
JWT 是一种用于安全传输信息的开放标准,它通常用于身份验证和授权。在 Node.js 中,你可以使用库如jsonwebtoken
来创建和验证 JWT。JWT 允许你在服务器和客户端之间安全地传递信息,而无需存储会话状态。这使得 JWT 非常适合构建分布式系统,如单页应用(SPA)、移动应用和微服务。
JWT 使用场景
在什么情况下使用 JWT 是有意义的?以下是一些常见的使用场景:
- 用户身份验证: JWT 可用于验证用户的身份。当用户登录时,服务器可以为其生成一个 JWT,客户端可以在后续请求中发送这个 JWT 来证明其身份。
- 单点登录(SSO): JWT 可以用于实现单点登录,让用户一次登录即可访问多个相关应用,而无需重复登录。
- API 授权: 在 API 调用中,JWT 可以包含授权信息,以便在服务器端验证用户是否有权限执行特定操作。
- 密码重置: JWT 可用于安全地生成包含重置密码令牌的链接,以允许用户重置其密码。
- 移动应用认证: 移动应用可以使用 JWT 来与后端服务器进行身份验证,确保只有经过授权的用户可以访问后端资源。
JWT 基本概念
在 Node.js 中使用 JWT,你需要了解以下基本概念:
- JWT 结构: JWT 由三部分组成,分别是头部(Header)、载荷(Payload)和签名(Signature)。头部包含算法和令牌类型,载荷包含要传递的信息,签名用于验证令牌的真实性。
- 密钥: 生成和验证 JWT 时,你需要一个密钥。密钥可以是对称密钥(使用相同的密钥进行签名和验证)或非对称密钥(使用不同的密钥进行签名和验证)。
- 签名验证: 接收 JWT 的服务器使用密钥验证签名以确保 JWT 未被篡改。如果签名验证成功,服务器可以信任 JWT 中的信息。
常用方法
以下是在 Node.js 中进行 JWT 身份验证和授权的常用方法:
1. 生成 JWT
使用jsonwebtoken
库可以生成 JWT。首先,你需要安装该库:
npm install jsonwebtoken
然后,你可以使用以下代码生成 JWT:
const jwt = require('jsonwebtoken');
const payload = { userId: 123, role: 'admin' };
const secretKey = 'your-secret-key';
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
2. 验证 JWT
服务器收到 JWT 后,需要验证其真实性。使用以下代码验证 JWT:
const jwt = require('jsonwebtoken');
const token = 'your-jwt-token';
const secretKey = 'your-secret-key';
try {
const decoded = jwt.verify(token, secretKey);
console.log(decoded);
} catch (error) {
console.error('JWT verification failed');
}
3. 中间件
在 Express.js 中,你可以创建 JWT 验证中间件来保护特定路由。以下是一个示例:
const jwt = require('jsonwebtoken');
const secretKey = 'your-secret-key';
function authenticateToken(req, res, next) {
const token = req.header('Authorization');
if (!token) return res.status(401).send('Access denied');
try {
const decoded = jwt.verify(token, secretKey);
req.user = decoded;
next();
} catch (error) {
res.status(403).send('Invalid token');
}
}
实践案例
让我们通过一个实际案例来演示如何在 Node.js 中进行 JWT 身份验证和授权。我们将创建一个简单的 Express.js 应用,并使用 JWT 来保护一个受限的路由。
const express = require('express');
const jwt = require('jsonwebtoken');
const secretKey = 'your-secret-key';
const app = express();
app.use(express.json());
// 登录路由,生成JWT
app.post('/login', (req, res) => {
const { username, password } = req.body;
// 在实际应用中,这里会验证用户名和密码
if (username === 'user' && password === 'password') {
const payload = { username };
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
res.json({ token });
} else {
res.status(401).json({ error: 'Authentication failed' });
}
});
// 受保护的路由,需要JWT验证
app.get('/protected', authenticateToken, (req, res) => {
res.json({ message: 'This is a protected route', user: req.user });
});
function authenticateToken(req, res, next) {
const token = req.header('Authorization');
if (!token) return res.status(401).send('Access denied');
try {
const decoded = jwt.verify(token, secretKey);
req.user = decoded;
next();
} catch (error) {
res.status(403).send('Invalid token');
}
}
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
在上面的示例中,我们创建了一个登录路由,生成 JWT 并将其返回给客户端。然后,我们创建了一个受保护的路由,使用authenticateToken
中间件来验证 JWT。如果 JWT 验证成功,用户将能够访问受保护的路由。
项目实战
封装jwt文件
const jsonwebtoken = require("jsonwebtoken");
const secrcet = "贾公子";
const JWT = {
// 生成token
generate(value, exprires) {
// 加密内容 密钥 过期时间
return jsonwebtoken.sign(value, secrcet, { expiresIn: exprires });
},
// token 解密
verify(token) {
try {
return jsonwebtoken.verify(token, secrcet);
} catch (error) {
return false;
}
},
};
module.exports = JWT;
后端登录接口生成token 设置为响应头
login: async (req, res) => {
console.log(req.body);
// req.body
var result = await UserService.login(req.body)
if (result.length == 0) {
res.send({
code: '-1',
error: '用户名密码不匹配'
})
} else {
// 生成token
const token = JWT.generate({
_id: result[0]._id,
username: result[0].username
}, '1d')
res.header('Authoization', token)
res.send({
ActionType: 'ok',
data: {
username: result[0].username,
gender: result[0].gender ? result[0].gender : 0, //性别 0 1 2
introduction: result[0].introduction,//简介
avatar: result[0].avatar,
role: result[0].role,//管理员1 编辑2
}
})
}
},
全局路由中间件验证token
// 如果token有效,next()
// 如果token过期了,返回401
每次访问刷新token 延续token
app.use((req, res, next) => {
// 如果token有效,next()
// 如果token过期了,返回401
if (req.url == '/adminapi/user/login') {
next()
return
}
const token = req.headers['authoization'].split(' ')[1]
if (token) {
let payload = JWT.verify(token)
console.log(payload);
if (payload) {
const newToken = JWT.generate({
_id: payload._id,
username: payload.username
}, '1d')
res.header('Authoization', newToken)
next()
} else {
res.status(401).send({ errCode: '-1', errorInfo: 'token过期' })
}
}
前端拦截处理token
import axios from 'axios'; // 引入axios
axios.interceptors.request.use(Config => {
const token = localStorage.getItem('token')
Config.headers.Authoization = `Beare ${token}`
return Config
})
axios.interceptors.response.use(function (response) {
// console.log(response.headers);
const { authoization } = response.headers
authoization && localStorage.setItem('token', authoization)
return response
}, function (error) {
const { status } = error.response
if (status == 401) {
localStorage.removeItem('token')
window.location.href = '#/login'
return Promise.reject(error)
}
})
提示、技巧和注意事项
- 安全存储密钥: 密钥是 JWT 的关键部分,应安全存储。不要硬编码密钥,最好从环境变量或配置文件中读取。
- 定期刷新令牌: 为 JWT 设置适当的到期时间,以降低安全风险。客户端应定期获取新的 JWT 令牌。
- 不要在 JWT 中存储敏感信息 : 避免在 JWT 中存储敏感信息,因为 JWT 可以被解码。如果需要存储敏感信息,应使用加密而不是签名。