在现代Web开发中,用户认证是一个至关重要的环节。随着前后端分离架构的流行,传统的Session认证方式逐渐暴露出了一些局限性。而JWT(JSON Web Token)作为一种无状态的认证机制,凭借其简洁、安全和高效的特性,在现代Web应用中得到了广泛应用。本文将详细介绍Session与Token认证的对比、JWT的原理与生成/验证流程、使用bcrypt加密用户密码,并通过实战展示如何在Node.js中实现登录鉴权中间件。
1. Session与Token认证对比
1.1 Session认证
Session认证是一种传统的认证方式,其基本原理是在用户登录成功后,服务器会在内存中(或数据库、缓存中)存储一份用户的登录信息(Session),然后将Session ID通过Cookie返回给客户端。每次客户端请求时,都会携带这个Session ID,服务器通过Session ID查找对应的Session数据,从而验证用户身份。
优点
- 简单易用,适合初学者。
- 安全性相对较高,Session信息存储在服务器端,不易被窃取。
缺点
- 服务器需要存储Session数据,占用服务器资源。
- 在分布式系统中,需要共享Session数据,增加了系统复杂性。
- Session依赖于Cookie,对于非浏览器客户端(如手机APP)不适用。
1.2 Token认证
Token认证是一种无状态的认证方式,其核心思想是在用户登录成功后,服务器生成一个Token(通常是JWT)并返回给客户端。客户端在每次请求时,都会携带这个Token,服务器通过解析和验证Token来验证用户身份。
优点
- 无状态,服务器无需存储Token数据,减轻了服务器负担。
- 适合分布式系统,无需共享状态。
- 跨域支持好,Token可以通过HTTP头部发送。
缺点
- Token需要存储在客户端,存在被窃取的风险。
- 实现相对复杂,需要一定的技术知识。
2. JWT原理与生成/验证流程
2.1 JWT原理
JWT(JSON Web Token)是一种用于双方之间安全传输信息的简洁的、URL安全的令牌标准。JWT由三部分组成:Header(头部)、Payload(负载)和Signature(签名)。
- Header:包含令牌的类型(如JWT)和签名算法(如HS256)。
- Payload:包含需要传递的信息,如用户ID、用户名、角色等。注意,Payload部分的数据是未加密的,因此不适合存储敏感数据。
- Signature:通过对Header和Payload进行Base64编码并使用密钥加密生成的字符串,用于验证令牌的完整性和真实性。
2.2 JWT生成/验证流程
生成JWT
创建Header:指定令牌类型和签名算法。
{
"alg": "HS256",
"typ": "JWT"
}
创建Payload:包含用户信息和过期时间等。
{
"sub": "1234567890",
"name": "John Doe",
"exp": 1625347200
}
- 编码Header和Payload:将Header和Payload进行Base64编码。
- 生成Signature:使用密钥对编码后的Header和Payload进行签名。
- 拼接JWT:将编码后的Header、Payload和Signature用“.”拼接成完整的JWT。
验证JWT
- 解析JWT:将JWT用“.”分割成Header、Payload和Signature三部分。
- 验证Signature:使用相同的密钥对编码后的Header和Payload进行签名,并与JWT中的Signature进行比较。
- 检查Payload:验证Payload中的信息(如过期时间)是否有效。
3. 使用bcrypt加密用户密码
bcrypt是一种基于Blowfish算法的密码哈希函数,专为抵御暴力破解攻击设计。它使用随机生成的字符串和可调节的工作因子来确保密码的安全性。
安装bcrypt
首先,你需要在Node.js项目中安装bcrypt库:
npm install bcrypt
bcrypt加密密码
const bcrypt = require('bcrypt');
async function hashPassword(password) {
// 生成随机数据串
const salt = await bcrypt.genSalt(10);
// 加密密码
const hashedPassword = await bcrypt.hash(password, salt);
return hashedPassword;
}
// 使用示例
hashPassword('my_password').then(hashedPassword => {
console.log('Hashed Password:', hashedPassword);
});
验证密码
const bcrypt = require('bcrypt');
async function comparePasswords(inputPassword, hashedPassword) {
const isMatch = await bcrypt.compare(inputPassword, hashedPassword);
return isMatch;
}
// 使用示例
comparePasswords('my_password', hashedPassword).then(isMatch => {
console.log('Password Match:', isMatch);
});
4. 实战:实现登录鉴权中间件
4.1 安装必要的依赖
首先,你需要在Node.js项目中安装express和jsonwebtoken库:
npm install express jsonwebtoken
4.2 配置JWT密钥
在项目的配置文件中(如config.js)设置JWT密钥:
module.exports = {
jwtSecret: 'your_jwt_secret_key'
};
4.3 实现登录接口
const express = require('express');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const config = require('./config');
const app = express();
// 模拟用户数据库
const users = [];
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user) {
return res.status(401).send('用户或则密码错误');
}
const isMatch = await bcrypt.compare(password, user.hashedPassword);
if (!isMatch) {
return res.status(401).send('用户或则密码错误');
}
const token = jwt.sign({ username: user.username }, config.jwtSecret, { expiresIn: '1h' });
res.json({ token });
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
4.4 实现JWT鉴权中间件
const jwt = require('jsonwebtoken');
const config = require('./config');
function authenticateJWT(req, res, next) {
const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1];
if (!token) {
return res.status(403).send('认证的token数据必须传递');
}
jwt.verify(token, config.jwtSecret, (err, decoded) => {
if (err) {
return res.status(401).send('token数据错误');
}
req.user = decoded;
next();
});
}
// 使用中间件
app.use('/protected', authenticateJWT, (req, res) => {
res.send('调用此接口的都是需要认证的接口');
});
4.5 测试登录和鉴权
登录接口测试:
curl -X POST http://localhost:3000/login -H "Content-Type: application/json" -d '{"username":"testuser","password":"testpassword"}'
响应示例:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3R1c2VyIiwiaWF0IjoxNjI1MzQ3MjAwLCJleHAiOjE2MjUzNTA4MDB9.8y3H_m16G010a-35fJ8K5I9wM80_y3r4b3_Qe4-z55A"
}
受保护接口测试:
curl -X GET http://localhost:3000/protected -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3R1c2VyIiwiaWF0IjoxNjI1MzQ3MjAwLCJleHAiOjE2MjUzNTA4MDB9.8y3H_m16G010a-35fJ8K5I9wM80_y3r4b3_Qe4-z55A"
响应示例:
This is a protected route
结语
通过本文,我们详细介绍了Session与Token认证的对比、JWT的原理与生成/验证流程、使用bcrypt加密用户密码,并通过实战展示了如何在Node.js中实现登录鉴权中间件。希望这些内容能够帮助你更好地理解和应用用户认证与JWT技术。
关注我!!🫵 持续为你带来Nodejs相关内容。