• [优秀实践] 没文档也要扒源码让 ShardingSphere 支持 openGauss SCRAM 前端认证
    本人有幸参与了 openGauss 众智计划《ShardingSphere 对接 openGauss》,让我学习到了很多与 openGauss 数据库协议、安全等方面的知识。 本文主要记录如何在没有协议文档的情况下,根据 openGauss JDBC Driver 源码,让 ShardingSphere openGauss Proxy 支持 openGauss 的 SCRAM SHA-256 前端认证机制。 ## 前言 > 目标:让 ShardingSphere openGauss Proxy 支持以 SCRAM SHA-256 机制验证 openGauss 客户端的身份。 最近在做一个和 ShardingSphere Proxy openGauss 前端认证方式相关的 issue [ShardingSphere openGauss Proxy supports sha256 authentication method #13995](https://github.com/apache/shardingsphere/issues/13995)。 PostgreSQL 有一种前端认证方式是 `MD5` Password。客户端根据服务端提供的 salt 对密码进行 MD5 加密并发送,完成认证过程。 但 MD5 安全性比较有限,openGauss 前端认证推荐并默认使用 `SHA-256`。 虽然说是 `SHA-256` 认证,听起来就是与 PostgreSQL 的 MD5 认证大同小异,只是摘要的长度不一样,后来做起来才发现完全不是这样。openGauss 用的是 SCRAM,其中的哈希算法可以使用 `SHA-256` 或者国密算法。 在做这个 issue 之前,我不了解 SCRAM,其他安全相关知识也了解不深,实现过程也是费了一些心思。 ## 环境准备 ### openGauss 数据库与客户端 本文所使用的 openGauss 数据库及 `gsql` 均使用 Docker Image [enmotech/opengauss:2.1.0](https://hub.docker.com/r/enmotech/opengauss) openGauss JDBC Driver 使用 [org.opengauss:opengauss-jdbc:2.0.1-compatibility](https://mvnrepository.com/artifact/org.opengauss/opengauss-jdbc/2.0.1-compatibility) ```xml org.opengauss opengauss-jdbc 2.0.1-compatibility ``` ### openGauss 服务端配置 `enmotech/opengauss:2.1.0` 默认使用了 MD5 认证方式,需要修改各项配置为 `sha256` 并新建用户。 `postgresql.conf` 需要配置: ``` password_encryption_type = 2 ``` `pg_hba.conf` 需要配置: ``` host all all 0.0.0.0/0 sha256 ``` 配置完成后需要重启或执行 `gs_ctl reload` 生效。 最后创建一个新用户: ``` CREATE USER wuweijie PASSWORD 'Wuweijie@123'; ``` ## 没有文档?去扒一下客户端源码看看是怎么做的。 ### 确定 SHA-256 认证方式的枚举值 可能是一般的用户与开发者不会关心数据库协议,openGauss 的文档中没有包含对协议细节的说明,包括本次要做的 SCRAM SHA-256 前端认证,openGauss 文档说明了如何配置,但没有说明如何实现。 没有文档怎么办?就像之前实现 openGauss 批量协议一样,看一下客户端是怎么做的。 /gitee.com/opengauss/openGauss-connector-jdbc/blob/master/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java#L63> ```java private static final int AUTH_REQ_MD5 = 5; // 省略部分代码... private static final int AUTH_REQ_SHA256 = 10; ``` PostgreSQL MD5 认证方式的枚举值为 5,openGauss 所增加的 SHA-256 认证方式枚举值为 10。 ### 确定 Auth Request 数据包的内容 /gitee.com/opengauss/openGauss-connector-jdbc/blob/master/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java#L668> openGauss JDBC Driver 认证相关关键逻辑节选及本文注释说明: ```java case AUTH_REQ_SHA256: { // 省略部分代码... byte[] digest; int passwordStoredMethod = pgStream.receiveInteger4(); // 省略部分代码... if (passwordStoredMethod == PLAIN_PASSWORD || passwordStoredMethod == SHA256_PASSWORD) { String random64code = pgStream.receiveString(64); String token = pgStream.receiveString(8); byte[] result = null; // 由于 openGauss 最新的客户端都使用 3.51 的协议版本,以下分支只需关心最后的 else if (this.protocolVerion PROTOCOL_VERSION_350) { // 省略部分代码... } else if (this.protocolVerion == PROTOCOL_VERSION_350) { // 省略部分代码... } else { // 本次实现只看本分支 int server_iteration = pgStream.receiveInteger4(); result = MD5Digest.RFC5802Algorithm(password, random64code, token, server_iteration); } if (result == null) // 省略部分代码... pgStream.sendChar('p'); pgStream.sendInteger4(4 + result.length + 1); pgStream.send(result); pgStream.sendChar(0); pgStream.flush(); break; } else if (passwordStoredMethod == MD5_PASSWORD) { // 省略部分代码... } else { // 省略部分代码... } // 省略部分代码... } ``` > 代码中的方法 `RFC5802Algorithm` 也就是 `SCRAM`。 ### 大致明确 Auth Request 数据包的结构 综上,如果要把 ShardingSphere Proxy 伪装成 openGauss 数据库服务端进行 SCRAM SHA-256 认证,需要发送的数据包长这样: ``` Byte1('R') 表明这个消息是个认证请求。 Int32(88) 消息内容的长度(以字节为单位),包括长度自身,不包括消息类型。 Int32(10) 表明使用的认证方式为 SHA-256。 Int32(2) 表明用户的密码存储方式使用的是 SHA-256。 Byte64 random64code 长度为 64 的字符串,含义暂时不清楚,根据命名猜测类似 salt,参与 SCRAM 计算。 Byte8 token 长度为 8 的字符串,参与 SCRAM 计算。 Int32 server_iteration 一个整数,参与 SCRAM 计算。 ``` 从客户端接收到的数据包长这样: ``` Byte1('p') 表明这个消息是个认证响应。 Int32 消息内容的长度(以字节为单位),包括长度自身,不包括消息类型。 String 一个以 '\0' 结尾的字符串。 ``` ## Wireshark 抓包观察 从源码层面了解了数据包的格式后,通过 Wireshark 抓包偷窥 openGauss 客户端与数据库的交互过程。 当数据库收到客户端发送的 Startup 消息后,给客户端响应了一个类型为 `R` 的消息,要求客户端按照协议完成身份认证。以下为服务端发送给客户端的数据: > 因为 Wireshark 只支持解析 PostgreSQL 协议,openGauss 特有的消息在 Wireshake 里体现为 Malformed Packet。 下图中数据包的结构用不同颜色的方框标注,可以看出数据的结构跟前面明确的数据包结构一致。(看不清可以点开大图) ![Authentication Request](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/15/1657877602828640958.png) 再看以下客户端返回的信息,根据之前明确的数据包结构,客户端的认证消息里面只有一个字符串,所以客户端发送的是一个长度为 `64` 的字符串。(69 - 长度字段本身 4 - `'\0'` 结尾字符) ![Password Message](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/15/1657877648249516770.png) ## 实现过程 ### 创建对应的消息定义 Proxy 发送给客户端的认证请求节选: [OpenGaussAuthenticationSha256Packet.java](https://github.com/apache/shardingsphere/pull/14002/files#diff-7b4e56bc061bee907486d1077830e8b9e1e70ba4c8c9730e581609b3f8bc9574) ```java public final class OpenGaussAuthenticationSha256Packet implements PostgreSQLIdentifierPacket { private static final int AUTH_REQ_SHA256 = 10; private static final int PASSWORD_STORED_METHOD_SHA256 = 2; private final byte[] random64Code; private final byte[] token; private final int serverIteration; @Override public void write(final PostgreSQLPacketPayload payload) { payload.writeInt4(AUTH_REQ_SHA256); payload.writeInt4(PASSWORD_STORED_METHOD_SHA256); payload.writeBytes(random64Code); payload.writeBytes(token); payload.writeInt4(serverIteration); } @Override public PostgreSQLIdentifierTag getIdentifier() { return PostgreSQLMessagePacketType.AUTHENTICATION_REQUEST; } } ``` 客户端发送给 Proxy 的认证响应直接复用了 [PostgreSQLPasswordMessagePacket.java](https://github.com/apache/shardingsphere/pull/14002/files#diff-7b4e56bc061bee907486d1077830e8b9e1e70ba4c8c9730e581609b3f8bc9574),代码节选: ```java public final class PostgreSQLPasswordMessagePacket implements PostgreSQLIdentifierPacket { private final String digest; public PostgreSQLPasswordMessagePacket(final PostgreSQLPacketPayload payload) { payload.readInt4(); digest = payload.readStringNul(); } @Override public void write(final PostgreSQLPacketPayload payload) {} @Override public PostgreSQLIdentifierTag getIdentifier() { return PostgreSQLMessagePacketType.PASSWORD_MESSAGE; } } ``` ### 实现校验逻辑 首次实现校验逻辑的时候,我还不了解 SCRAM 的逻辑,于是我就按照 `MD5` 认证方式的实现方式去做。 实现思路:把客户端对密码的处理方式在 Proxy 重现一遍,最后把客户端发送的认证数据和 Proxy 内计算产生的结果对比。 Proxy 在认证请求里需要给客户端发送两个字符串 `random64code` 和 `token`,那就随机生成;整数 `server_iteration` 看到代码里在协议 `3.50` 里写死 `2048`,那此处就取 `2048`: ```java public OpenGaussAuthenticationEngine() { random64Code = RandomStringUtils.randomAlphanumeric(64); token = RandomStringUtils.randomAlphanumeric(8); serverIteration = 2048; } ``` ### 初步验证 写了一段简单的程序验证:(`5433` 是 openGauss,`55433` 是 ShardingSphere Proxy openGauss) ```java //try (Connection connection = DriverManager.getConnection("jdbc:opengauss://127.0.0.1:5433/postgres", "u3", "Wuweijie@123")) { try (Connection connection = DriverManager.getConnection("jdbc:opengauss://127.0.0.1:55433/freedom", "wuweijie", "Wuweijie@123")) { try (PreparedStatement preparedStatement = connection.prepareStatement("show all variables")) { try (ResultSet resultSet = preparedStatement.executeQuery()) { while (resultSet.next()) { System.out.println(resultSet.getString(1) + " -> " + resultSet.getString(2)); } } } } ``` 部分输出: ``` sql_show -> true sql_simple -> false ``` 能够正常连接 ShardingSphere Proxy openGauss 并通过认证,于是就形成了这个 PR: [Add SHA256 authentication for openGauss Proxy #14002](https://github.com/apache/shardingsphere/pull/14002) ### 无意间发现问题 第二天想着当时只用了 openGauss JDBC Driver 验证,要不试试 `gsql`: ```bash wuweijie@wuweijie-ubuntu /home/wuweijie % docker run --rm -i -t --network host enmotech/opengauss:2.1.0 gsql -h 127.0.0.1 -Uwuweijie -W'Wuweijie@123' -p 55433 freedom gsql: FATAL: password authentication failed for user "wuweijie" ``` 结果发现认证失败!以同样的方式直连 openGauss 却没有问题,可能是我的实现方式有问题。但是 openGauss 在协议方面没有文档,我也不知道应该怎么做。问了一下 openGauss 的专家,得到一个参考文档:[openGauss支持国密SM3和SM4算法](https://opengauss.org/zh/blogs/blogs.html?post/douxin/sm3_for_opengauss/) 除了哈希算法不一样,SCRAM 的机制是一样的。 这时候我才进一步了解 SCRAM 这种机制。 ### 调整 Proxy 校验逻辑 后面我根据以下参考资料调整了 Proxy 的校验逻辑。 ![SCRAM](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/15/1657877969662194346.png) 以上图片来源于:/opengauss.org/zh/blogs/blogs.html?post/douxin/sm3_for_opengauss/> 之前的变量名对应关系如下: * `random64code` -> `salt` * `token` -> `nonce` 调整后的逻辑大致如下: ```java private static boolean isPasswordRight(final ShardingSphereUser user, final Object[] args) { String h3HexString = (String) args[0]; String salt = (String) args[1]; String nonce = (String) args[2]; int serverIteration = (int) args[3]; byte[] serverStoredKey = calculatedStoredKey(user.getPassword(), salt, serverIteration); byte[] h3 = hexStringToBytes(h3HexString); byte[] h2 = calculateH2(user.getPassword(), salt, nonce, serverIteration); byte[] clientCalculatedStoredKey = sha256(xor(h3, h2)); return Arrays.equals(clientCalculatedStoredKey, serverStoredKey); } ``` ### 再次验证 实现完了,使用之前的 Java 代码验证 openGauss JDBC Driver 能够通过认证。 再用 `gsql` 验证,居然还是报错: ```bash wuweijie@wuweijie-ubuntu /home/wuweijie % docker run --rm -i -t --network host enmotech/opengauss:2.1.0 gsql -h 127.0.0.1 -Uwuweijie -W'Wuweijie@123' -p 55433 freedom gsql: FATAL: password authentication failed for user "wuweijie" ``` 又试了一下 2.0.0 的版本,同样报错: ```bash wuweijie@wuweijie-ubuntu /home/wuweijie % docker run --rm -i -t --network host enmotech/opengauss:2.0.0 gsql -h 127.0.0.1 -Uwuweijie -W'Wuweijie@123' -p 55433 freedom gsql: FATAL: password authentication failed for user "wuweijie" ``` ### 再次 Review 并修改 看逻辑没看出什么问题,再次观察 Wireshake 窃听到的 openGauss 数据库发送给客户端的数据包: ![Authentication Request](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/15/1657878813838639334.png) 发现我之前遗漏了细节: 在数据包的 `random64code` 和 `token` 参数部分,数据值的字符只有 `[a-f0-9]`,而我用的随机字符串生成方法是 `[A-Za-z0-9]`。 ```java random64Code = RandomStringUtils.randomAlphanumeric(64); token = RandomStringUtils.randomAlphanumeric(8); ``` 调整随机字符串生成方法并重命名变量: ```java saltHexString = generateRandomHexString(64); nonceHexString = generateRandomHexString(8); private String generateRandomHexString(final int length) { ThreadLocalRandom random = ThreadLocalRandom.current(); StringBuilder result = new StringBuilder(length); for (int i = 0; i result.capacity(); i++) { result.append(Integer.toString(random.nextInt(0x10), 0x10)); } return result.toString(); ``` 再次验证。 ### 最终验证 `gsql` 输出结果: ``` [~] docker run --rm -i -t --network host enmotech/opengauss:2.1.0 gsql -h 127.0.0.1 -Uwuweijie -W'Huawei@123' -p 55433 freedom gsql ((openGauss 2.1.0 build 590b0f8e) compiled at 2021-09-30 14:29:04 commit 0 last mr ) Non-SSL connection (SSL connection is recommended when requiring high-security) Type "help" for help. freedom=> show all variables; variable_name | variable_value ---------------------------------------+---------------- sql_show | true sql_simple | false kernel_executor_size | 0 省略部分输出 transaction_type | LOCAL (20 rows) ``` 用之前的代码验证 openGauss JDBC Driver,部分输出: ``` sql_show -> true sql_simple -> false ``` 两种客户端都能完成认证,于是再提一个 PR。 [Refactor openGauss frontend SCRAM SHA-256 authentication #14073](https://github.com/apache/shardingsphere/pull/14073) 完成! ## 回顾 如果一开始能够正确生成随即字符串,第一个 PR 的做法应该也是能够达到身份认证的目的的,但这不符合 SCRAM 的做法。 本次众智计划,让我学习到了很多与 openGauss 数据库协议、安全等方面的知识。后续再做类似的事情的时候,一定要先了解清楚相关知识,比如本次的 SCRAM。不明确字段含义的时候,一定要多观察特征、细节。 ## 参考资料 * /opengauss.org/zh/docs/2.1.0/docs/Developerguide/%E9%85%8D%E7%BD%AE%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%8E%A5%E5%85%A5%E8%AE%A4%E8%AF%81.html> * /datatracker.ietf.org/doc/html/rfc5802> * /opengauss.org/zh/blogs/blogs.html?post/douxin/sm3_for_opengauss/>
  • [问题求助] 【AppCube】前端运行包下载报错
    编译完成下载前端包时出现报错
  • [基础组件] Loader的submit_job命令出错,web执行过没问题
    配置文件已经按照用户文档配置好了。直接运行Loader的 ./shell-client/submit_job.sh的命令。./submit_job.sh -n GaussDB-to-HDFS -u n(已经通过webui配置了名为GaussDB-to-HDFS的作业,并且页面成功了)web页面已经配置并运行成功了。但shell还是报错shell这个报错是log4j的问题,感觉是shell-client下面的conf/log4j.properties有问题。但是检查了下并没有发现错误
  • [问题求助] 【RPA】【WeAutomate浏览器插件】插件和Web系统冲突,导致浏览器卡死(CPU动态100%)
    【功能模块】【RPA】【WeAutomate浏览器插件】【操作步骤&问题现象】1、步骤:谷歌浏览器启用WeAutomate插件,在浏览器中登录我们的Web系统2、现象:浏览器卡死,点页面无任何反应,CPU动态100%3、反例:禁用WeAutomate插件时,一切正常;【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [大咖交流] 企业容器化改造方案
    X企业容器化改造方案 【背景】A企业是一家位于杭州的软件开发公司,具备自主设计软件,交付软件及销售的能力,目前公司业务已经上华为云,考虑到开发及交付的便利性,准备进行容器化改,目标是能够实现软件开发即交付。业务现网状况如下:目前2台web服务器作为前端,mysql数据库,软件负载均衡器,无数据库中间件,后端EVS云硬盘,针对于本企业的现状,给出各部分的容器化改造及后续方案. 1:负载均衡应用改造点:选择合适的负载均衡器中小型的Web应用可以使用ngnix或HAProxy,大型网站或重要的服务可以使用LVS,目前该企业业务较小,选取nginx作为负载均衡器!2:web应用改造点:应用存在长时间执行请求   增加消息队列,通过消息队列将长任务与用户请求解耦3:应用服务器应用改造点:应用实例依赖于本地的存储来持久化数据如果是日志,建议变成流汇聚到分布式日志系统中。如果必须要使用存储,要使用共享文件系统如NFS4:资源及集群规划规划:目前采用单集群规划,云资源中有其他应用项目请画出简要的资源规划图: 5:高可用规划   结合华为云,给出高可用规划的简单说明:    分别在2个AZ中部署两套CCE集群,K8S Master采用本地3节点高可用部署;应用AZ内高可用部署,通过ClusterIP服务调用不跨AZ。应用发布LoadBalancer类型的Service对接到集群所在AZ的融合ELB服务实例;应用通过VIP访问数据库,数据库自动切换应用不感知。支持多AZ动态容器存储,根据pod所在AZ创建数据卷。6:网络规划:集群内部应用默认可通过ClusterIP类型服务相互通信。k8s集群内置DNS服务,服务间访问可以通过IP或域名访问,请画出K8S集群内部应用网络互通示意图: Step1:kube-proxy、core-dns从Master中kube-apiserver订阅service,POD2的Service创建时,kube-proxy刷新本节点iptables,core-DNS更新路由数据。Step2:Pod2通过域名访问Pod4的service4,发起到core-dns查询请求,并获取对应的ClusterIP(如果使用ClusterIP直接访问则忽略这一步骤)Step3:Pod2发送业务报文,目的地址为获取到的ClusterIP。容器网络根据目的地址匹配策略后进行VxLAN封装,封装源地址为容器所在的VM IP地址,目的地址为目的容器所在VM IP,并将报文发给I层vSwitch,然后转发至目的容器所在VM,容器网络解VxLAN封装后,根据ClusterIP将业务报文发送目的service及POD。
  • [大咖交流] 中小企业容器化改造建议
    中小企业容器化改造建议企业应用容器化改造,一般有以下三种方式:• 方式一:单体应用整体容器化,应用代码和架构不做任何改动。• 方式二:将应用中升级频繁,或对弹性伸缩要求高的组件拆分出来,将这部分组件容器化。• 方式三:将应用做全面的微服务架构改造,再单独容器化。对于中小企业而言,首次做容器化改造,建议选择方式一,主要优点有:• 业务0修改:应用架构和代码不需要做任何改动。• 提升部署和升级效率:应用可构建为容器镜像,确保应用环境一致性,提升部署效率。• 降低资源成本:Docker对系统资源利用率高。相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。确定容器化改造方式后,企业就可以着手改造。以近期遇到的某中小企业改造为例。企业现状:该企业目前2台web服务器作为前端,mysql数据库,软件负载均衡器,无数据库中间件,后端EVS云硬盘。企业改造点:1、负载均衡应用改造点:选择合适的负载均衡器。一般中小型的Web应用可以使用ngnix或HAProxy,大型网站或重要的服务可以使用LVS,目前该企业业务规模较小,选取nginx作为负载均衡器;2、web应用改造点:应用存在长时间执行请求。增加消息队列,通过消息队列将长任务与用户请求解耦。3、应用服务器应用改造点:应用实例依赖于本地的存储来持久化数据。日志建议变成流汇聚到分布式日志系统中。如果必须要使用存储,要使用共享文件系统如NFS。4、资源及集群规划:目前采用单集群规划(假设云资源中有其他应用项目),给出示意图如下:5、 高可用规划• 分别在2个AZ中部署两套CCE集群,K8S Master采用本地3节点高可用部署。• 应用AZ内高可用部署,通过ClusterIP服务调用不跨AZ。• 应用发布LoadBalancer类型的Service对接到集群所在AZ的融合ELB服务实例。• 应用通过VIP访问数据库,数据库自动切换应用不感知。• 支持多AZ动态容器存储,根据pod所在AZ创建数据卷。6、 网络规划集群内部应用默认可通过ClusterIP类型服务相互通信。k8s集群内置DNS服务,服务间访问可以通过IP或域名访问。K8S内部应用网络互通示意图如下:Step1:kube-proxy、core-dns从Master中kube-apiserver订阅service,POD2的Service创建时,kube-proxy刷新本节点iptables,core-DNS更新路由数据。Step2:Pod2通过域名访问Pod4的service4,发起到core-dns查询请求,并获取对应的ClusterIP(如果使用ClusterIP直接访问则忽略这一步骤)Step3:Pod2发送业务报文,目的地址为获取到的ClusterIP。容器网络根据目的地址匹配策略后进行VxLAN封装,封装源地址为容器所在的VM IP地址,目的地址为目的容器所在VM IP,并将报文发给I层vSwitch,然后转发至目的容器所在VM,容器网络解VxLAN封装后,根据ClusterIP将业务报文发送目的service及POD。
  • [技术干货] 现在前端开发的主流框架是啥啊
    现在前端开发的主流框架是啥啊
  • [技术干货] 前端下载
    async handleExcel() {      await exportExcel(JSON.stringify(this.gridData)).then(res => {        const a = document.createElement("a");        a.download = "新闻";        const blob = new Blob([res], {          type: "application/vnd.ms-excel"        });        const objectUrl = URL.createObjectURL(blob);        a.href = objectUrl;        a.click();        window.URL.revokeObjectURL(objectUrl);      });    },// 通用下载方法export function download(url, params, filename) {  return service.post(url, params, {    transformRequest: [(params) => {      return tansParams(params)    }],    headers: {        'Content-Type': 'application/x-www-form-urlencoded'    },    responseType: 'blob'  }).then((data) => {    const content = data    const blob = new Blob([content])    if ('download' in document.createElement('a')) {      const elink = document.createElement('a')      elink.download = filename      elink.style.display = 'none'      elink.href = URL.createObjectURL(blob)      document.body.appendChild(elink)      elink.click()      URL.revokeObjectURL(elink.href)      document.body.removeChild(elink)    } else {      navigator.msSaveBlob(blob, filename)    }  }).catch((r) => {    console.error(r)  })}//zipexport function resolveBlob(res) {  const aLink = document.createElement('a')  var blob = new Blob([res.data], { type: 'zip' })  // //从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名;  var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')  var contentDisposition = decodeURI(res.headers['content-disposition'])  var result = patt.exec(contentDisposition)  var fileName = result[1]  fileName = fileName.replace(/\"/g, '')  aLink.href = URL.createObjectURL(blob)  aLink.setAttribute('download', fileName) // 设置下载文件名称  document.body.appendChild(aLink)  aLink.click()  document.body.appendChild(aLink)}
  • [技术干货] WEB聊天控件/接续条——Token生成与认证验证接口代码demo
     WEB聊天控件/接续条——Token生成与认证验证接口代码demo注意:Demo主要提供算法和校验逻辑的思路,实际使用的时候请使用安全算法。该Demo不能直接用于生产,仅限DEMO演示,请依据您系统的安全要求进行合理设计。 对象说明:名称是否必选说明contextTypeY请求头contextTypesecretKeyY密钥,可使用与租间绑定的相关密钥httpmethodN请求方法timestampY时间戳authVersionN企业系统版本号HeadersY请求头bodyY请求体uriNUri,根据实际情况 可以使用urltenantSpaceIdN租间IDuserAccountN用户名 其他注意事项:1、Demo使用的加密方式安全级别较低,请设计符合企业要求的加密方式。2、请配合证书使用https。3、请勿跳过SSLClient。4、AuthorizaInterceptor:鉴权部分,不推荐在接口中构造,推荐在拦截器进行构造。 同时建议使用map/json格式进行构造
  • [积分闯关赛] 【GDE直播公开课·第二期】低代码平台关键能力之UI编排观后感+花溪
    直播链接:https://bbs.huaweicloud.com/forum/thread-163869-1-1.html观后感:UI编排提供丰富的界面设计组件,通过拖拽式实现前端界面快速开发,并支持多终端界面开发,降低开发门槛,提升开发效率。ADC-UI编排前端框架技术栈是基于:VueGDE开发地址https://adcdev.gde.huawei.com/dspcas/login?service=https%3A%2F%2Fadcdev.gde.huawei.com%3A443%2Fportal%2Fweb%2Frest%2Fsso%2Findex%3Fori_url%3Dhttps%253A%252F%252Fadcdev.gde.huawei.com%252Fportal-web%252Fportal%252Fhomepage.html登录之后 必须授权之后才能进行编排https://2001-adcdev.gde.huawei.com/portal-web/portal/homepage.html界面设计器通过页面设计器可以进行页面的开发与修改。页面设计器支持通过各类组件的拖拽与配置,实现Web页面设计与编排。通过流程图的界面设计器预置了100+个常用组件,可通过拖拉拽形式进行页面编排,开发过程中可随时进行页面预览。低门槛、拖曳式编排快速生成灵活定制支持多终端界面开发强大的页面js定制能力。终端灵活适配报表编排提供基于eCharts的报表编排能力,可在页面设计器中进行报表和页面联合编排。图形控件30+控件全部可调参数按配置树开放模拟数据在线调测机制JS脚本扩展UI编排流程实战参考——[热门活动-积分闯关赛] 【第三关:全球疫情数据看板开发】GDE闯关积分赛 https://bbs.huaweicloud.com/forum/thread-190299-1-1.html全球疫情数据看板开发
  • [新手课堂] Tomcat 的类加载器?
    1)Bootstrap ClassLoader:可以看到上图中缺少了 Extension ClassLoader,在 Tomcat 中 Extension ClassLoader 被集成到了 Bootstrap ClassLoader 里面。2)System ClassLoader 就是 Application ClassLoader:Tomcat 中的系统类加载器不会加载 CLASSPATH 环境变量的内容,而是从以下资源库构建 System 类加载器。$CATALINA_HOME/bin/bootstrap.jar,包含用于初始化Tomcat服务器的 main() 方法,以及它所依赖的类加载器实现类。$CATALINA_BASE/bin/tomcat-juli.jar 或 $CATALINA_HOME/bin/tomcat-juli.jar,日志实现类。如果 $CATALINA_BASE/bin 中存在 tomcat-juli.jar,则使用它来代替 $CATALINA_HOME/bin中的那个。$CATALINA_HOME/bin/commons-daemon.jar3)Common ClassLoader:从名字也看出来来了,主要包含一些通用的类,这些类对 Tomcat 内部类和所有 Web 应用程序都可见。该类加载器搜索的位置由 $CATALINA_BASE/conf/catalina.properties 中的 common.loader 属性定义,默认设置将按照顺序搜索以下位置。$CATALINA_BASE/lib 中未打包的类和资源$CATALINA_BASE/lib 目录下的JAR 文件$CATALINA_HOME/lib 中未打包的类和资源$CATALINA_HOME/lib 目录下的JAR文件4)WebappX ClassLoader:Tomcat 为每个部署的 Web 应用程序创建一个单独的类加载器,这样保证了不同应用之间是隔离的,类和资源对其他 Web 应用是不可见的。加载的路径如下:Web应用的 /WEB-INF/classes 目录下的所有未打包的类和资源Web应用的 /WEB-INF/lib 目录下的 JAR 文件中的类和资源
  • [技术干货] API工具--Apifox和Postman对比(区别)
    ## 前言 Postman和Apifox有什么区别?他们之间分别有什么优势,感兴趣的同学可以继续往下看。 不吹不黑,只列功能,纯客观比对。 ## 一.功能列表对比 ### (一)接口设计与文档管理功能 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282565040530404.png) 1.导入功能对比 Apifox的导入功能除了支持OpenApi之外,还支持yapi,RAP2,postman等国内用得比较多的接口文档导入,而Postman支持的格式相对较少。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282576741711183.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282585176936279.png) 2.在线分享功能对比 Postman的在线分享功能,付费版支持“只读”功能,Apifox分享功能支持选择过期日期、设置密码,选择分享内容的范围,选择环境等功能。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282596041219729.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282605326923851.png) 3.编辑接口文档对比 接口文档既可以纯粹的MD格式文档对接口做整体说明,也可以在单个接口内部对单个接口进行说明注释。Apifox会增加创建时间、负责人、所属业务分组等业务和协同层面的注释信息。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282614774579220.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282622401738551.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282631475197465.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282640297691879.png) 4.生成代码功能对比 Postman支持将接口生成代码,postman支持的接口和框架为4种,Apifox支持130多种语言和框架 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282649937591303.4) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282657274516776.png) 5.数据模型功能对比 在postman中没有这个功能,在Apifox中,由于本身具备接口设计的功能,因此会将实体类的相关参数封装成一个数据模型,供不同的接口调用,提高数据复用的效率,提高接口封装的程度,减少重复的工作。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282667876270264.png) ### (二)接口调试功能对比 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282676280468705.png) 对比了下,Postman基本依赖于JS脚本,通过编写脚本对接口进行调试。 Apifox则是可视化调试界面为主,自定义脚本编辑为辅。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282684951710175.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282692278583614.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282700429181459.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282708291131952.png) 两者对比,在postman中需要写脚本才能实现的接口断言和提取变量、等待时间,在这里都能直接通过填写参数来完成、不需要写脚本。 而操作数据库这个功能postman则不支持。postman只支持js脚本,Apifox目前支持调用其他语言的外部函数和脚本,不过需要先安装相关的Python、java等环境。 ### (三)接口mock功能 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282717724329514.png) Postman也有mock功能,但它的mock服务需要自己搭建而且mock功能并不强。 在Postman上执行API mock 需要经过3步: 第一步:创建 mock服务器,获得mock url 第二步:逐个编写并添加 mock 示例,供执行mock时返回对应的接口响应 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282725490242234.png) 也就是说接口mock 出来的响应来源于先前调试已经有的,或者直接自己编辑一个响应进去,才能得到一个返回。 mock server 只能返回自己手动添加进去的几条响应,而无法自己无限制创建出mock 数据。 第三步: 将mock url 复制到接口里进行调试。 而想要在 Apifox 内做接口 mock 只需要在`环境`中选择mock服务 在响应参数中选择mock规则,点击发送请求,则mock服务会返回与实际业务返回高度相似的接口响应。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282738275949352.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282746608606303.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282754904742773.png) ### (四)接口测试功能 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282765832339857.png) 在Postman里写测试脚本,使用动态参数,接口响应断言,参数传递都通过写脚本来实现。 如果要作业务接口测试,需要写各种场景下的用例,同样是通过写脚本来修改参数用例的执行顺序和设置循环次数的。使用postman至少需要掌握基础的js语言。 Apifox里面做自动化测试可视化程度相对较高一些,创建用例的时候可以在接口设计面板修改参数然后保存,场景用例可以添加不同的参数用例作为步骤,通过拖曳来选择用例的执行顺序。 右侧的面板可以填写循环次数,接口间的参数传递和断言也可以在可视化面板提取出来。完成单个接口测试或者场景测试,都不需要写代码。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282780666517472.png) ## 二.团队协作功能 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282790439655374.png) Postman的团队协作功能是付费的,3人以下团队可使用免费版协作,3人以上根据可用功能和人数有不同的价格版本。 但通常一个团队不可能只有3个人,也就是说,有限开放的那点协同功能是无法支持正常的团队协作需求的。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282798642373509.png) Apifox的协同功能是免费的,团队成员的权限管理,接口数据同步、在线分享都没有障碍。 本身Apifox的定位和Postman就不一样,它一出生就是定位在API管理和协作上。 所以除了协作功能必须的权限管理和数据同步上,它也最大程度地做数据复用,尽量减少不必要的工作量。 比如说接口调试的参数用例可以直接导入来做自动化测试,一个数据模型可以给多个接口使用,一套接口数据可以给后端做调试、前端做mock、测试做自动化。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282806721395621.png) ## 三.Apifox 没有的功能 Postman支持fork GitHub上的代码,以及API 网关。这两块在Apifox上均没有相关的功能。 两个工具的功能有相同的地方,但本质上各自的市场定位还是不同的,Postman打通了接口调试、测试、到线上监测,代码生成。 而Apifox始终立身于前端、后端测试间基于接口的设计、调试、测试、文档管理等一系列接口的生命周期管理来发力。 在相同的功能点上,Apifox基于本土互联网团队的协作模式和痛点,基本做到了人无我有,人有我优 的程度。 因此如果基于各种原因,寻找Postman替代的开发们,不妨体验一下Apifox。 ## 四.产品价格 从收费模式上看,postman是基础功能不收费,协作功能收费;Apifox是公网版本不收费,私有化部署收费。 Apifox的SaaS版本也没有什么功能和团队人数的限制,对于我们常规的项目开发来说,免费版本就够用了。 公网的SaaS版本,数据的确是放在他们服务器上的,但这点Postman其实也一样,而且postman的服务器可是放在国外的。 如果大家的项目安全保密级别较高,想要做私有化部署,可以去他们官网咨询,这方面我没咨询过就不对比了。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282816062828538.png) ### 下载地址 **Apifox官网**:[www.apifox.cn](https://www.apifox.cn/?utm_source=liam)
  • [行业资讯] Web3.0浪潮下,隐私计算再度迎来风口
           数据是数字经济时代的核心生产要素,但数据在自由流通或共享中才能产生更大价值。然而,隐私泄露事件层出不穷,出于数据归属、安全、隐私保护的顾虑,数据价值链不同环节之间流动受阻,分工协作脆弱,很难形成有效闭环。为使得数据交换具有安全保障,各方都在加大对数据在隐私保护下的挖掘和开发力度。在此背景下,隐私计算的提出以及快速发展,使其在消除“数据孤岛”、合规避险、弥合“信任鸿沟”方面具有重大意义。        2020年被称为"隐私计算元年"。根据国际调研机构Gartner战略科技趋势预测,隐私计算将成为2021年最具潜力的9项技术之一。那么,隐私计算究竟指什么,有哪些应用场景,现阶段竞争格局如何,未来发展前景是怎样呢?何为隐私计算?为数据价值而生,使数据可用但不可见。隐私计算是一类技术统称,旨在保护数据本身不对外泄露的前提下,实现数据分析计算。隐私计算针对不同的应用场景、信任环境和需求,将不同技术、算法、接口集成在一个平台上,并结合人工智能、机器学习、区块链等跨学科技术,为用户提供综合的解决方案,使得“不分享数据、但分享数据的价值”成为可能。隐私计算可区分数据承载的具体信息和计算价值,厘清数据使用的“权、责、利”,使得数据可以作为资产流通与变现。• 关键技术隐私计算的关键技术包括联邦学习、安全多方计算、机密计算、差分隐私、同态加密。其中,联邦学习、多方安全计算和机密计算目前应用最广。隐私计算将贯穿整个IaaS基础算力层、BaaS、SaaS服务层。在算力层,隐私计算与云计算作为重要的IaaS基础设施,同时和AI存在融合空间,可以为数据交换、存储和计算协作的可信环境提供算法支撑。在BaaS/SaaS层,隐私计算在数据价值挖掘环节可以发挥巨大作用,在金融、医疗、科学研究、社会征信、供应链金融、防伪溯源、社会治理等领域提供基于数据分析的应用服务。• 驱动因素随着数字要素时代来临,隐私计算潜在市场规模巨大。大数据市场规模大小一定程度决定隐私计算的行业天花板。根据IDC Global DataSphere统计数据显示,2020年,全球创造了59.0ZB的数据,其中50.4%的数据需要保护;根据Statista报告显示,2020年全球大数据市场收入规模接近600亿美元,未来大数据市场仍将稳步发展,预期增速将达到14%。由微众银行和毕马威联合发布的《隐私计算行业研究报告2021》显示:“国内市场规模将快速发展,三年后技术服务营收有望触达100-200亿人民币的空间,甚至将撬动千亿级的数据平台运营收入空间。”随着全球数字化进程加快,在隐私计算推动下,数据孤岛将被打破,从而挖掘数据要素巨大的市场价值,创造新的市场空间。隐私保护与开放共建,Web3.0面临监管挑战在Web3.0保护隐私和开放共建的背景下,并不意味着Web3.0应用不需要监管。由于Web3.0应用业务模式的巨大革新,监管方式势必会产生大的变化以适应新事物的发展业态。       事实上,合规性尚不明确正是阻碍隐私计算大规模商用的主要因素之一。虽然与传统明文传输、数据包的流通方式比,隐私计算更利于满足数据合规要求,通过控制原始数据不出域、只传递梯度信息,对输入模型的数据进行脱敏、加密处理等,有效解决数据在二次扩散过程中的安全风险。但仍然存在原始数据被复原、模型泄漏、参与方违背公约,由此导致数据泄漏等合规瑕疵。尤其是现有监管未对数据提供和处理给出明确依据,数据授权、数据分级等操作如何适配监管尚缺乏落地参考,这在应用隐私计算的过程中将导致合规风险进一步加大。因此一些机构在接受采访时表示,在数据安全法、个人信息保护法等新规出台后,各机构主要持谨慎观望状态,行业期望有更多的落地案例提供参考。        业内人士观点,围绕隐私计算合规路径,用户匿名和数据保护可能存在这样一种监管方案:        在区块链网络底层实现KYC监管,而在中间协议层和应用层实现适度匿名。所谓KYC认证,其实是—种实名认证机制,主要用于预防反洗钱、身份盗窃、金融诈骗等犯罪行为。当然,监管的手段可以是灵活的,用户KYC等信息可以存储在由监管参与的多签网络中。但除此之外,隐私计算在数据使用、流转过程中还存在多种问题,比如联合建模的数据使用目的很难被涵盖到授权协议中,如何二次获取用户授权成为难题。       结语       过去几年,市场多次传出Web3.0隐私赛道将迎来爆发,但目前来看隐私赛道仍然是个小众市场,用户量与使用量都不高,甚至很多人认为隐私是伪命题。隐私赛道之所以还没有爆发,很大原因在于技术、安全、监管等方面的一些难题还未得到解决,同时市场对于这块领域的需求还未明显展露出来。但长期来看,数据监管政策的出台始终利好于隐私计算市场,这也将进一步推动Web3.0基础设施的发展,让用户享有真正的数据自主权。届时,互联网的面貌必定焕然一新。 原文链接:https://www.xianjichina.com/special/detail_511211.html
  • [openEuler] 【软件迁移】鲲鹏代码迁移工具无法打开Web页面
    【问题简述】按照鲲鹏社区提供的官方安装指南,成功完成安装后,出现Web页面打不开的情况浏览器:VScode插件:【使用场景】本代码迁移工具尝试被应用于鲲鹏众智的Hadoop,Spark和Tez软件迁移项目,购买华为云服务器,按照官方指南在服务器已下载安装,并配置好鲲鹏代码迁移工具(官方指南链接:https://www.hikunpeng.com/document/detail/zh/kunpengdevps/porting/qs/qs-pa-kunpengdevps.html)【操作步骤】1、获取软件包:[root@hadoop02 ~]# wget https://mirror.iscas.ac.cn/kunpeng/archive/Porting_Dependency/Packages/Porting-advisor_2.3.0_linux-Kunpeng.tar.gz2、解压:[root@hadoop02 ~]# tar --no-same-owner -zxvf Porting-advisor_2.3.0_linux-Kunpeng.tar.gz3、执行runtime_env_check脚本,检查鲲鹏代码迁移工具的依赖文件。[root@hadoop02 Porting-advisor_2.3.0_linux-Kunpeng]# .\install web Porting Web console is now running, go to: https://192.168.0.250:8084/porting/#/login Successfully installed the Kunpeng Porting Advisor in /opt/portadv/.注:Windows和Linux防火墙均已关闭。怀疑是Windows端口的问题,但是防火墙关闭仍然打不开,希望有大佬帮忙解答~
  • [技术干货] 工业物联网之驱动简介
    关于驱动​驱动是对传统的数据采集程序的标准化,驱动可以作为你的资产进行管理系统为每个设备创建一个驱动实例通过反射创建驱动实例驱动不可以是静态的驱动要继承IDriver接口驱动内需要通过web配置的属性(支持枚举等基本类型),上要加上Attribute[ConfigParameter("端口号")] public int Port { get; set; } = 666;系统通过反射为属性赋值若设备设置为启动,则进行连接、读取的工作可以在驱动内,使用任何C#语法,记得在关闭和释放后释放你创建的资源,尤其是后台线程等驱动生命周期​构造[DriverInfoAttribute("YourDriver", "V1.0.0", "Copyright iotgateway© 2022-06-04")] public class YourDriverClass : IDriver{ }连接public bool Connect(){ }读取[Method("方法中文名", description: "方法描述")] public DriverReturnValueModel Read(DriverAddressIoArgModel ioarg){ } public class DriverAddressIoArgModel { public string Address { get; set; } public DataTypeEnum ValueType { get; set; } } public class DriverReturnValueModel { public object Value { get; set; } [JsonConverter(typeof(StringEnumConverter))] public VaribaleStatusTypeEnum StatusType { get; set; } = VaribaleStatusTypeEnum.UnKnow; }说明你可以有多个读取数据的方法,只要加上MethodAttribute即可识别DriverAddressIoArgModel.Address是通过前端传入的地址,你可以发挥想象(如使用逗号分隔符)传入更过的内容DriverAddressIoArgModel.ValueType是通过前端传入的数据类型,你可以选择是否使用它DriverReturnValueModel.Value 是object类型,你可以传出任何类型,或者与DriverAddressIoArgModel.ValueType呼应起来断开public bool Close(){ }释放public void Dispose(){ }