您现在的位置是:网站首页 > 认证授权方案实现文章详情
认证授权方案实现
陈川
【
Node.js
】
1710人已围观
7102字
认证与授权的基本概念
认证(Authentication)是验证用户身份的过程,授权(Authorization)则是确定用户权限的过程。在Express应用中,这两者通常结合使用。常见的认证方式包括基于Session、JWT(JSON Web Token)和OAuth等。授权通常通过角色(Role)或权限(Permission)系统实现。
// 简单的用户对象示例
const user = {
id: 1,
username: 'admin',
password: 'hashed_password',
roles: ['admin', 'editor']
}
基于Session的认证实现
Session认证是传统的服务器端认证方式。Express中可以使用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
}))
// 登录路由
app.post('/login', (req, res) => {
const { username, password } = req.body
// 验证用户凭证
if (authenticateUser(username, password)) {
req.session.user = { username }
res.send('登录成功')
} else {
res.status(401).send('认证失败')
}
})
// 受保护的路由
app.get('/profile', (req, res) => {
if (!req.session.user) {
return res.status(403).send('请先登录')
}
res.send(`欢迎 ${req.session.user.username}`)
})
JWT认证实现
JWT是一种无状态的认证机制,适合分布式系统和前后端分离架构:
const jwt = require('jsonwebtoken')
const express = require('express')
const app = express()
const SECRET_KEY = 'your_jwt_secret'
// 生成JWT
function generateToken(user) {
return jwt.sign(
{ userId: user.id, username: user.username },
SECRET_KEY,
{ expiresIn: '1h' }
)
}
// 登录路由
app.post('/login', (req, res) => {
const { username, password } = req.body
const user = authenticateUser(username, password)
if (user) {
const token = generateToken(user)
res.json({ token })
} else {
res.status(401).send('认证失败')
}
})
// JWT验证中间件
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_KEY, (err, user) => {
if (err) return res.sendStatus(403)
req.user = user
next()
})
}
// 受保护的路由
app.get('/profile', authenticateToken, (req, res) => {
res.json({ message: `欢迎 ${req.user.username}` })
})
基于角色的访问控制(RBAC)
RBAC是一种常见的授权模式,通过角色来控制资源访问:
// 用户角色定义
const ROLES = {
ADMIN: 'admin',
EDITOR: 'editor',
USER: 'user'
}
// 检查角色中间件
function checkRole(requiredRoles) {
return (req, res, next) => {
const userRoles = req.user.roles || []
const hasRole = requiredRoles.some(role => userRoles.includes(role))
if (!hasRole) {
return res.status(403).send('无权访问')
}
next()
}
}
// 使用示例
app.get('/admin',
authenticateToken,
checkRole([ROLES.ADMIN]),
(req, res) => {
res.send('管理员面板')
}
)
OAuth2.0集成
OAuth2.0常用于第三方认证,如Google、Facebook登录:
const passport = require('passport')
const GoogleStrategy = require('passport-google-oauth20').Strategy
// 配置Passport
passport.use(new GoogleStrategy({
clientID: 'your_google_client_id',
clientSecret: 'your_google_client_secret',
callbackURL: '/auth/google/callback'
},
(accessToken, refreshToken, profile, done) => {
// 查找或创建用户
User.findOrCreate({ googleId: profile.id }, (err, user) => {
return done(err, user)
})
}
))
// 序列化用户
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user)
})
})
// Express配置
app.use(passport.initialize())
app.use(passport.session())
// 认证路由
app.get('/auth/google',
passport.authenticate('google', { scope: ['profile'] })
)
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
(req, res) => {
res.redirect('/profile')
}
)
权限中间件的进阶实现
更细粒度的权限控制可以基于资源和操作:
// 权限检查中间件
function checkPermission(resource, action) {
return async (req, res, next) => {
const user = req.user
const hasPermission = await PermissionService.check(
user.id,
resource,
action
)
if (!hasPermission) {
return res.status(403).send('无权执行此操作')
}
next()
}
}
// 使用示例
app.put('/articles/:id',
authenticateToken,
checkPermission('article', 'update'),
(req, res) => {
// 更新文章逻辑
}
)
安全最佳实践
- 密码存储:始终使用bcrypt等库哈希密码
const bcrypt = require('bcrypt')
const saltRounds = 10
async function hashPassword(password) {
return await bcrypt.hash(password, saltRounds)
}
async function comparePassword(password, hash) {
return await bcrypt.compare(password, hash)
}
- HTTPS:生产环境必须使用HTTPS
- CSRF防护:使用
csurf
中间件
const csrf = require('csurf')
const csrfProtection = csrf({ cookie: true })
app.use(csrfProtection)
- 速率限制:防止暴力破解
const rateLimit = require('express-rate-limit')
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100 // 每个IP限制100次请求
})
app.use('/login', limiter)
多因素认证(MFA)实现
增强安全性可以实现多因素认证:
const speakeasy = require('speakeasy')
// 生成MFA密钥
app.get('/mfa/setup', authenticateToken, (req, res) => {
const secret = speakeasy.generateSecret({ length: 20 })
// 保存secret到用户记录
User.updateMfaSecret(req.user.id, secret.base32)
res.json({
secret: secret.base32,
otpauth_url: secret.otpauth_url
})
})
// 验证MFA令牌
app.post('/mfa/verify', authenticateToken, (req, res) => {
const { token } = req.body
const user = req.user
const verified = speakeasy.totp.verify({
secret: user.mfaSecret,
encoding: 'base32',
token
})
if (verified) {
// 标记用户为完全认证
req.session.mfaVerified = true
res.send('MFA验证成功')
} else {
res.status(401).send('无效的MFA令牌')
}
})
// 需要MFA的路由
app.get('/sensitive-data',
authenticateToken,
(req, res, next) => {
if (!req.session.mfaVerified) {
return res.status(403).send('需要MFA验证')
}
next()
},
(req, res) => {
res.send('敏感数据')
}
)
令牌刷新机制
长期会话需要安全的令牌刷新机制:
// 生成刷新令牌
function generateRefreshToken(user) {
return jwt.sign(
{ userId: user.id },
REFRESH_SECRET,
{ expiresIn: '7d' }
)
}
// 令牌刷新端点
app.post('/token/refresh', (req, res) => {
const { refreshToken } = req.body
if (!refreshToken) return res.sendStatus(401)
jwt.verify(refreshToken, REFRESH_SECRET, (err, user) => {
if (err) return res.sendStatus(403)
const accessToken = generateToken({ id: user.userId })
res.json({ accessToken })
})
})
审计日志记录
记录重要操作以便审计:
// 审计日志中间件
function auditLog(action) {
return (req, res, next) => {
const user = req.user || { id: 'anonymous' }
const timestamp = new Date()
const details = {
action,
userId: user.id,
ip: req.ip,
timestamp,
method: req.method,
path: req.path
}
AuditLog.create(details)
.then(() => next())
.catch(err => {
console.error('审计日志失败:', err)
next()
})
}
}
// 使用示例
app.delete('/users/:id',
authenticateToken,
checkRole([ROLES.ADMIN]),
auditLog('DELETE_USER'),
(req, res) => {
// 删除用户逻辑
}
)
上一篇: 数据验证与输入过滤
下一篇: 日志系统的集成与配置