系统集成技术接入指引
智能问数页面支持被灵活的嵌入至第三方平台中,可以在此对嵌入页面的呈现样式、展示元素进行自定义配置。
实现方式
通过 iframe 分享智能问数页面的方式嵌入;
分享支持 2 种方式:
- 公开分享:即所有互联网用户都能随意访问。
-
token 分享:即用户在访问发布的智能问数 iframe 嵌入页时需要通过 Sugar BI 提供的 Token 参数签名计算规则,生成访问 URL,用此 URL 访问时Sugar BI 会自动进行访问 URL 的 鉴权,鉴权通过之后才能访问智能问数页面,否则不能访问,以此来将Sugar BI智能问数的访问权限与用户的第三方系统权限体系进行集成。
token 分享方式更利于保证数据的安全,需要对用户传递的参数进行签名鉴权,保证计算得到的 URL 参数不能被更改,如果私自更改了传参,页面将无法访问。
集成步骤
公开分享的集成
新建公开分享智能问数 iframe 页面
在「组织管理——智能问数嵌入管理」中,新建嵌入页面,添加问数范围,右侧控制面板「逻辑设置——过程代码分享方式」选项选择「公开」

发布和查看智能问数 iframe 页面
点击「发布生效」后,系统会生成一个可访问的链接,可以使用这个链接直接访问智能问数页面

集成智能问数 iframe 页面
第三方系统集成时可直接嵌入此 URL 即可
token 分享的集成
新建 token 分享智能问数 iframe 页面
在「组织管理——智能问数嵌入管理」中,新建嵌入页面,添加问数范围,右侧控制面板「逻辑设置——过程代码分享方式」选项选择「通过 token 验证」 页面上会自动生成一个 token 字符串,用于后续集成中的签名生成和鉴权

发布和查看智能问数 iframe 页面
点击「发布生效」后,系统会生成一个 URL 注意:这个 URL 不能直接访问和使用智能问数

集成智能问数 iframe 页面
集成流程
- 确定需要签名计算的参数名(即不允许被篡改的参数,一般是访问者的 email 信息);
- 使用签名参数,token,和 3.2.2 中生成的 URL 计算生成最终访问智能问数页面的 URL;
- 使用上一步中计算得到的 URL 访问Sugar BI 智能问数页面,在访问过程中,Sugar BI 系统会自动进行鉴权;
- 如果鉴权通过,将带着该用户信息访问Sugar BI 智能问数页面;
- 当访问者修改了签名参数,再次访问此 URL 时,访问会被拒绝(例如参数zhangsan@baidu.com被修改为lisi@baidu.com,将无法访问)。
参数说明
| 参数名 | 参数说明 | 参数值 |
|---|---|---|
| shareID | shareID 是智能问数在分享时自动生成的 url 中的部分:
如:http://127.0.0.1:8000/gbi-iframe/1b89d8d621bca917dd481b965c216dbf 最后一段 1b89d8d621bca917dd481b965c216dbf 就是 shareID |
eg. 1b89d8d621bca917dd481b965c216dbf |
| token | 新建智能问数分享页面,右侧控制面板「逻辑设置——过程代码分享方式」选项选择「通过token验证」后页面上生成的token,可直接复制 | eg. 9zUfbrmDJhBhQt8qywjzasZmWNbHnOXg |
| sugar_sign_user_email |
需要加入签名的参数,其参数名需以 sugar_sign_ 开头,后面可以带任何有效的参数名字符; 不符合此签名参数规则的参数,将不会进行参数签名校验,允许修改参数值; 签名参数按升序排序。 |
eg. zhangsan@baidu.com |
示例代码
拿到上述参数后,就可以使用以下示例代码,进行智能问数页面的集成开发。
nodejs
1const crypto = require('crypto');
2const querystring = require('querystring');
3const signedQueryParamReg = /^sugar_sign_.*/; // 符合此正则表达式的参数是需要签名的。
4let token = 'OAMf7CvniOGgoNijH9mFHEHSAf7****';
5let shareID = '1827981ec07ac66f937a88c9e65f****'; // shareID详见前面文档中的说明
6const time = Date.now();
7
8const customParams = {
9 sugar_sign_user_email: 'zhangsan@baidu.com'
10};
11let signParamsStr = Object.keys(customParams)
12 .filter(paramName => customParams[paramName] && signedQueryParamReg.test(paramName))
13 .sort()
14 .map(param => `${param}=${customParams[param]}`)
15 .join('&');
16
17let stringToSign = [shareID, time];
18signParamsStr && stringToSign.push(signParamsStr);
19stringToSign = stringToSign.join('|');
20let signature = crypto.createHmac('sha256', token).update(stringToSign).digest().toString('base64');
21let queryParams = {
22 _sugar_time: time,
23 _sugar_signature: signature
24};
25
26Object.keys(customParams).forEach(paramName => {
27 queryParams[paramName] = customParams[paramName];
28});
29
30let url = `https://sugar.baidubce.com/gbi-iframe/${shareID}?${querystring.stringify(queryParams)}`;
31console.log(url);
PHP
1<?php
2 $token = "OAMf7CvniOGgoNijH9mFHEHSAf7****";
3 $shareID = "1827981ec07ac66f937a88c9e65f****"; // shareID详见前面文档中的说明
4 $time = time()*1000;
5 $customParams = array(
6 'sugar_sign_user_email'=>'zhangsan@baidu.com'
7 );
8 $sign_array = preg_grep("/^sugar_sign_.*/", array_keys($customParams));
9 sort($sign_array);
10 function toPlain($v)
11 {
12 global $customParams;
13 return "$v=$customParams[$v]";
14 };
15 $signParamsStr = join("&",array_map("toPlain",$sign_array));
16 $stringToSign = $shareID.'|'.$time.'|'.$signParamsStr;
17 $signature = urlencode(base64_encode(hash_hmac('sha256', $stringToSign, $token, true)));
18 $queryParams = join("&",array_map("toPlain",array_keys($customParams)));
19 $url = "https://sugar.baidubce.com/gbi-iframe/".$shareID."?_sugar_time=".$time."&_sugar_signature=".$signature."&".$queryParams;
20 echo $url;
21?>
22<iframe width=100% height=100% src="<?=$url?>"/>
java
1package com.company;
2import java.security.*;
3import java.util.Date;
4import javax.crypto.*;
5import javax.crypto.spec.SecretKeySpec;
6import org.apache.commons.codec.binary.Base64;
7import java.net.URLEncoder;
8public class TokenTest {
9 public static String getSignedUrl(String shareID, String token, String signParamsStr) {
10 Date date = new Date();
11 Long time = date.getTime();
12 String stringToSign = shareID + "|" + time;
13 if (!StringUtils.isEmpty(signParamsStr)) {
14 stringToSign = stringToSign + "|" + signParamsStr;
15 }
16 String signature = HMACSHA256(stringToSign.getBytes(), token.getBytes());
17 String url = "https://sugar.baidubce.com/gbi-iframe/" + shareID + "?_sugar_time=" + time + "&_sugar_signature=" + signature + "&" + signParamsStr;
18 return url;
19 }
20 /**
21 * 使用java原生的摘要实现SHA256加密。
22 * @param str加密后的报文。
23 * @return
24 */
25 public static String HMACSHA256(byte[] data, byte[] key) {
26 try {
27 SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA256");
28 Mac mac = Mac.getInstance("HmacSHA256");
29 mac.init(signingKey);
30 return URLEncoder.encode(byte2Base64(mac.doFinal(data)));
31 } catch (NoSuchAlgorithmException e) {
32 e.printStackTrace();
33 } catch (InvalidKeyException e) {
34 e.printStackTrace();
35 }
36 return null;
37 }
38 private static String byte2Base64(byte[] bytes){
39 return Base64.encodeBase64String(bytes);
40 }
41 public static void main(String[] args) throws Exception {
42 String signedQueryParamReg = "^sugar_sign_.*";
43 Map<String, Integer> customParams = new HashMap<>();
44 customParams.put("sugar_sign_user_email", 'zhangsan@baidu.com');
45 String signParamsStr = customParams.entrySet().stream().filter(entry -> Pattern.matches(signedQueryParamReg, entry.getKey()))
46 .map(entry -> entry.getKey() + "=" + entry.getValue())
47 .sorted()
48 .collect(Collectors.joining("&"));
49 System.out.println(getSignedUrl("shareID", "token", signParamsStr)); // shareID详见前面文档中的说明
50 }
51}
.NET
1using System;
2using System.Security.Cryptography;
3using System.Text.RegularExpressions;
4using System.Collections.Generic;
5using System.Linq;
6using System.Web;
7using System.Text;
8
9namespace sugarToken
10{
11 class Program
12 {
13 static void Main(string[] args)
14 {
15 var dic = new Dictionary<string, string>(); // 自定义参数。
16 dic.Add("sugar_sign_user_email", "zhangsan@baidu.com"); // sugar_sign_开头,需要签名。
17
18 // 分享页前缀,iframe分享shareID、token,自定义参数字典。
19 Console.WriteLine(GenerateUrl("https://sugar.baidubce.com/gbi-iframe/", "1827981ec07ac66f937a88c9e65f****", "OAMf7CvniOGgoNijH9mFHEHSAf7****", dic));
20 }
21 private static string GenerateUrl(string sugarBase, string shareID, string token, Dictionary<string, string> customParams)
22 {
23 string pattern = @"^sugar_sign_.*";
24 string timestamp = GetTimeStamp();
25
26 // 参数排序
27 Dictionary<string, string>.KeyCollection keyCol = customParams.Keys;
28 List<string> signKeys = new List<string>();
29
30 foreach (var item in keyCol.ToList())
31 {
32 if (Regex.IsMatch(item, pattern))
33 {
34 signKeys.Add(item);
35 }
36 }
37
38 // 按照key排序
39 signKeys = signKeys.OrderBy(k => k).ToList();
40
41 string paramsSignStr = signKeys.Aggregate("", (total, key) =>
42 {
43 if (total != "")
44 {
45 total += "&";
46 }
47 total += key + "=" + customParams[key];
48 return total;
49 });
50
51 string signStr = shareID + "|" + timestamp + "|" + paramsSignStr;
52
53 var encoding = new System.Text.ASCIIEncoding();
54 byte[] keyByte = encoding.GetBytes(token);
55 byte[] messageBytes = encoding.GetBytes(signStr);
56 string signature;
57 using (var hmacsha256 = new HMACSHA256(keyByte))
58 {
59 byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
60 signature = Convert.ToBase64String(hashmessage);
61 }
62
63 var paramDic = new Dictionary<string, string>();
64 paramDic.Add("_sugar_time", timestamp);
65 paramDic.Add("_sugar_signature", signature);
66
67 foreach (var item in customParams)
68 {
69 paramDic.Add(item.Key, item.Value);
70 }
71 return sugarBase + shareID + "?" + ParseToString(paramDic);
72 }
73 public static string GetTimeStamp()
74 {
75 TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
76 return Convert.ToInt64(ts.TotalMilliseconds).ToString();
77 }
78 static public string ParseToString(IDictionary<string, string> parameters)
79 {
80 IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
81 IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();
82
83 StringBuilder query = new StringBuilder("");
84 while (dem.MoveNext())
85 {
86 string key = dem.Current.Key;
87 string value = dem.Current.Value;
88 if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
89 {
90 query.Append(key).Append("=").Append(HttpUtility.UrlEncode(value)).Append("&");
91 }
92 }
93 string content = query.ToString().Substring(0, query.Length - 1);
94
95 return content;
96 }
97 }
98}
Python
1import time
2import hmac
3import base64
4import urllib.parse
5
6now = str(round(time.time() * 1000))
7token = b'OAMf7CvniOGgoNijH9mFHEHSAf7****'
8# shareID详见前面文档中的说明
9share_id = '1827981ec07ac66f937a88c9e65f****'
10
11# 自定义参数,其中sugar_sign_开头,需要签名
12custome_params = {
13 "sugar_sign_user_email": 'zhangsan@baidu.com'
14}
15# 按照key排序
16sorted_custome_params = {i: custome_params[i] for i in sorted(custome_params.keys())}
17sign_params_str = '&'.join([f"{k}={v}" for k, v in sorted_custome_params.items() if k.startswith("sugar_sign_")])
18string_to_sign = '|'.join([share_id, now, sign_params_str])
19signature = base64.b64encode(hmac.digest(token, string_to_sign.encode('utf-8'), 'sha256')).decode('utf-8')
20signature = urllib.parse.quote(signature)
21# 生成最终链接
22url = f"https://sugar.baidubce.com/gbi-iframe/{share_id}?_sugar_time={now}&_sugar_signature={signature}&" \
23 + urllib.parse.urlencode(sorted_custome_params)
24
25print(url)
结果验证
可以根据用户自己的技术栈选择对应的示例代码,生成的 URL 示例如下:
在 URL 的有效期内(分享未取消或被删除),如果修改了 sugar_sign_user_email 字段的值,将无法访问,可以避免数据泄漏风险
验证工具地址
Sugar BI 官方文档中提供了一个在线验证 URL 正确性的工具,可以使用此工具生成目标 URL 与用户在权限集成过程中代码生成的 URL 进行比对,来判断结果是否正确。您可以访问「签名 URL 计算工具」来生成带签名参数的 URL 示例。
