广告 JS SDK 跨域与安全合规开发
开发投放广告的 JS SDK 并支持百度、抖音等第三方接入,核心要解决 跨域请求合法性、接口安全、兼容性 和 接入便捷性 四大问题。以下是需要做的具体调整,分为 SDK 前端封装、服务端配置、安全与合规 三大部分:
一、SDK 前端封装(核心:让第三方接入简单,且请求无跨域)
1. 封装跨域请求逻辑(兼容复杂请求)
广告接口(如获取广告素材、上报曝光 / 点击)通常是 POST 请求(需传递广告位 ID、用户设备信息等),且可能携带自定义头(如 X-AppId 用于验证第三方身份),属于 “复杂请求”,需确保请求格式符合服务端 CORS 配置。
// 广告SDK核心代码(your-ad-sdk.js)
class AdSDK {
constructor(options) {
// 第三方接入时必须传入的参数(用于安全验证)
this.appId = options.appId; // 分配给百度/抖音的唯一标识
this.adSlotId = options.adSlotId; // 第三方页面的广告位ID
// 你的广告接口域名(必须是HTTPS)
this.apiDomain = 'https://ad.yourcompany.com';
// 配置请求头(需服务端CORS允许)
this.headers = {
'Content-Type': 'application/json',
'X-AppId': this.appId, // 自定义头:用于服务端验证第三方身份
'X-Sdk-Version': '1.0.0' // SDK版本,便于问题排查
};
}
// 核心方法:请求广告素材
async fetchAd() {
try {
const url = `${this.apiDomain}/api/ad/fetch`;
const response = await fetch(url, {
method: 'POST',
headers: this.headers,
body: JSON.stringify({
adSlotId: this.adSlotId,
// 可选:传递第三方页面的环境信息(需用户授权)
pageUrl: window.location.href,
userAgent: navigator.userAgent
}),
credentials: 'omit' // 广告SDK通常无需携带Cookie,避免跨域凭证问题
});
if (!response.ok) {
// 区分跨域错误和业务错误(跨域错误时response.status可能为0)
const errorMsg = response.status === 0
? '跨域请求失败,请检查服务端CORS配置'
: `广告请求失败:${response.statusText}`;
throw new Error(errorMsg);
}
const adData = await response.json();
// 渲染广告(插入到第三方页面的广告位)
this.renderAd(adData);
return adData;
} catch (error) {
// 暴露错误给第三方(通过回调函数)
if (this.options.onError) this.options.onError(error);
console.error('AdSDK错误:', error);
}
}
// 渲染广告到第三方页面
renderAd(adData) {
const adContainer = document.getElementById(this.options.containerId);
if (!adContainer) {
throw new Error('未找到广告容器,请检查containerId');
}
// 渲染逻辑(如插入图片/视频/HTML,避免样式冲突)
adContainer.innerHTML = `
<div class="your-ad-sdk-container"> <!-- 命名空间隔离样式 -->
<a href="${adData.clickUrl}" target="_blank">
<img src="${adData.imgUrl}" alt="${adData.title}">
</a>
</div>
`;
// 上报曝光事件
this.reportImpression(adData.adId);
}
// 上报广告曝光(也是跨域请求)
async reportImpression(adId) {
await fetch(`${this.apiDomain}/api/ad/impression`, {
method: 'POST',
headers: this.headers,
body: JSON.stringify({ adId, adSlotId: this.adSlotId })
});
}
}
// 暴露全局变量,方便第三方接入
window.AdSDK = AdSDK;
2. 接入便捷性优化(降低第三方接入成本)
- 提供简单的初始化方式:第三方只需传入
appId、广告位 ID、容器 ID 即可。
<!-- 百度/抖音页面接入示例 -->
<div id="ad-container"></div> <!-- 广告容器 -->
<script src="https://sdk.yourcompany.com/your-ad-sdk.js"></script>
<script>
// 初始化SDK(appId需向你申请)
const adSdk = new AdSDK({
appId: 'baidu-123456', // 百度的专属appId
adSlotId: 'baidu-homepage-banner', // 百度页面的广告位ID
containerId: 'ad-container', // 广告渲染的容器ID
onError: (error) => { console.log('广告加载失败:', error); } // 错误回调
});
// 加载广告
adSdk.fetchAd();
</script>
- 异步加载 SDK:允许第三方通过
async加载,避免阻塞页面。
<script async src="https://sdk.yourcompany.com/your-ad-sdk.js" onload="initAdSdk()"></script>
- 样式隔离:广告 DOM 使用独立类名(如
your-ad-sdk-xxx),避免与第三方页面的 CSS 冲突。
二、服务端配置(核心:确保跨域请求合法,接口安全)
1. 跨域配置(CORS):允许百度、抖音域名访问
服务端必须配置 CORS 响应头,明确允许第三方域名(如百度 https://www.baidu.com、抖音 https://www.douyin.com)的跨域请求,同时处理预检请求(OPTIONS)。
以 Node.js + Express 为例:
const express = require('express');
const app = express();
app.use(express.json());
// 允许的第三方域名白名单(提前与百度、抖音确认其实际域名)
const ALLOWED_ORIGINS = [
'https://www.baidu.com',
'https://baidu.com',
'https://www.douyin.com',
'https://douyin.com'
];
// CORS中间件
app.use((req, res, next) => {
const origin = req.headers.origin;
// 只允许白名单内的域名跨域
if (ALLOWED_ORIGINS.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
// 允许的请求方法(广告SDK用POST,需包含OPTIONS预检)
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
// 允许的请求头(需包含SDK中自定义的X-AppId等)
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-AppId, X-Sdk-Version');
// 广告SDK通常无需携带Cookie,关闭credentials
res.setHeader('Access-Control-Allow-Credentials', 'false');
// 预检结果缓存1小时(3600秒),减少重复预检
res.setHeader('Access-Control-Max-Age', '3600');
// 处理预检请求(OPTIONS)
if (req.method === 'OPTIONS') {
return res.sendStatus(204); // 无需返回body
}
next();
});
// 广告接口:获取广告素材
app.post('/api/ad/fetch', (req, res) => {
const { appId, adSlotId } = req.body;
// 验证appId合法性(防止恶意调用)
if (!verifyAppId(appId)) {
return res.status(403).json({ code: 403, msg: '无效的appId' });
}
// 业务逻辑:根据adSlotId返回对应的广告素材
const adData = getAdBySlotId(adSlotId); // 自定义函数:从数据库查询广告
res.json({ code: 200, data: adData });
});
// 启动服务(必须用HTTPS,否则第三方HTTPS页面会拦截)
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('your-ssl-key.pem'),
cert: fs.readFileSync('your-ssl-cert.pem')
};
https.createServer(options, app).listen(443, () => {
console.log('广告服务启动:https://ad.yourcompany.com');
});
2. 接口安全加固(防止滥用和攻击)
-
appId 验证:为百度、抖音分配唯一
appId,服务端校验X-AppId头,拒绝无效appId的请求(防止其他网站盗用 SDK)。 -
请求频率限制:对每个
appId限制每秒请求次数(如 100 次 / 秒),防止 DDoS 攻击。
// 伪代码:使用redis记录请求次数
const rateLimit = (req, res, next) => {
const appId = req.headers['x-appid'];
const key = `rate_limit:${appId}`;
redis.incr(key, (err, count) => {
if (count === 1) redis.expire(key, 1); // 1秒过期
if (count > 100) { // 超过100次/秒
return res.status(429).json({ msg: '请求过于频繁' });
}
next();
});
};
app.use('/api/ad', rateLimit); // 对广告接口应用限流
- 广告素材域名白名单:广告图片 / 视频的 URL 域名(如
https://img.yourcompany.com)需配置 CORS,允许第三方域名加载(否则图片可能无法显示)。
三、安全与合规(避免第三方平台拒绝接入)
1. 必须使用 HTTPS
-
百度、抖音等正规平台的页面都是 HTTPS,若你的 SDK 或广告接口用 HTTP,会被浏览器拦截(混合内容错误),导致广告无法加载。
-
申请可信 CA 证书(如 Let’s Encrypt 免费证书),确保
sdk.yourcompany.com(SDK 地址)和ad.yourcompany.com(接口地址)均为 HTTPS。
2. 隐私合规(关键!否则可能被下架)
-
用户数据收集限制:SDK 仅收集必要信息(如广告位 ID、页面 URL),禁止收集敏感信息(如用户 Cookie、地理位置),除非第三方已获得用户授权。
-
提供合规文档:向百度、抖音提供《SDK 隐私政策》,说明数据收集目的、方式,承诺不滥用数据(符合 GDPR、国内《个人信息保护法》)。
-
可选:支持隐私开关:允许第三方通过配置关闭数据收集(如
{ collectData: false })。
3. 广告内容合规
-
广告素材需符合第三方平台的规范(如百度禁止虚假广告,抖音禁止低俗内容),避免 SDK 被第三方封禁。
-
提供广告审核机制,确保返回给第三方的广告内容合法。
四、测试与接入支持
-
提供测试环境:给百度、抖音提供测试
appId和测试接口(https://test-ad.yourcompany.com),方便其联调。 -
错误排查工具:SDK 中添加详细日志(如
console.log('[AdSDK] 开始请求广告...')),服务端记录请求日志(包含appId、adSlotId、错误原因),便于快速定位跨域或业务问题。 -
接入文档:明确说明
appId申请流程、初始化参数、错误码含义(如403表示appId无效,500表示服务端错误)。
总结
核心调整围绕 “跨域合法” 和 “安全合规”:
-
前端 SDK 封装跨域请求,简化接入;
-
服务端配置精准的 CORS 白名单,处理预检请求,并用 HTTPS;
-
通过
appId验证、限流确保接口安全; -
遵守隐私和广告内容规范,避免第三方平台拒绝接入。
这样百度、抖音等第三方接入时,只需简单配置即可加载广告,且无跨域或安全风险。