实现 Token 鉴权:保护你的应用程序和 API
在当今互联网应用程序中,安全性是至关重要的。为了保护用户数据并确保只有经过授权的用户可以访问敏感信息,开发人员需要实施有效的身份验证和授权机制。Token 鉴权是一种常见的方法,它通过使用令牌来验证用户并授予对受保护资源的访问权限。
什么是 Token 鉴权?
Token 鉴权是一种基于令牌的身份验证方法,通常被用于 Web 应用程序和 API。它基于用户提供的凭证(通常是用户名和密码)生成一个加密的令牌,并将该令牌返回给客户端。客户端在之后的请求中携带这个令牌,服务器则用于验证用户的身份并授权其访问受保护的资源。
实现 Token 鉴权的步骤
1. 用户登录
用户提供凭据(通常是用户名和密码)进行登录。服务器验证凭据,并在验证通过后生成一个唯一的令牌。
//用户登录处理
app.post('/login', (req, res) => {
// 验证用户提供的凭据
if (credentialsValid(req.body.username, req.body.password)) {
// 生成并返回令牌
const token = generateToken(req.body.username);
res.json({ token });
} else {
res.status(401).json({ error: 'Authentication failed' });
}
});
2. 令牌生成与签名
服务器使用加密算法(如 HMAC 或 RSA)对用户信息进行签名,创建一个令牌,并将其返回给客户端。这个令牌应该包括必要的信息以及一个签名,以防止伪造或篡改。
//生成令牌(包括有效期)
const jwt = require('jsonwebtoken');
function generateToken(username) {
const payload = { username, role: 'user' };
const secretKey = 'your_secret_key'; // 用于签名的密钥
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' }); // 令牌有效期设置为1小时
return token;
}
3. 令牌的存储与传递
客户端通常会将令牌存储在本地,例如在浏览器的 localStorage 中。在随后的请求中,客户端会将令牌作为 Authorization 头或其他方式发送给服务器。
//将令牌存储在本地
localStorage.setItem('token', receivedToken);
// 在请求中发送令牌
fetch('/protected/resource', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
4. 服务端验证与授权
每当客户端发送请求时,服务器会验证令牌的有效性和真实性。如果令牌有效且用户被授权,服务器将允许对请求资源的访问。否则,请求将被拒绝。
//服务端验证与授权(包括令牌有效期检查)
app.use((req, res, next) => {
const token = req.headers.authorization.split(' ')[1]; // 获取请求中的令牌
jwt.verify(token, 'your_secret_key', (err, decoded) => {
if (err) {
return res.status(401).json({ error: 'Unauthorized' });
}
// 检查令牌有效期
if (new Date() > decoded.exp * 1000) {
return res.status(401).json({ error: 'Token expired' });
}
req.decoded = decoded; // 保存解码后的用户信息
以下代码是一个实现 Token 鉴权流程的 Node.js Express 应用程序的详细例子。其中包括了用户登录、令牌生成、服务端验证与授权的中间件以及保护资源的路由示例。
// 导入所需的模块
const express = require('express'); // 引入 Express 框架
const jwt = require('jsonwebtoken'); // 引入 JWT 模块
// 创建 Express 应用程序
const app = express();
app.use(express.json()); // 使用 JSON 解析中间件
// 伪代码 - 用户凭证验证函数
function credentialsValid(username, password) {
// 进行用户凭证验证的逻辑
// 返回 true 或 false
}
// 处理用户登录请求
app.post('/login', (req, res) => {
if (credentialsValid(req.body.username, req.body.password)) {
const token = generateToken(req.body.username); // 生成令牌
res.json({ token }); // 返回令牌给客户端
} else {
res.status(401).json({ error: 'Authentication failed' }); // 登录失败时返回错误消息
}
});
// 生成令牌(包括有效期)
function generateToken(username) {
const payload = { username, role: 'user' }; // 设置载荷信息
const secretKey = 'your_secret_key'; // 用于签名的密钥
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' }); // 生成令牌,有效期设置为1小时
return token; // 返回生成的令牌
}
// 服务端验证与授权(包括令牌有效期检查)中间件
app.use((req, res, next) => {
const token = req.headers.authorization.split(' ')[1]; // 获取请求中的令牌
jwt.verify(token, 'your_secret_key', (err, decoded) => {
if (err) {
return res.status(401).json({ error: 'Unauthorized' }); // 未授权时返回错误消息
}
// 检查令牌有效期
if (new Date() > decoded.exp * 1000) {
return res.status(401).json({ error: 'Token expired' }); // 令牌过期时返回错误消息
}
req.decoded = decoded; // 保存解码后的用户信息至请求对象
next(); // 继续处理下一个中间件或路由
});
});
// 保护资源的路由示例
app.get('/protected/resource', (req, res) => {
res.json({ message: 'You have accessed the protected resource' }); // 返回受保护资源的信息
});
// 启动服务器并监听指定端口
app.listen(3000, () => {
console.log('Server is running on port 3000');
});