在进行多系统集成的时候,是不是经常会被各种集成问题给难道,集成很多细节点没有注意导致集成失败,引发了一系列的疑问:

  1. 为什么openresource集成请求参数传递失败?
  2. 为什么令牌token令牌解析失败?
  3. 为什么令牌token登录之后参数传递失败
  4. ......

下面我们就从基础出发,深入探究基础的秘密。


一、openresource集成问题

注意本节介绍的内容只是单独使用openresource时的注意事项,默认均是已完成单点登录的集成。

1、选择正确的传参字符串json

在资源集成的时候,openresource请求的paramsInfo、paramsInfoBase64两个参数是用来传递参数,传递的参数信息需要一个json格式的字符串。在不同的资源里其传递的字符串格式是不一样的,所以需要根据不同的资源选择正确的参数json串,选择正确的传参字符串参数传递的问题可以解决很多参数传递失败的问题。

http://localhost:18080/smartbi/vision/openresource.jsp?resid=I402881e5019608c408c4fb7801960e2ec9b42d3d&paramsInfo=

各资源参数选择如下

电子表格、旧即席查询、旧透视分析
单参数: [{"name":"发货区域", "value":"华北","displayValue":"华北"}]

多参数: [{"name":"发货区域", "value":"华北","displayValue":"华北"},{"name":"订单年份", "value":"2020","displayValue":"2020"} ]

多选参数:[{"name":"订单年份", "value":"2020,2021","displayValue":"2020,2021"}]
仪表盘、新即席查询、新透视分析
单参数: {"订单年份":{"values":["2020"]}}

多参数: {"订单年份":{"values":["2020"]}, {"订单月份":{"values":["2020-01"]} }

多选参数:{"订单年份":{"values":"2020,2021" }} 或 {"订单年份":{"values":["2020","2021"]}} 或 [{"name":"订单年份", "value":["2020","2021"],"displayValue":["2020","2021"] }]

注意:仪表盘除去以上格式之外也支持使用电子表格的参数传递方式


多选参数注意事项
  1. 由于电子表格里参数是通过英文逗号进行分割多个参数,所以在多个参数传递的时候只能写成"value":"2020,2021" ,若业务数据中不规范有逗号,则会解析为两个参数,由于电子表格的架构实际目前无法解决该问题。
  2. 仪表盘上的参数逻辑不同于电子表格,不仅可以写成"values":"2020,2021"和电子表格的逻辑一致,还额外提供了数组的写法"values":["2020","2021"]。此时需要注意,如果使用数组的写法,数组中的每一项表示一个参数,如写为"values":["2020,2021"]则表示传递的参数是2020,2021的整体,而不是拆分成两个参数,可解决业务数据中有英文逗号的问题。

2、url转码

在选择正确的参数之后,拼接得到最终访问资源的请求,具体可以参考如下示例。那么如下链接如果直接访问会有哪些问题呢?

http://localhost:18080/smartbi/vision/openresource.jsp?resid=I402881e5019608c408c4fb7801960e2ec9b42d3d&paramsInfo=[{"name":"发货区域", "value":"华北","displayValue":"华北"}]

HTTP状态 400 - 错误的请求

在参数的链接中我们可以发现其存在中文数据,同时也存在特殊字符,访问时就很可能会出现如下Tomcat报错"HTTP状态 400 - 错误的请求"的报错,在报错的描述信息中可以看到原本paramsInfo后面加的参数变成了一堆编码,其错误描述中大致的意思是请求信息中有无效的字符导致的。

出现这个问题是因为tomcat高版本严格按照RFC 3986规范解析地址。该规范只允许包含a-zA-Z 0-9 - _ . ~ 以及所有保留字符 ! * ’ ( ) ; : @ & = + $ , / ? # [ ]    通常情况下参数中使用的保留字符并不会识别为参数的内容,而是识别为请求相关的内容

所以当请求中有除去上述的特殊字符或中文的时候会被Tomcat的限制,会识别不到请求所以抛出400的错误状态码。那实际遇到这种问题的时候又该如何解决呢?

方案一:使用 encodeURIComponent 函数进行url转码

url转码是浏览器请求时面对不合法的请求时进行转化的重要手段,实现的原理是将需要转码的字符,按指定编码方式(默认使用UTF-8编码)转化为字节流,然后每个字节按16进制表示,最后添加%组成一个percent编码,最终就会变成%+16进制+16进制的格式

例如:你好

此时转码之后的字符就可以被浏览器识别,所以只需要将paramsInfo后面的参数内容全部进行url转码之后拼接到请求中就可以被Tomcat识别。如下这里我们直接在浏览器控制台中使用encodeURIComponent,得到的结果拼接成完整的url请求具体如下。此时就可以正常访问并且传参成功,在实际开发场景中只需要将拼接之后参数json进行字符串格式化之后再拼接到请求中即可完成参数传递。

http://localhost:18080/smartbi/vision/openresource.jsp?resid=I402881e5019608c408c4fb7801960e2ec9b42d3d&paramsInfo=%5B%7B%22name%22%3A%22%E5%8F%91%E8%B4%A7%E5%8C%BA%E5%9F%9F%22%2C%22value%22%3A%22%E5%8D%8E%E5%8C%97%22%2C%22displayValue%22%3A%22%E5%8D%8E%E5%8C%97%22%7D%5D


方案二:修改tomcat的server.xml配置文件

在Tomcat/conf/server.xml文件中,可以在请求端口的配置中添relaxedPathChars="|{}[],%"relaxedQueryChars="|{}[],%" 两个参数同样也可解决该问题,特殊情况下还需要多添加一个URIEncoding="UTF-8" 的参数,避免因为字符集导致的转码异常。

注意:若为一键安装部署的Tomcat,默认情况下这些参数是已经添加过的。


测试html文件:open.html (测试资源仅提供测试验证以及参考,实际的集成时请以实际场景为准)

下载后双击打开html文件,输入需要集成资源的openresource地址之后,选择不加密,然后在下方添加需要传递的参数以及参数值,最后点击拼接url之后即可得到完整的集成链接,访问拼接的地址可验证参数是否正常传递成功。

3、base64加密

在实际生产的过程中使用openresource.jsp集成资源的时候,除去会遇到Tomcat无法识别字符的问题之外,还可能或有参数过多请求头过长请求被拦截、请求中大量的特殊字符被网络安全策略拦截等各式各样的场景,那是否还有其他办法规避掉这些问题?

base64加密流程

(1)配置好需要加密的参数,这里以发回区域参数为例,如:[{"name":"发货区域", "value":"华北","displayValue":"华北"}]

(2)用加密算法进行加密,加密要点如下:

  1. 使用base64加密是需要以UTF-8的字符集来进行加密
  2. 建议使用sun.misc.BASE64Encoder类来加密处理,也可以使用js代码来处理,但是不作为首要推荐,因为加密不一致时解密可能会异常。
  3. 加密完成后需要对加密后的字符串进行转码

(3)使用encodeURLComponent对加密后的字符串进行转码。

base64参考示例

java代码实现参考:

// 对格式化的json字符串sourceString使用UTF-8字符集进行加密
String targetTempString = new sun.misc.BASE64Encoder().encode(sourceString.getBytes(StandardCharsets.UTF_8));
// 对加密后的字符串进行url转码
String targetString = java.net.URLEncoder.encode(targetTempString);

js代码实现参考:

// 1、对参数进行字符串转换,json格式化
let jsonString = JSON.stringify(params);
// 2、对字符串的数据转为二进制
let utf8Bytes = new TextEncoder().encode(jsonString);
// 3、用二进制的数据转为utf-8之后进行base64编码
let base64Encoded = btoa(String.fromCharCode(...utf8Bytes));
// 4、对base64编码进行url转义
return encodeURIComponent(base64Encoded)

最终访问地址拼接示例如下:

http://localhost:18080/smartbi/vision/openresource.jsp?resid=I402881e5019608c408c4fb7801960e2ec9b42d3d&W3sibmFtZSI6IuS6p%2BWTgeWQjeensCIsICJ2YWx1ZSI6IuWNjuWMlyIsImRpc3BsYXlWYWx1ZSI6IuWNjuWMlyJ9XQ%3D%3D


测试页面

base64加密:https://www.bejson.com/enc/base64/

url转码https://www.sojson.com/encodeurl.html


测试html文件:open.html (测试资源仅提供测试验证以及参考,实际的集成时请以实际场景为准)

下载后双击打开html文件,输入需要集成资源的openresource地址之后,选择不加密,然后在下方添加需要传递的参数以及参数值,最后点击拼接url之后即可得到完整的集成链接,访问拼接的地址可验证参数是否正常传递成功。

4、post请求提交

前面介绍的集成方式都是通过get请求来进行集成的,集成的时候有发现存在各种浏览器的限制问题,有没有什么方式能直接绕开这些限制的传参方式呢?

在Smartbi中支持通过form表单的形式来提交post请求来进行集成,使用post请求来进行集成时基本上可以规避很多get请求时会出现的问题,实现的方式可以参考下面的简单示例。

<div style="width:100%;height:100%;">
	<!-- 1、表单的响应区域 -->
    <iframe id="myFrame" width="100%" height="100%" allowTransparency name="myFrame" />
</div>

<!-- 2、表单部分,在表单中设置post请求提交的地址、参数、参数值等信息 -->
<form id="submitForm" method="post" action="http://localhost:18080/smartbi/vision/openresource.jsp" target="myFrame">
	<!-- 3、请求参数部分 -->
    <input  id="resid" type="text" name="resid" value="I402881e5019608c408c4fb7801960e2ec9b42d3d">
	<!-- 3.1、设置集成资源的参数信息 -->
    <!-- 方式1 -->
    <input  id="paramsInfo" type="text" name="paramsInfo" value='[{"name":"发货区域","value":"东北",displayValue:"东北"}]'>
    <!-- 方式2 -->
    <input type="text" name="param.发货区域" value="东北">
    <input type="text" name="paramDisplay.发货区域" value="东北">

    <!-- 3.2、openresource的其他参数 -->
	<input id="refresh" value="true" type="text" name="refresh">
</form>

<script language='javascript'>
   // 4、提交表单
    var submitForm = document.getElementById("submitForm");
    // 新窗口打开
    //submitForm.target ="_blank";
    // 提交表单
    submitForm.submit();
</script>


post集成示例拆解

示例总共有四个部分组成,下面我们来逐步拆解

(1)设置提供表单的响应区域

在实际的集成场景中,比较常见的就是点击了某些类似资源树上的节点之后再在页面上展示集成资源的结果,这里我们需要使用<form>标签的target参数来指定显示的位置,可以修改target的参数值为不同的值来得到不同的响应效果,如:

<iframe id="myFrame" width="100%" height="100%" allowTransparency name="myFrame" />
(2)form表单部分

在<form>标签中需要设置的参数如下:

这样设置完成之后当form表单提交的时候就可以用post请求的方式给openresource请求,并按照指定的方式展示响应资源。


<form id="submitForm" method="post" action="http://localhost:18080/smartbi/vision/openresource.jsp" target="myFrame">

</form>


(3)参数部分

在<form>标签内,需要使用input标签来写需要传递的参数内容,这里的input标签必须要需要设置的内容如下:

在一个input标签中name和value需要同时存在,否则传参异常。多个openresource请求的参数需要用多个input标签


<input  id="resid" type="text" name="resid" value="I402881e5019608c408c4fb7801960e2ec9b42d3d" />
<input  id="paramsInfo" type="text" name="paramsInfo" value='[{"name":"发货区域","value":"东北",displayValue:"东北"}]' />


使用post请求来给打开资源传参的时候,除去可以在input标签中设置name属性为paramsInfoz然后value中直接等于传参的json之外,还支持简易版本的传参方式,两种传参方式具体如下。注意两种传参方式选择其一即可。


方式一:使用paramsInfo传参

<input  id="paramsInfo" type="text" name="paramsInfo" value='[{"name":"发货区域","value":"东北",displayValue:"东北"}]'>

实际post请求时的参数负载


方式二:使用param.参数名传参


<input type="text" name="param.发货区域" value="东北">
<input type="text" name="paramDisplay.发货区域" value="东北">

实际post请求时的参数负载


(4)表单提交

这里form表单提交是通过使用js代码来进行控制,这种提交方式相对于在form表单中添加submit类型的<button>标签的提交按钮更灵活,毕竟在实际的集成场景很难控制让用户点击提交表单的按钮,另外就是使用js代码提交时候还能在提交前去设置form表单的一些属性。


// 获取form标签
var submitForm = document.getElementById("submitForm");
// 在设置的iframe中打开
submitForm.target = "myFrame"
submitForm.submit();


参考示例页面:postHtml.html (测试资源仅提供测试验证以及参考,实际的集成时请以实际场景为准)

使用时请用文本工具打开并修改里面的服务器地址信息以及集成的参数资源。


调优部分

由于form表单本身需要在html页面上创建<form>标签以及传参的<input>标签,在实际基础场景中集成参数不会是一直固定的,所以在实际集成时,可以在html上预留好ifarme标签位置之后,使用js代码根据实际需要来动态创建标签,实际更符合业务需求。

参考示例页面:post.html (测试资源仅提供测试验证以及参考,实际的集成时请以实际场景为准)


二、令牌token单点登录

在Smartbi中使用令牌token进行单点登录的时候,往往会因为在加密时某一个步骤操作不当导致出现各式各样的问题,可能遇到的问题如下:

问题1:令牌token单点登录:解析令牌失败

问题2:其他提示信息,如令牌超时、用户不存在等

问题3:报错自定义的提示信息

如果在系统选项中设置了自定义的异常提示,前台的提示信息就不是很明显,此时可以打开系统监控然后监控日志,就能看到如下日志,可确认是上述问题中的那一个

image2024-12-31_9-24-27.pngimage2024-12-31_9-17-27.png


在使用令牌token的时候要如何避免这些问题,在生成和使用令牌的时候又要注意哪些问题呢?下面我们就用一个代码示例来确认来讲解:

public class SmartbiToken {
    public static void main(String[] args) throws Exception {
        // 1、准备加密参数
        String secretKey = "12345678";// 密钥
        long timestamp =  new java.util.Date().getTime();// 必需是毫秒数的时间戳
        String userName = "admin";  // 用户名,可基于业务动态获取
        String password = "admin";  // 用户密码(可选),可基于业务动态获取

        // 准备加密的JSON串,用户密码可选
        String loginInfo= "{\"username\":\"" + userName + "\",\"password\":\"" + password + "\",\"timestamp\":" + timestamp + "}";

        // 2、进行加密
        String token  = CryptoUtil.encrypt(AlgorithmType.DES, loginInfo, secretKey);
        // 3、链接转码
        String encodedToken = java.net.URLEncoder.encode(token, "UTF-8");
        // 4、拼接登录链接
        String url = "http://localhost:8080/smartbi/vision/loginByToken?smartbiToken=" + encodedToken;

        // 5、拼接登录后的跳转请求
        String targetPath = "openresource.jsp?resid=I2c94ea86298cbe6c01298cfd9ba900fa";
        targetPath = java.net.URLEncoder.encode(targetPath, "UTF-8");
        String endUrl = url + "&targetPath=" + targetPath;
        System.out.println(endUrl);
    }
}


(1)加密参数部分

加密参数主要由四部分组成

(2)进行加密

加密部分主要有主要有两种算法DES加密和AES-GCM加密

注意秘钥长度,如果秘钥不是8个字符的,使用AES-GCM加密加密会报错如下:The key length must be 16 bytes


加密注意事项:

下面使用相同用户、密码、时间戳、令牌,算法不同来加密之后得到的结果参考如下:

// V10版本加密之后的令牌
54f296bd4649b0d44628a5c32faee32b8dcc1603e65f5926bcff5ecd042f5176ebefa28274987245be277798ca3175c6ac93f1e0ae92bd16841e7713b31959e6edf967ea6f13b4bc
// V11版本DES算法加密之后的令牌
T0_VPKWvUZJsNRGKKXDL67jK43MFgPmX1kmvP9ezQQvUXbr76KCdJhyRb4nd5jKMXXGrJPx4K6SvRaEHncTsxlZ5u35Z+pvE7S8
// V11版本AES算法加密之后的令牌
T1_DNEJke1569+HbJtUBb9a4m9l1ZtI4UR4CGUfwn+mMsuuw9PkNKoHAchSpoXDxc0CsFgVwVMlibe9uOa+QliqOrNxg05qz7ZzDlHNLYCsvi6kmliCNBPio2MstouYIw==


(3)令牌转码

拿到令牌之后我们可以看到如下令牌他是存在特殊字符的,又因为令牌单点一般都是通过get请求来提交令牌参数,此时就可能会出现上一章节的相同问题:2、url转码。此时就需要对令牌进行url转码,如果是Java系统可以使用 java.net.URLEncoder.encode(token, "UTF-8")进行转码,js代码则可以使用encodeURIComponent函数来进行转码。如果说不转码的情况下拼接地址访问之后,同样会出现问题1“解析令牌失败”的问题。


(4)拼接登录链接

拼接的单点地址需要使用loginByToken,然后smartbiToken参数用来指定token:"http://localhost:8080/smartbi/vision/loginByToken?smartbiToken=" + encodedToken;

(5)拼接登录后的跳转请求

登录完成之后,需要使用targetPath参数来指定跳转之后需要拼接的地址,这个地址在拼接到最终请求之前同样需要进行url转码,否则在openresource的请求里,当设置集成报表的参数时,会将集成的参数当成是loginByTokenq请求的参数,导致openresource请求传参失败,具体可以参考如下图中的请求参数信息。


最终拼接访问的请求如下:

http://localhost:18080/smartbi/vision/loginByToken?smartbiToken=T1_DNEJke1569%2BHbJtUBb9a4m9l1ZtI4UR4CGUfwn%2BmMsuuw9PkNKoHAchSpoXDxc0CsFgVwVMlibe9uOa%2BQliqOrNxg05qz7ZzDlHNLYCsvi6kmliCNBPio2MstouYIw%3D%3D&targetPath=openresource.jsp%3Fresid%3D2472fece6b5e9162c5ecf31425ac3b6c%26paramsInfo%3D%255B%257B%2522name%2522%253A%2522%25E5%258F%2591%25E8%25B4%25A7%25E5%258C%25BA%25E5%259F%259F%2522%252C%2522value%2522%253A%2522%25E5%258D%258E%25E5%258C%2597%2522%252C%2522displayValue%2522%253A%2522%25E5%258D%258E%25E5%258C%2597%2522%257D%255D