认证与授权
认证与授权是构建安全应用的核心机制。认证用于确认用户身份,授权则决定用户能访问哪些资源。在Node.js中,这些功能通常通过中间件和库来实现,比如Passport、JWT和OAuth2.0。
认证的基本概念
认证是验证用户身份的过程。常见的认证方式包括用户名密码、社交登录和API密钥。在Node.js中,可以使用bcrypt
对密码进行哈希处理,确保安全性。
const bcrypt = require('bcrypt');
const saltRounds = 10;
async function hashPassword(password) {
const salt = await bcrypt.genSalt(saltRounds);
return await bcrypt.hash(password, salt);
}
async function comparePassword(password, hash) {
return await bcrypt.compare(password, hash);
}
会话管理与Cookie
HTTP是无状态的,因此需要会话管理来跟踪用户。Express中可以使用express-session
中间件:
const session = require('express-session');
app.use(session({
secret: 'your_secret_key',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}));
JWT认证
JSON Web Token(JWT)是一种无状态的认证方式。它由三部分组成:头部、载荷和签名。使用jsonwebtoken
库可以轻松生成和验证JWT:
const jwt = require('jsonwebtoken');
const token = jwt.sign({ userId: 123 }, 'secret_key', { expiresIn: '1h' });
jwt.verify(token, 'secret_key', (err, decoded) => {
if (err) throw err;
console.log(decoded); // { userId: 123, iat: ..., exp: ... }
});
OAuth2.0与第三方登录
OAuth2.0允许用户通过第三方服务(如Google、GitHub)登录。Passport.js提供了多种策略:
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
clientID: 'your_client_id',
clientSecret: 'your_client_secret',
callbackURL: '/auth/google/callback'
},
(accessToken, refreshToken, profile, done) => {
// 处理用户数据
done(null, profile);
}
));
授权的实现方式
授权决定用户能做什么。常见的模式包括RBAC(基于角色的访问控制)和ABAC(基于属性的访问控制)。以下是一个RBAC示例:
function checkRole(role) {
return (req, res, next) => {
if (req.user.role !== role) {
return res.status(403).send('Forbidden');
}
next();
};
}
app.get('/admin', checkRole('admin'), (req, res) => {
res.send('Admin Dashboard');
});
权限的细粒度控制
有时需要更精细的权限控制。可以使用ACL(访问控制列表)或策略模式:
const permissions = {
admin: ['read', 'write', 'delete'],
user: ['read']
};
function checkPermission(action) {
return (req, res, next) => {
if (!permissions[req.user.role].includes(action)) {
return res.status(403).send('Forbidden');
}
next();
};
}
安全最佳实践
- HTTPS:始终使用HTTPS传输敏感数据。
- CSRF防护:使用
csurf
中间件防止跨站请求伪造。 - 速率限制:使用
express-rate-limit
防止暴力破解。
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100 // 限制请求数
});
app.use(limiter);
常见漏洞与防范
- SQL注入:使用参数化查询或ORM库。
- XSS攻击:对用户输入进行转义,使用
helmet
中间件。 - JWT安全:避免在客户端存储敏感数据,设置合理的过期时间。
const helmet = require('helmet');
app.use(helmet());
实际案例分析
假设有一个博客平台,用户可以发布文章,但只有管理员能删除文章。以下是实现代码:
app.post('/articles', authenticateUser, (req, res) => {
// 用户发布文章
});
app.delete('/articles/:id', authenticateUser, checkRole('admin'), (req, res) => {
// 管理员删除文章
});
性能优化
认证和授权可能成为性能瓶颈。可以通过以下方式优化:
- 缓存:缓存用户权限数据。
- 异步验证:使用异步方式验证JWT。
- 精简JWT:避免在JWT中存储过多数据。
const redis = require('redis');
const client = redis.createClient();
function cacheUserPermissions(userId, permissions) {
client.setex(`perms:${userId}`, 3600, JSON.stringify(permissions));
}
测试与调试
编写测试用例确保认证和授权逻辑正确。使用supertest
进行HTTP测试:
const request = require('supertest');
describe('Auth API', () => {
it('should deny access without token', async () => {
const res = await request(app).get('/protected');
expect(res.statusCode).toEqual(401);
});
});
日志与监控
记录认证和授权相关事件,便于排查问题:
const winston = require('winston');
const logger = winston.createLogger({
transports: [new winston.transports.File({ filename: 'auth.log' })]
});
app.use((req, res, next) => {
logger.info(`${req.method} ${req.path} - ${req.user?.id || 'anonymous'}`);
next();
});
多因素认证
提升安全性可以引入多因素认证(MFA),如短信验证码或TOTP:
const speakeasy = require('speakeasy');
const secret = speakeasy.generateSecret();
const token = speakeasy.totp({
secret: secret.base32,
encoding: 'base32'
});
// 验证令牌
const verified = speakeasy.totp.verify({
secret: secret.base32,
encoding: 'base32',
token: userToken,
window: 1
});
微服务中的认证
在微服务架构中,可以使用API网关集中处理认证,或传递JWT:
// 服务间传递JWT
axios.get('http://service-b/data', {
headers: { 'Authorization': `Bearer ${jwtToken}` }
});
无服务器架构的认证
在AWS Lambda等无服务器环境中,可以使用Cognito或Auth0:
exports.handler = async (event) => {
const user = event.requestContext.authorizer.claims;
return { statusCode: 200, body: JSON.stringify(user) };
};
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn