认证授权方案实现
认证与授权的基本概念
认证(Authentication)是验证用户身份的过程,授权(Authorization)则是确定用户是否有权限访问特定资源。在Express应用中,这两者通常结合使用来保护路由和API端点。常见的认证方式包括基于会话的认证、JWT(JSON Web Token)和OAuth,而授权通常通过角色或权限系统实现。
基于会话的认证实现
Express中可以使用express-session
中间件实现基于会话的认证。首先需要安装依赖:
npm install express-session
然后配置中间件:
const express = require('express');
const session = require('express-session');
const app = express();
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true,
cookie: { secure: false } // 生产环境应设为true并启用HTTPS
}));
用户登录时可以这样设置会话:
app.post('/login', (req, res) => {
const { username, password } = req.body;
// 验证用户凭证(实际项目中应使用数据库验证)
if (username === 'admin' && password === 'password') {
req.session.user = { username, role: 'admin' };
return res.send('登录成功');
}
res.status(401).send('认证失败');
});
保护路由的中间件示例:
function requireAuth(req, res, next) {
if (req.session.user) {
return next();
}
res.status(401).send('需要登录');
}
app.get('/protected', requireAuth, (req, res) => {
res.send('这是受保护的内容');
});
JWT认证实现
JWT是一种无状态的认证机制,适合RESTful API。首先安装jsonwebtoken
:
npm install jsonwebtoken
生成和验证Token的示例:
const jwt = require('jsonwebtoken');
const secret = 'your-jwt-secret';
// 登录路由生成Token
app.post('/api/login', (req, res) => {
const { username, password } = req.body;
if (username === 'user' && password === 'pass') {
const token = jwt.sign(
{ username, role: 'user' },
secret,
{ expiresIn: '1h' }
);
return res.json({ token });
}
res.status(401).json({ error: '认证失败' });
});
// 验证Token的中间件
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, secret, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
// 受保护的路由
app.get('/api/protected', authenticateToken, (req, res) => {
res.json({ message: `欢迎, ${req.user.username}` });
});
基于角色的授权控制
在认证基础上,可以添加角色检查实现授权:
// 角色检查中间件
function requireRole(role) {
return (req, res, next) => {
if (req.user?.role !== role) {
return res.status(403).send('无权访问');
}
next();
};
}
// 管理员专属路由
app.get('/admin/dashboard',
authenticateToken,
requireRole('admin'),
(req, res) => {
res.send('管理员仪表板');
}
);
OAuth2.0集成
Express可以集成Passport.js实现OAuth认证。以GitHub OAuth为例:
npm install passport passport-github2
配置示例:
const passport = require('passport');
const GitHubStrategy = require('passport-github2').Strategy;
passport.use(new GitHubStrategy({
clientID: 'your-client-id',
clientSecret: 'your-client-secret',
callbackURL: 'http://localhost:3000/auth/github/callback'
},
(accessToken, refreshToken, profile, done) => {
// 在此处查找或创建用户
return done(null, profile);
}
));
// 序列化和反序列化用户
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));
// 初始化Passport
app.use(passport.initialize());
app.use(passport.session());
// 认证路由
app.get('/auth/github', passport.authenticate('github'));
app.get('/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/login' }),
(req, res) => res.redirect('/')
);
权限管理系统
更复杂的系统可能需要基于权限的访问控制(PBAC):
// 权限检查中间件
function checkPermission(permission) {
return (req, res, next) => {
const userPermissions = getUserPermissions(req.user.id); // 假设的函数
if (!userPermissions.includes(permission)) {
return res.status(403).send('无权执行此操作');
}
next();
};
}
// 使用示例
app.delete('/api/posts/:id',
authenticateToken,
checkPermission('delete_post'),
(req, res) => {
// 删除文章逻辑
}
);
安全最佳实践
- 始终使用HTTPS传输敏感数据
- 设置安全的Cookie属性:
app.use(session({
// ...其他配置
cookie: {
secure: true,
httpOnly: true,
sameSite: 'strict',
maxAge: 24 * 60 * 60 * 1000 // 1天
}
}));
- 密码存储应使用bcrypt等库进行哈希处理:
const bcrypt = require('bcrypt');
const saltRounds = 10;
// 注册时哈希密码
app.post('/register', async (req, res) => {
const { username, password } = req.body;
const hashedPassword = await bcrypt.hash(password, saltRounds);
// 存储username和hashedPassword到数据库
});
- 实现速率限制防止暴力破解:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 5 // 每个IP最多5次请求
});
app.use('/login', limiter);
错误处理
专门的认证错误处理中间件:
function authErrorHandler(err, req, res, next) {
if (err.name === 'UnauthorizedError') {
return res.status(401).json({ error: '无效的Token' });
}
if (err.name === 'ForbiddenError') {
return res.status(403).json({ error: '无权访问' });
}
next(err);
}
app.use(authErrorHandler);
测试认证授权
使用Supertest进行端点测试:
const request = require('supertest');
const app = require('../app');
describe('认证测试', () => {
it('未认证用户访问受保护路由应返回401', async () => {
const res = await request(app).get('/protected');
expect(res.statusCode).toEqual(401);
});
it('有效Token应允许访问', async () => {
const loginRes = await request(app)
.post('/login')
.send({ username: 'user', password: 'pass' });
const token = loginRes.body.token;
const res = await request(app)
.get('/protected')
.set('Authorization', `Bearer ${token}`);
expect(res.statusCode).toEqual(200);
});
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:数据验证与输入过滤
下一篇:日志系统的集成与配置