乐闻世界logo
搜索文章和话题

在前端框架(React、Vue、Angular)中如何实现 CSRF 防护?

2月19日 19:13

前端框架(如 React、Vue、Angular)中的 CSRF 防护需要考虑框架特性和最佳实践,以确保在单页应用(SPA)中提供有效的安全保护。

React 中的 CSRF 防护

1. 使用 CSRF Token

jsx
// 获取 CSRF Token import { useEffect, useState } from 'react'; function CSRFProtectedForm() { const [csrfToken, setCsrfToken] = useState(''); const [formData, setFormData] = useState({ name: '', email: '' }); useEffect(() => { // 从服务器获取 CSRF Token fetch('/api/csrf-token') .then(res => res.json()) .then(data => setCsrfToken(data.csrfToken)); }, []); const handleSubmit = async (e) => { e.preventDefault(); try { const response = await fetch('/api/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken // 在请求头中发送 Token }, body: JSON.stringify(formData) }); if (response.ok) { alert('提交成功!'); } else { alert('提交失败'); } } catch (error) { console.error('Error:', error); } }; return ( <form onSubmit={handleSubmit}> <input type="text" value={formData.name} onChange={(e) => setFormData({...formData, name: e.target.value})} placeholder="姓名" /> <input type="email" value={formData.email} onChange={(e) => setFormData({...formData, email: e.target.value})} placeholder="邮箱" /> <button type="submit">提交</button> </form> ); }

2. 使用 Axios 拦截器

jsx
import axios from 'axios'; // 创建 Axios 实例 const api = axios.create({ baseURL: '/api', withCredentials: true // 允许发送 Cookie }); // 请求拦截器:自动添加 CSRF Token api.interceptors.request.use(async (config) => { // 对于需要 CSRF 保护的请求方法 if (['post', 'put', 'patch', 'delete'].includes(config.method)) { try { const response = await axios.get('/api/csrf-token'); config.headers['X-CSRF-Token'] = response.data.csrfToken; } catch (error) { console.error('Failed to get CSRF token:', error); } } return config; }); // 使用示例 function SubmitForm() { const handleSubmit = async () => { try { await api.post('/submit', { data: 'example' }); alert('提交成功!'); } catch (error) { alert('提交失败'); } }; return <button onClick={handleSubmit}>提交</button>; }

Vue 中的 CSRF 防护

1. 使用 Vue Router 和 Axios

vue
<template> <form @submit.prevent="handleSubmit"> <input v-model="formData.name" placeholder="姓名" /> <input v-model="formData.email" placeholder="邮箱" /> <button type="submit">提交</button> </form> </template> <script> import axios from 'axios'; export default { data() { return { csrfToken: '', formData: { name: '', email: '' } }; }, async created() { // 获取 CSRF Token const response = await axios.get('/api/csrf-token'); this.csrfToken = response.data.csrfToken; }, methods: { async handleSubmit() { try { const response = await axios.post('/api/submit', this.formData, { headers: { 'X-CSRF-Token': this.csrfToken } }); if (response.data.success) { alert('提交成功!'); } } catch (error) { alert('提交失败'); } } } }; </script>

2. 使用 Vuex 管理 CSRF Token

javascript
// store/csrf.js import axios from 'axios'; export default { namespaced: true, state: { token: null }, mutations: { SET_TOKEN(state, token) { state.token = token; } }, actions: { async fetchToken({ commit }) { try { const response = await axios.get('/api/csrf-token'); commit('SET_TOKEN', response.data.csrfToken); } catch (error) { console.error('Failed to fetch CSRF token:', error); } } } };

Angular 中的 CSRF 防护

1. 使用 HttpClient 拦截器

typescript
// csrf.interceptor.ts import { Injectable } from '@angular/core'; import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; import { Observable, from } from 'rxjs'; import { switchMap } from 'rxjs/operators'; @Injectable() export class CsrfInterceptor implements HttpInterceptor { constructor(private http: HttpClient) {} intercept( req: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> { // 对于需要 CSRF 保护的请求方法 if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) { return from(this.getCsrfToken()).pipe( switchMap(token => { const csrfReq = req.clone({ setHeaders: { 'X-CSRF-Token': token } }); return next.handle(csrfReq); }) ); } return next.handle(req); } private async getCsrfToken(): Promise<string> { const response = await this.http.get<{ csrfToken: string }>('/api/csrf-token').toPromise(); return response.csrfToken; } } // app.module.ts import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { CsrfInterceptor } from './csrf.interceptor'; @NgModule({ imports: [HttpClientModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: CsrfInterceptor, multi: true } ] }) export class AppModule {}

通用最佳实践

1. Token 刷新策略

javascript
// 通用 Token 刷新逻辑 class CSRFTokenManager { constructor() { this.token = null; this.tokenExpiry = null; } async getToken() { // 检查 Token 是否过期 if (!this.token || Date.now() > this.tokenExpiry) { await this.refreshToken(); } return this.token; } async refreshToken() { const response = await fetch('/api/csrf-token'); const data = await response.json(); this.token = data.csrfToken; this.tokenExpiry = Date.now() + 3600000; // 1 小时后过期 } clearToken() { this.token = null; this.tokenExpiry = null; } }

2. 错误处理和重试

javascript
// 带有重试机制的请求 async function makeRequestWithRetry(url, data, maxRetries = 3) { let retries = 0; while (retries < maxRetries) { try { const token = await csrfTokenManager.getToken(); const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': token }, body: JSON.stringify(data) }); if (response.status === 403) { // Token 可能已过期,刷新并重试 csrfTokenManager.clearToken(); retries++; continue; } return await response.json(); } catch (error) { retries++; if (retries >= maxRetries) { throw error; } } } }

3. 安全配置

javascript
// Cookie 配置(服务器端) res.cookie('sessionId', sessionId, { httpOnly: true, // 防止 XSS 窃取 secure: true, // 仅 HTTPS sameSite: 'strict', // 最严格的 CSRF 防护 maxAge: 3600000 // 1 小时过期 });

框架特定的注意事项

React

  • 使用 Context API 共享 CSRF Token
  • 考虑使用 React Query 或 SWR 管理请求
  • 在组件卸载时清理 Token

Vue

  • 使用 Vuex 或 Pinia 管理 Token 状态
  • 利用 Vue 的生命周期钩子获取 Token
  • 考虑使用 VueUse 的 useFetch

Angular

  • 使用 HTTP 拦截器自动处理 Token
  • 利用依赖注入管理 Token 服务
  • 使用 RxJS 处理异步操作

前端框架中的 CSRF 防护需要结合框架特性和最佳实践,确保在提供良好用户体验的同时,不牺牲安全性。

标签:CSRF