腾讯云图数据可视化支持用户通过自己的 API 提供实时数据,支持浏览器端发起请求和服务器端发起请求两种。
浏览器端发起请求
注意
由浏览器端发起的请求为跨域请求,接口的实现需要满足下文的要求。
下图由接口
https://127.0.0.1/api
举例,文章后面会给出代码。设置数据源
接口的实现
返回 HTTP 响应头以支持浏览器端跨域发起请求。
Access-Control-Allow-Credentials: true
:勾选需要 Cookie,需要返回。Access-Control-Allow-Origin: https://yuntu.cloud.tencent.com
:需要按照 HTTP 请求的协议头 Origin 来源返回,如果请求https://v.yuntus.com
页面(Origin: https://v.yuntus.com
),则需要对应返回Access-Control-Allow-Origin: https://v.yuntus.com
。
注意
Chrome 和 Edge 浏览器跨域携带 Cookie,需要额外设置2个 Cookie:
SameSite = None;
、secure=true;
同时,建议使用 https 协议访问。返回数据
返回数据需要满足以下条件:
JSON 格式。
仅包含完整数据(不包含返回码)。
接口示例代码
接口
https://127.0.0.1/api
的 NodeJs 示例代码( 支持 node 8 及以上版本运行):const express = require('express')const app = express()const PORT = 443const BAR_DATA_MAX = 120const CORS_ALLOW_ORIGIN = ['http://yuntu.cloud.tencent.com','https://yuntu.cloud.tencent.com','http://v.yuntus.com','https://v.yuntus.com']function randomNumber(max) {return parseInt(Math.random() * max, 10)}app.get('/api', (req, res, next) => {const origin = req.header('origin')if (CORS_ALLOW_ORIGIN.includes(origin)) {res.header('Access-Control-Allow-Origin', origin)res.header('Access-Control-Allow-Credentials', true)}res.json([{x: '一月',y: randomNumber(BAR_DATA_MAX),s: 's1'},{x: '二月',y: randomNumber(BAR_DATA_MAX),s: 's1'},{x: '三月',y: randomNumber(BAR_DATA_MAX),s: 's1'},{x: '四月',y: randomNumber(BAR_DATA_MAX),s: 's1'},{x: '五月',y: randomNumber(BAR_DATA_MAX),s: 's1'}])})app.listen(PORT)
服务器端发起请求
注意
接口不支持跨域,需勾选服务器发起请求。已勾选服务器发起请求时,勾选需要 Cookie 无效(无法传递接口域名下的 Cookie)。
由服务器端发起请求,接口响应数据格式与浏览器端发起请求一致,但需要接口支持外网访问,即需勾选服务器发起请求。
使用访问密钥
如果 API 是公网地址,会导致 API 暴露在公网,下面有两种方法可解决其他人能调用接口查看数据的隐患。
1. 如果是服务器端发起请求,可以设置数据代理 IP 白名单。
2. 使用访问密钥,在 API 里实现鉴权。
创建密钥
选择访问密钥
计算与验证签名
可以通过浏览器开发工具看到,服务器使用计算出的签名向设置的 API 发起了请求。
上图中请求签名后的 API URL 如下:
https://127.0.0.1/api?TcvSecretId=zUYUtjPu2Kob9xxxxxxxxrbkau3FEq6pqxe6&TcvSignature=Ds3cyyhQCo%2FTvdyUi3%2BmuPj2DQKZXMpIRwTqvMXPiRE%3D&TcvTimestamp=1583399912&TcvNonce=302190
参数拆分如下:
TcvSecretId
TcvTimestamp
TcvNonce
TcvSignature
计算签名内容
将非 TcvSignature 的参数按照名称升序排列拼接,格式为 key1=value1&key2=value2&key3=value3,这里排序后如下:
TcvNonce=302190&TcvSecretId=zUYUtjPu2Kob9xxxxxxxxrbkau3FEq6pqxe6&TcvTimestamp=1583399912
计算签名
使用 HMAC-SHA256 算法计算签名,NodeJs 的计算代码如下:
// 签名内容const signStr='TcvNonce=302190&TcvSecretId=zUYUtjPu2Kob9xxxxxxxxrbkau3FEq6pqxe6&TcvTimestamp=1583399912'// 刚才获取的签名密钥const secretKey = 'xrck1Mgi0IxVjS08B3HsECajxxxxxxxx'// 采用 sha256 算法签名,签名结果再转 base64const signature = crypto.createHmac('sha256', SecretKey).update(signStr).digest().toString('base64')// 将 signature 与 TcvSignature 对比,结果一致则确认是来自云图的请求
签名示例代码
加上校验签名的完整 NodeJs 示例代码如下( node 8 及以上版本运行通过):
const express = require('express')const crypto = require('crypto')const app = express()const PORT = 443const BAR_DATA_MAX = 120const CORS_ALLOW_ORIGIN = ['http://yuntu.cloud.tencent.com','https://yuntu.cloud.tencent.com','http://v.yuntus.com','https://v.yuntus.com']const secretKey = 'xrck1Mgi0IxVxxxxjO01RYfGW'function randomNumber(max) {return parseInt(Math.random() * max, 10)}function isSignatureOK(receivedSignature, query) {// TcvSignature 不参与签名delete query.TcvSignatureconst params = Object.entries(query)// 升序排列字段params.sort(([key1], [key2]) => {if (key1 > key2) {return 1}if (key1 < key2) {return -1}return 0})// 生成签名字符串const signStr = params.map(kv => kv.join('=')).join('&')console.log(signStr)// 计算签名const signature = crypto.createHmac('sha256', secretKey).update(signStr).digest().toString('base64')console.log(`signature=${signature}, receivedSignature=${receivedSignature}`)// 比较签名结果是否相同return signature === receivedSignature}app.get('/api', (req, res, next) => {if (!isSignatureOK(req.query.TcvSignature, req.query)) {// 签名不同,返回没有权限res.status(401).end()return}// 设置响应 CORS 头const origin = req.header('origin')if (CORS_ALLOW_ORIGIN.includes(origin)) {res.header('Access-Control-Allow-Origin', origin)res.header('Access-Control-Allow-Credentials', true)}res.json([{x: '一月',y: randomNumber(BAR_DATA_MAX),s: 's1'},{x: '二月',y: randomNumber(BAR_DATA_MAX),s: 's1'},{x: '三月',y: randomNumber(BAR_DATA_MAX),s: 's1'},{x: '四月',y: randomNumber(BAR_DATA_MAX),s: 's1'},{x: '五月',y: randomNumber(BAR_DATA_MAX),s: 's1'}])})app.listen(PORT)