SPA(单页应用)调用公共REST API时面临的主要安全挑战是:由于前端代码完全暴露在客户端,传统的API密钥或凭证无法安全存储。需要采用特殊机制来确保只有你的SPA可以调用API,而其他客户端无法滥用。
原理:通过HTTP头部限制哪些源可以访问你的API
实现:
// Express示例
const express = require('express');
const app = express();
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://your-spa-domain.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
优点:
缺点:
原理:后端颁发短期有效的JWT令牌,SPA在每次请求时携带
实现:
// 颁发令牌
const jwt = require('jsonwebtoken');
const token = jwt.sign({ client: 'spa' }, 'your-secret-key', { expiresIn: '15m' });
// 验证中间件
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, 'your-secret-key', (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
优点:
缺点:
原理:每次请求生成动态签名,防止重放攻击
实现:
// 前端请求签名
const crypto = require('crypto');
function signRequest(method, path, body, timestamp, secret) {
const stringToSign = `${method}\n${path}\n${JSON.stringify(body)}\n${timestamp}`;
return crypto.createHmac('sha256', secret).update(stringToSign).digest('hex');
}
// 后端验证
function verifyRequest(req, res, next) {
const { signature, timestamp } = req.headers;
const calculatedSig = signRequest(req.method, req.path, req.body, timestamp, 'your-secret');
if (signature !== calculatedSig || Date.now() - timestamp > 300000) {
return res.sendStatus(403);
}
next();
}
优点:
缺点:
原理:通过反向代理添加特殊请求头,API只接受带此头的请求
实现(Nginx配置):
location /api {
proxy_pass http://api-server;
add_header X-Internal-Request "true";
}
# API服务器只接受带X-Internal-Request头的请求
优点:
缺点:
原理:为SPA部署客户端SSL证书
实现:
server {
listen 443 ssl;
ssl_client_certificate /path/to/ca.crt;
ssl_verify_client on;
location /api {
if ($ssl_client_verify != SUCCESS) { return 403; }
proxy_pass http://api-server;
}
}
优点:
缺点:
通过组合这些技术,可以有效限制只有你的SPA能够合法调用公共REST API,同时保持较好的用户体验和安全性平衡。
没有搜到相关的文章