认证鉴权体系
问题
前端和服务端的认证鉴权方案有哪些?Session、JWT、OAuth、SSO 各有什么特点?
答案
认证方案全景
方案对比
| 维度 | Session-Cookie | JWT | OAuth 2.0 | SSO |
|---|---|---|---|---|
| 状态 | 有状态(服务端存储) | 无状态(客户端存储) | 基于 Token | 基于 Token |
| 存储 | 服务端内存/Redis | 客户端 | 客户端 | 认证中心 |
| 扩展性 | 需要共享 Session | 天然支持分布式 | 天然支持 | 天然支持 |
| 安全性 | CSRF 风险 | XSS 风险 | 较安全 | 较安全 |
| 适用场景 | 传统 Web 应用 | SPA、移动端、API | 第三方登录 | 多系统统一登录 |
Session-Cookie
session-example.ts
// 服务端(Express + express-session)
import session from 'express-session';
import RedisStore from 'connect-redis';
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret-key',
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true, // 防 XSS
secure: true, // 仅 HTTPS
sameSite: 'lax', // 防 CSRF
maxAge: 24 * 60 * 60 * 1000, // 1 天
},
}));
// 登录
app.post('/login', (req, res) => {
// 验证用户名密码...
req.session.userId = user.id;
res.json({ success: true });
});
JWT
jwt-example.ts
import jwt from 'jsonwebtoken';
// 签发 Token
const generateTokens = (userId: string) => {
const accessToken = jwt.sign(
{ userId, type: 'access' },
process.env.JWT_SECRET!,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId, type: 'refresh' },
process.env.JWT_REFRESH_SECRET!,
{ expiresIn: '7d' }
);
return { accessToken, refreshToken };
};
// 验证中间件
const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) return res.status(401).json({ error: 'Unauthorized' });
try {
const payload = jwt.verify(token, process.env.JWT_SECRET!);
req.user = payload;
next();
} catch {
return res.status(401).json({ error: 'Token expired' });
}
};
JWT 注意事项
- JWT 无法主动失效:一旦签发,在过期前一直有效。需要黑名单机制或短有效期 + RefreshToken
- JWT 体积比 Session ID 大
- 不要在 JWT 中存敏感信息(payload 只是 Base64 编码,不是加密)
OAuth 2.0 授权码流程
常见面试问题
Q1: JWT 存在 localStorage 还是 Cookie 中?
答案:
| 存储位置 | 优点 | 缺点 |
|---|---|---|
| localStorage | 不会自动发送,无 CSRF 风险 | XSS 可窃取 |
| Cookie (httpOnly) | XSS 无法读取 | 需防 CSRF |
推荐:httpOnly Cookie + SameSite=Lax,安全性最高。如果是纯 SPA + 跨域 API,用 localStorage + 短有效期也可接受。
Q2: Token 无感刷新怎么做?
答案:
双 Token 机制:AccessToken(短期 15min)+ RefreshToken(长期 7d)。AccessToken 过期后,用 RefreshToken 换新的 AccessToken,用户无感知。
关键实现:Axios 拦截器中检测 401,自动用 RefreshToken 刷新,并重放失败的请求。
Q3: 如何实现退出登录让 JWT 失效?
答案:
JWT 本身无法撤销,常用方案:
- Redis 黑名单:退出时将 token 加入黑名单,每次请求检查
- 版本号:用户表加
tokenVersion,退出时 +1,JWT 中携带版本号比对 - 短有效期:AccessToken 5-15 分钟,过期自然失效
Q4: Session 和 JWT 怎么选?
答案:
- Session:传统 SSR 应用、需要即时踢人、安全性要求高
- JWT:SPA、移动端、微服务、需要跨域/跨服务认证
Q5: PKCE 是什么?为什么需要?
答案:
PKCE(Proof Key for Code Exchange)解决公共客户端(SPA、移动端)OAuth 授权码被截获的风险:
- 客户端生成随机
code_verifier,计算code_challenge = SHA256(code_verifier) - 授权请求携带
code_challenge - 换 Token 时携带
code_verifier,服务端验证匹配
即使授权码被截获,没有 code_verifier 也无法换取 Token。
相关链接
- JWT 认证 - JWT 结构与实现
- Cookie 与 Session - Cookie/Session 详解
- 设计单点登录系统 - SSO 系统设计
- Token 无感刷新 - Token 刷新实战