身份验证与安全性
在 Express.js 应用程序中,身份验证与会话管理是确保用户数据安全和访问控制的核心功能。以下是三种常见的身份验证机制:Cookie 与 Session、JWT (JSON Web Token) 以及路由保护的详细介绍。
Cookie 和 Session
Session 通过在服务器端保存会话数据,并在客户端通过 Cookie 存储 Session ID 来实现。客户端的每个请求都会包含这个 ID,服务器根据此 ID 查找并验证会话数据。
安装依赖
npm install express-session cookie-parser
express-session
:用于管理服务器端的会话。cookie-parser
:用于解析请求中的 Cookie。
基本实现
以下是一个使用 Session 实现用户会话管理的例子:
import express from 'express';
import session from 'express-session';
import cookieParser from 'cookie-parser';
const app = express();
// 使用 cookie-parser 解析请求中的 Cookie
app.use(cookieParser());
// 使用 express-session 管理 Session
app.use(
session({
secret: 'your-secret-key', // 用于加密 Session ID 的密钥
resave: false, // 如果 Session 没有修改,是否强制保存
saveUninitialized: true, // 是否保存未初始化的会话
cookie: { secure: false }, // 如果使用 HTTPS,secure 应设为 true
}),
);
// 测试会话存储
app.get('/', (req, res) => {
if (req.session.views) {
req.session.views++; // 增加访问次数
res.send(`You have visited this page ${req.session.views} times`);
} else {
req.session.views = 1; // 初始化访问次数
res.send(
'Welcome to the session demo! Refresh to see the view count increase.',
);
}
});
app.listen(1234, () => {
console.log('Server is running on http://localhost:1234');
});
Cookie 和 Session 机制
- 用户首次访问时,服务器生成一个唯一的 Session ID 并存储在 Cookie 中。
- 后续的每次请求都会带上这个 Cookie,服务器根据 Session ID 找到对应的会话数据。
登入和登出
// 用户登录
app.post('/login', (req, res) => {
const { username } = req.body;
req.session.username = username; // 将用户信息存储在 Session 中
res.send(`Logged in as ${username}`);
});
// 用户登出
app.post('/logout', (req, res) => {
req.session.destroy(err => {
if (err) {
return res.status(500).send('Logout failed');
}
res.send('Logged out successfully');
});
});
使用 JWT 进行身份验证
JWT (JSON Web Token) 是一种无状态的身份验证方式, 用户信息直接存储在 Token 中,客户端持有 Token,服务器通过验证 Token 来识别用户。
安装依赖
npm install jsonwebtoken bcryptjs
jsonwebtoken
:生成和验证 JWT。bcryptjs
:用于加密和验证用户密码。
JWT 身份验证流程
- 用户注册:用户输入用户名和密码,密码通过 bcrypt 加密后存储。
- 用户登 录:用户提供用户名和密码,服务器验证后生成 JWT 并返回给客户端。
- 验证请求:每次客户端请求时,携带 JWT,服务器验证该 Token 并允许访问受保护资源。
实现用户注册和登录
import express from 'express';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';
const app = express();
app.use(express.json());
const users: { id: number; username: string; password: string }[] = [];
const secretKey = 'your-secret-key'; // JWT 密钥
// 用户注册
app.post('/register', async (req, res) => {
const { username, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10); // 密码加密
const newUser = { id: users.length + 1, username, password: hashedPassword };
users.push(newUser);
res.status(201).json({ message: 'User registered successfully' });
});
// 用户登录并生成 JWT
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user || !(await bcrypt.compare(password, user.password))) {
return res.status(401).json({ message: 'Invalid credentials' });
}
// 生成 JWT
const token = jwt.sign({ id: user.id, username: user.username }, secretKey, { expiresIn: '1h' });
res.json({ token });
});
验证 JWT 的中间件
通过中间件来验证用户请求中的 JWT。
function authenticateToken(req: any, res: any, next: any) {
const token = req.headers['authorization'];
if (!token) return res.sendStatus(401); // 未提供 Token
jwt.verify(token.split(' ')[1], secretKey, (err: any, user: any) => {
if (err) return res.sendStatus(403); // Token 无效或过期
req.user = user; // 将解码的用户信息存储到 req 中
next();
});
}
受保护的路由
只有已登录并携带有效 JWT 的用户才能访问受保护的路由。
app.get('/protected', authenticateToken, (req, res) => {
res.json({ message: 'This is a protected route', user: req.user });
});
路由保护
通过使用中间件,可以保护特定路由,使得只有通过身份验证的用户才能访问。
在上述 JWT 示例中,authenticateToken
函数就是一个用来保护路由的中间件。所有受保护的路由都需要经过这个中间件的验证。
app.get('/user/profile', authenticateToken, (req, res) => {
// 用户信息只有在验证通过后才能访问
res.json({ profile: req.user });
});
安全性
在使用 Express.js 开发 web 应用时,安全性是非常重要的一个方面。常见的安全攻击包括跨站脚本攻击(XSS)、跨站请求伪造(CSRF)和 SQL 注入。为了防御这些攻击,我们可以采取多种措施,如数据过滤、验证、使用第三方安全库等。
以下是防御这些常见安全攻击的详细说明,以及如何使用 helmet 来增强 Express 应用的安全性。
防御 XSS(跨站脚本攻击)
什么是 XSS?
XSS 攻击是指攻击者通过向网页注入恶意脚本,使用户在访问页面时执行这些脚本。它通常通过不安全的数据输入或输出来实现。
如何防御 XSS?
- 输入过滤与输出编码:永远不要信任用户输入,在处理用户输入的数据时应进行过滤和清理。输出到 HTML 时,应对数据进行编码。
- 使用库来防止 XSS:例如
xss-clean
、DOMPurify
等库可以帮助你过滤不安全的 HTML 内容。
安装 xss-clean
库:
npm install xss-clean
在 Express 中应用 xss-clean
:
import express from 'express';
import xssClean from 'xss-clean';
const app = express();
// 防御 XSS 攻击
app.use(xssClean());
app.post('/submit', (req, res) => {
// 假设用户提交的数据被存储或呈现给其他用户
res.send(`Received input: ${req.body.input}`);
});
app.listen(1234, () => {
console.log('Server is running on http://localhost:1234');
});
防御 CSRF(跨站请求伪造)
什么是 CSRF?
CSRF 攻击是指攻击者通过欺骗用户的身份认证信息,让用户在不知情的情况下执行恶意操作,如发起转账请求、修改密码等。
如何防御 CSRF?
使用 CSRF 令牌:每个请求附带一个唯一的 CSRF 令牌,只有服务器生成并验证的令牌才能通过验证。