用 Claude Code 构建认证系统实战(JWT 与 OAuth)
从 JWT 双 Token 方案到 Google OAuth 集成,手把手教你用 Claude Code 搭建一套生产级的认证系统,含完整代码示例。
为什么用 Claude Code 构建认证系统
认证是直接关系到安全的核心功能,一旦实现出错,往往会演变成严重漏洞。Claude Code 能基于安全最佳实践生成代码,并主动提醒那些容易被忽视的攻击面,让你少踩坑。
JWT 认证的实现
> 用 Express + TypeScript 实现 JWT 认证。
> 采用双 Token 方案:Access Token(15 分钟)+ Refresh Token(7 天)。
> Refresh Token 用 httpOnly Cookie 管理。
认证服务
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
import { prisma } from './db';
const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET!;
const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET!;
interface TokenPayload {
userId: string;
email: string;
}
export class AuthService {
async register(email: string, password: string, name: string) {
const existing = await prisma.user.findUnique({ where: { email } });
if (existing) throw new Error('Email already registered');
const hashedPassword = await bcrypt.hash(password, 12);
const user = await prisma.user.create({
data: { email, password: hashedPassword, name },
});
return this.generateTokens({ userId: user.id, email: user.email });
}
async login(email: string, password: string) {
const user = await prisma.user.findUnique({ where: { email } });
if (!user) throw new Error('Invalid credentials');
const valid = await bcrypt.compare(password, user.password);
if (!valid) throw new Error('Invalid credentials');
return this.generateTokens({ userId: user.id, email: user.email });
}
async refreshToken(token: string) {
const payload = jwt.verify(token, REFRESH_TOKEN_SECRET) as TokenPayload;
// 在数据库中校验 Refresh Token 是否有效
const stored = await prisma.refreshToken.findFirst({
where: { token, userId: payload.userId, revoked: false },
});
if (!stored) throw new Error('Invalid refresh token');
// 吊销旧 Token(Token Rotation)
await prisma.refreshToken.update({
where: { id: stored.id },
data: { revoked: true },
});
return this.generateTokens(payload);
}
private async generateTokens(payload: TokenPayload) {
const accessToken = jwt.sign(payload, ACCESS_TOKEN_SECRET, {
expiresIn: '15m',
});
const refreshToken = jwt.sign(payload, REFRESH_TOKEN_SECRET, {
expiresIn: '7d',
});
await prisma.refreshToken.create({
data: { token: refreshToken, userId: payload.userId },
});
return { accessToken, refreshToken };
}
}
认证中间件
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
export function authMiddleware(req: Request, res: Response, next: NextFunction) {
const header = req.headers.authorization;
if (!header?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = header.slice(7);
try {
const payload = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET!);
req.user = payload as TokenPayload;
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid or expired token' });
}
}
路由配置
import { Router } from 'express';
import { AuthService } from './auth-service';
const router = Router();
const auth = new AuthService();
router.post('/register', async (req, res) => {
try {
const { accessToken, refreshToken } = await auth.register(
req.body.email, req.body.password, req.body.name
);
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
res.json({ accessToken });
} catch (err) {
res.status(400).json({ error: (err as Error).message });
}
});
router.post('/login', async (req, res) => {
try {
const { accessToken, refreshToken } = await auth.login(
req.body.email, req.body.password
);
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
res.json({ accessToken });
} catch (err) {
res.status(401).json({ error: 'Invalid credentials' });
}
});
router.post('/refresh', async (req, res) => {
try {
const token = req.cookies.refreshToken;
const { accessToken, refreshToken } = await auth.refreshToken(token);
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
res.json({ accessToken });
} catch (err) {
res.status(401).json({ error: 'Invalid refresh token' });
}
});
export default router;
OAuth 集成(Google 登录)
> 用 PassportJS 加一下 Google 登录,
> 跟现有的 JWT 认证体系集成在一起。
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: '/auth/google/callback',
}, async (accessToken, refreshToken, profile, done) => {
let user = await prisma.user.findFirst({
where: { providerId: profile.id, provider: 'google' },
});
if (!user) {
user = await prisma.user.create({
data: {
email: profile.emails![0].value,
name: profile.displayName,
provider: 'google',
providerId: profile.id,
},
});
}
done(null, user);
}));
安全审查清单
你也可以让 Claude Code 帮你做一次安全审计:
> 帮我从安全角度 Review 一下认证相关的代码,
> 参照 OWASP Top 10 做检查。
重点要确认的条目包括:
- 密码是否使用 bcrypt 进行哈希
- JWT 的密钥长度是否足够
- Refresh Token 是否实现了 Rotation
- 是否做了 CSRF 防护
- 是否配置了速率限制
维持代码质量和安全可以参考 自动化重构实战。另外,把认证相关的规范 写进 CLAUDE.md,Claude Code 生成的代码会更加一致。
总结
借助 Claude Code,从 JWT 认证到 OAuth 集成,一套健壮的认证系统可以短时间内落地。由于生成的代码遵循安全最佳实践,那些最容易被忽略的漏洞也能提前被覆盖。上线时请务必妥善保管密钥、强制 HTTPS 通信。
更多信息请参阅 Anthropic 官方文档。
#Claude Code
#authentication
#JWT
#OAuth
#security
M
本文作者
Masa
深度使用 Claude Code 的工程师。运营 claudecode-lab.com——一个涵盖 10 种语言、超过 2,000 页内容的科技媒体。
相关文章
Use Cases
用 Claude Code 加速个人项目开发【附实战案例】
详解如何用 Claude Code 大幅提升个人项目的开发速度。包含从创意到上线的完整实战案例和工作流。
Use Cases
如何用 Claude Code 自动化代码重构
详解如何利用 Claude Code 高效完成代码重构自动化。包含实用提示词和真实项目中的重构模式。
Use Cases
Complete CORS Configuration Guide:Claude Code 实战指南
了解complete cors configuration guide:Claude Code 实战. 包含实用技巧和代码示例。