利用JWT、OAuth2、会话管理、RBAC、权限及资源所有权验证,实现安全的认证与授权。
认证和授权模式
功能概述
认证和授权模式是一项面向实际任务的技能,主要用于主体认证和授权模式包括JWT、 OAuth2、 会话管理以及 RBAC , 用于构建安全、 可扩展的访问控制系统。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。
核心要点
- 使用时应结合输入条件选择合适的执行方式,核对必要参数、依赖环境与输出内容,并按原始要求处理异常情况。
- 该技能适合需要稳定复用相关能力的场景,可作为自动化工作流的一部分,也便于后续检查、调整和扩展。
- 从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。
使用与执行
实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。
结果检查与注意事项
执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。
认证与授权模式
掌握 JWT、OAuth2、会话管理及 RBAC 等认证与授权模式,构建安全、可扩展的访问控制系统。
适用场景
USE WHEN(适用于以下情况):
- 实现用户认证系统
- 保护 REST 或 GraphQL API
- 集成 OAuth2/社交登录或单点登录(SSO)
- 设计会话管理机制
- 实现基于角色的访问控制(RBAC)或权限系统
- 调试认证相关问题
DON'T USE WHEN(不适用于以下情况):
- 仅需 UI / 登录页样式调整
- 任务纯属基础设施范畴,不涉及身份识别需求
- 无法修改现有认证策略
认证 vs 授权
| AuthN(认证) | AuthZ(授权) |
|---|---|
| “你是谁?” | “你能做什么?” |
| 验证身份 | 检查权限 |
| 颁发凭证 | 执行策略 |
| 登录 / 注销 | 访问控制 |
认证策略
| 策略 | 优点 | 缺点 | 最佳适用场景 |
|---|---|---|---|
| 会话(Session) | 简单、安全 | 有状态、横向扩展复杂 | 传统 Web 应用 |
| JWT | 无状态、可扩展性强 | Token 体积较大、难以主动吊销 | API、微服务架构 |
| OAuth2/OIDC | 支持委托式认证、天然适配 SSO | 配置复杂 | 社交登录、企业级集成 |
JWT 实现
生成 Token
import jwt from 'jsonwebtoken';
function generateTokens(user: User) {
const accessToken = jwt.sign(
{ userId: user.id, email: user.email, role: user.role },
process.env.JWT_SECRET!,
{ expiresIn: '15m' } // 短期有效
);
const refreshToken = jwt.sign(
{ userId: user.id },
process.env.JWT_REFRESH_SECRET!,
{ expiresIn: '7d' } // 长期有效
);
return { accessToken, refreshToken };
}
认证中间件
function authenticate(req: Request, res: Response, next: NextFunction) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.substring(7);
try {
const payload = jwt.verify(token, process.env.JWT_SECRET!);
req.user = payload;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
}
刷新 Token 流程
app.post('/api/auth/refresh', async (req, res) => {
const { refreshToken } = req.body;
try {
// 验证 refresh token
const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET!);
// 检查该 token 是否存在于数据库中(未被吊销)
const storedToken = await db.refreshTokens.findOne({
token: await hash(refreshToken),
expiresAt: { $gt: new Date() }
});
if (!storedToken) {
return res.status(403).json({ error: 'Token revoked' });
}
// 生成新的 access token
const user = await db.users.findById(payload.userId);
const accessToken = jwt.sign(
{ userId: user.id, email: user.email, role: user.role },
process.env.JWT_SECRET!,
{ expiresIn: '15m' }
);
res.json({ accessToken });
} catch {
res.status(401).json({ error: 'Invalid refresh token' });
}
});
基于会话的认证
import session from 'express-session';
import RedisStore from 'connect-redis';
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production', // 仅 HTTPS 传输
httpOnly: true, // 禁止 JavaScript 访问
maxAge: 24 * 60 * 60 * 1000, // 24 小时
sameSite: 'strict' // 防 CSRF 攻击
}
}));
// 登录
app.post('/api/auth/login', async (req, res) => {
const { email, password } = req.body;
const user = await db.users.findOne({ email });
if (!user || !(await verifyPassword(password, user.passwordHash))) {
return res.status(401).json({ error: 'Invalid credentials' });
}
req.session.userId = user.id;
req.session.role = user.role;
res.json({ user: { id: user.id, email: user.email } });
});
// 注销
app.post('/api/auth/logout', (req, res) => {
req.session.destroy(() => {
res.clearCookie('connect.sid');
res.json({ message: 'Logged out' });
});
});
OAuth2 / 社交登录
import passport from 'passport';
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
callbackURL: '/api/auth/google/callback'
}, async (accessToken, refreshToken, profile, done) => {
// 查找或创建用户
let user = await db.users.findOne({ googleId: profile.id });
if (!user) {
user = await db.users.create({
googleId: profile.id,
email: profile.emails?.[0]?.value,
name: profile.displayName
});
}
return done(null, user);
}));
// 路由
app.get('/api/auth/google',
passport.authenticate('google', { scope: ['profile', 'email'] }));
app.get('/api/auth/google/callback',
passport.authenticate('google', { session: false }),
(req, res) => {
const tokens = generateTokens(req.user);
res.redirect(`${FRONTEND_URL}/auth/callback?token=${tokens.accessToken}`);
});
授权:RBAC
enum Role {
USER = 'user',
MODERATOR = 'moderator',
ADMIN = 'admin'
}
const roleHierarchy: Record = {
[Role.ADMIN]: [Role.ADMIN, Role.MODERATOR, Role.USER],
[Role.MODERATOR]: [Role.MODERATOR, Role.USER],
[Role.USER]: [Role.USER]
};
function hasRole(userRole: Role, requiredRole: Role): boolean {
return roleHierarchy[userRole].includes(requiredRole);
}
function requireRole(...roles: Role[]) {
return (req: Request, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: 'Not authenticated' });
}
if (!roles.some(role => hasRole(req.user.role, role))) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
// 使用示例
app.delete('/api/users/:id',
authenticate,
requireRole(Role.ADMIN),
async (req, res) => {
await db.users.delete(req.params.id);
res.json({ message: 'User deleted' });
}
);
基于权限的访问控制
enum Permission {
READ_USERS = 'read:users',
WRITE_USERS = 'write:users',
DELETE_USERS = 'delete:users',
READ_POSTS = 'read:posts',
WRITE_POSTS = 'write:posts'
}
const rolePermissions: Record = {
[Role.USER]: [Permission.READ_POSTS, Permission.WRITE_POSTS],
[Role.MODERATOR]: [Permission.READ_POSTS, Permission.WRITE_POSTS, Permission.READ_USERS],
[Role.ADMIN]: Object.values(Permission)
};
function requirePermission(...permissions: Permission[]) {
return (req: Request, res: Response, next: NextFunction) => {
if (!req.user) return res.status(401).json({ error: 'Not authenticated' });
const hasAll = permissions.every(p =>
rolePermissions[req.user.role]?.includes(p)
);
if (!hasAll) return res.status(403).json({ error: 'Insufficient permissions' });
next();
};
}
资源所有权校验
function requireOwnership(resourceType: 'post' | 'comment') {
return async (req: Request, res: Response, next: NextFunction) => {
if (!req.user) return res.status(401).json({ error: 'Not authenticated' });
// 管理员可访问任意资源
if (req.user.role === Role.ADMIN) return next();
const resource = await db[resourceType].findById(req.params.id);
if (!resource) return res.status(404).json({ error: 'Not found' });
if (resource.userId !== req.user.userId) {
return res.status(403).json({ error: 'Not authorized' });
}
next();
};
}
// 使用示例:用户仅可更新自己的文章
app.put('/api/posts/:id', authenticate, requireOwnership('post'), updatePost);
密码安全
import bcrypt from 'bcrypt';
import { z } from 'zod';
const passwordSchema = z.string()
.min(12, 'Password must be at least 12 characters')
.regex(/[A-Z]/, 'Must contain uppercase')
.regex(/[a-z]/, 'Must contain lowercase')
.regex(/[0-9]/, 'Must contain number')
.regex(/[^A-Za-z0-9]/, 'Must contain special character');
async function hashPassword(password: string): Promise {
return bcrypt.hash(password, 12); // 12 轮哈希
}
async function verifyPassword(password: string, hash: string): Promise {
return bcrypt.compare(password, hash);
}
最佳实践
✅ 应当遵循
- 全站启用 HTTPS
- 使用 bcrypt(至少 12 轮)对密码进行哈希处理
- 设置短期有效的 access token(15–30 分钟)
- 将 refresh token 存储于数据库中(支持吊销)
- 严格校验所有输入数据
- 对认证相关端点实施速率限制
- 记录关键安全事件日志
- 为 Cookie 设置安全标志(httpOnly、secure、sameSite)
❌ 切勿采用
- 明文存储密码
- 将 JWT 存入 localStorage(易受 XSS 攻击)
- 使用弱密钥(weak JWT secrets)
- 仅依赖客户端校验认证状态
- 在错误响应中暴露堆栈信息
- 跳过服务端校验逻辑
- 忽略速率限制机制
常见陷阱与解决方案
| 问题 | 解决方案 |
|---|---|
| JWT 存储于 localStorage | 改用 httpOnly Cookie |
| Token 未设置过期时间 | 设定较短 TTL + 配合 refresh token 机制 |
| 密码强度不足 | 通过 zod 强制执行强密码策略 |
| 未启用速率限制 | 结合 express-rate-limit 与 Redis 实现 |
| 仅在客户端做权限校验 | 服务端必须始终校验权限 |
热门AI工具
相关专题
本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。
0
2026.11.12
本专题围绕Go-Zero的安装与工具链配置展开,讲清Go版本要求、goctl安装方式、代理镜像、protoc依赖与环境变量设置,说明多版本共存、升级回退与CI流水线安装的做法,并整理安装阶段的常见报错与验证步骤。
0
2026.11.12
本专题整理Go-Zero的入门知识,从框架定位、环境准备到跑通第一个API服务,讲清目录分层、配置加载、日志与中间件的基本用法,并对比Gin、Kratos的差异,帮助Go开发者建立整体认知,判断它是否适合当前项目。
0
2026.11.12
本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。
0
2026.11.06
围绕Postman HTTP请求头配置展开,讲解Headers选项的具体使用方式,包括Key和Value参数填写、请求头信息添加、常见Header字段设置及请求头修改方法,帮助用户解决接口调试过程中遇到的请求头配置问题。
0
2026.11.06
详细介绍Postman接口请求的创建与发送方法,涵盖GET和POST请求、URL配置、请求参数、Headers请求头、Body请求体设置以及响应结果查看,帮助用户掌握Postman接口请求的完整操作流程。
0
2026.11.06
DBeaver数据导入专题,整理Excel、CSV、SQL、TXT等常见文件的数据导入方法,涵盖目标数据库选择、数据表创建、字段映射、数据类型设置及导入参数配置等操作,帮助用户将本地文件中的数据写入数据库,并了解批量导入、重复数据处理和导入失败等常见问题的解决方法。
0
2026.11.05
DBeaver安全防护专题,围绕数据库连接安全、账号密码保护和访问权限管理展开,整理SSL/TLS加密连接、SSH隧道、数据库凭据保存及证书验证等配置方法,同时介绍敏感信息保护、远程连接风险和高权限账号使用等注意事项,帮助用户提高数据库日常管理的安全性。
0
2026.11.05
本专题聚焦DBeaver数据库连接配置与管理,覆盖从新建连接、选择数据库类型到填写连接参数和验证连接的完整流程。针对MySQL、PostgreSQL、SQL Server等常见数据库,整理相应的连接设置方法,并提供驱动缺失、认证失败和连接异常等问题的排查方向。
0
2026.11.04
热门下载
相关下载
精品课程
共6课时 | 12.1万人学习
共79课时 | 161.8万人学习
共6课时 | 54.7万人学习
最新文章