# Authentication Token
# PUBLIC KEY
GET /api/clients/key
{
"expiresAt": -1,
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9D3CVw6ONVPFNPTBp8vU1vHvPDf+W9De3+oILMZi2m+qG/A35U6izcpWsRjijJR2kSNOKyozN6RlsxAXPUmq1QLAGdtmR2dlPKPhVk2kKtxmVt1jyWpD4kjyjrG4DqsGITTE72QUkeLzaF34VhRXqpfTIanmUflvsVs8Cm5/XYQIDAQAB"
}
【注意】该接口所获取的公钥是经过BASE64
编码之后的字符串。
# Auth
POST /api/clients/auth
# FORM
以FORM表单方式发送认证信息,参数如下所示:(使用上面的API获取的公钥对密码进行加密后提交)
- HEAD
Content-Type: application/x-www-form-urlencoded
- FORM
client: {client-id}
- FORM
secret: {cipher-text}
如果需要使用明文提交密码,可以额外增加一个参数ciphertext=false
:
- FORM/QUERY
ciphertext: false
- FORM
secret: plain-text
# JSON Payload
以JSON报文发送认证信息,参数如下所示:
- HEAD
Content-Type: application/json
- Payload
{
"secret": "{cipher-text}",
"client": "{client-id}"
}
如果需要使用明文提交密码,可以额外增加一个查询参数ciphertext=false
:
- Query
ciphertext: false
- Payload
{
"secret": "{plain-text}",
"client": "{client-id}"
}
# TEXT Cipher Payload
以文本报文发送认证信息,参数如下所示:
- HEAD
Content-Type: application/text
- QUERY
cipherPayload: true
- Payload
{full-json-payload-ciphertext}
# Response Payload
# Success
{
"client": "2022",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ2PTM7cz0xMDAxO2M9MjAyMiIsImV4cCI6MTcxMTI2NTA5NCwiaWF0IjoxNjQ4MTkzMDk0fQ.R56ukHngXoy7kAbFKA8hX8JOYSNBYHi9XlgFeOs5xYc",
"expiresAt": 1711265094043
}
# Error
{
"status": 401,
"code": "UNAUTHORIZED",
"error": "UNAUTHORIZED",
"message": "Authorization failed, cause: ..."
}
# SQL Service - Query
# Version
在
PATH
上指定要调用服务的版本,如:
GET /api/v1/sql-services/:sid/query
POST /api/v1/sql-services/:sid/query
在
HEAD
请求头中指定要调用服务的版本,X-SERVICE-VERSION: 1.2
GET /api/sql-services/:sid/query
POST /api/sql-services/:sid/query
根据客户端系统订阅的版本进行响应
GET /api/sql-services/:sid/query
POST /api/sql-services/:sid/query
# SQL Matches
eq # Default mather
ne
gt
ge
lt
le
like # SQL like pattern
contains
starts_with
ends_with
not_like # SQL like pattern
not_contains
not_starts_with
not_ends_with
between
not_between
in
not_in
is_null
not_null
# POST Payload
POST /api/sql-services/:sid/query
POST /api/v1/sql-services/:sid/query
{
"args": [{
"name": "classId",
"value": [1010]
}, {
"name": "sno",
"value": [2020101001, 2020101002],
"match": "not_in"
}, {
"name": "name",
"value": ["周"],
"match": "starts_with"
}],
"selectedColumns": ["sno", "name", "sfzID"], // Optional, 默认返回所有有权限的字段的数据
"securityColumns": ["sfzID"], // Optional, 只针对存在脱敏的字段且拥有完整读权限的才可能需要传递(主动要求脱敏)
"sorts": [{ // Optional
"name": "name",
"direction": "DESC"
}],
"join": "OR" // Optional, Default `AND`
}
# 参数解释:
args
入参、即查询条件,支持多种匹配方式,参考SQL Matches,单值、多值条件匹配都要把匹配值按数组格式编写"value": [...]
。是否必填取决于授权。selectedColumns
可选参数,默认为空,返回所拥有权限的所有字段column
数据,可以设置仅返回其中某几列的数据(有助于节约带宽、提升查询速度)。securityColumns
可选参数,拥有完整读权限+支持脱敏的字段,缺省以最高权限计算,但支持客户端以低权限(脱敏读)的数据格式返回。支持["*"]
代表其所拥有的权限的所有脱敏列都要脱敏。sorts
可选参数,数据结果数据排序(使用服务定义的排序字段子集)join
可选参数,用于args
条件连接(缺省使用AND
连接)criteria
与args
参数不同时使用(互斥),兼容args
写法同时间支持参数条件逻辑嵌套
写法。e.g.
{
"criteria": {
"match": "OR",
"children": [{
"name": "classId",
"value": [1010],
"match": "eq"
}, {
"name": "sno",
"value": [2020101001, 2020101002],
"match": "not_in"
}, {
"match": "AND",
"children": [] // TODO Nested
}]
}
}
// SQL伪代码 `classId = 1010 OR sno NOT IN (2020101001, 2020101002) OR (... AND ... AND ...)`
# GET Payload
GET /api/sql-services/:sid/query
GET /api/v1/sql-services/:sid/query
# rawCriteriaPayload
对POST
方式提交的查询参数报文使用URLEncoder
进行编码,并以查询参数(rawCriteriaPayload
)的方式传递。效果等同于在报文体里传递Payload
。
// java e.g.
// java.net.URLEncoder.encode(payload)
//
// js e.g.
// encodeURIComponent(payload)
# base64EncodedRawCriteriaPayload
对POST
方式提交的查询参数报文使用Base64Encoder
进行编码,并以查询参数(base64EncodedRawCriteriaPayload
)的方式传递(与前者类似)。效果等同于在报文体里传递Payload
。
# Easy arguments
优先级低于rawCriteriaPayload
和base64EncodedRawCriteriaPayload
。简单查询参数需求可以使用(如简单API测试)。
- 支持使用格式
p.{argName}={value}
的方式传递查询条件(eq
精确匹配,如果是多值则使用in
进行匹配) - 支持使用格式
p.{argName}=~{value}
的方式传递查询条件(contains
模糊匹配)
# 其他可选参数
skipCountSql
分页查询数据时,true
允许跳过执行COUNT SQL
语句。在查询条件不变的情况下跳页查询时可以使用这一可选参数,提升查询速度。totalElements
配合skipCountSql
参数使用,为使得每次返回的分页信息基本正确。onlyCountSql
true
仅执行COUNT SQL
语句,数据查询API可以仅返回记录总数(Total)。onlySqlDetails
辅助功能,true
返回引擎动态生成的SQL信息,方便分析SQL性能和调试错误等问题。illegalArgStrategy
非法参数处理策略,默认值ERROR
,可选值IGNORE
。argJoinType
使用查询参数方式逐个传递参数时有效,参数逻辑连接关系,默认值AND
,可选值OR
。selectedColumns
使用查询参数方式逐个传递参数时有效,仅返回这些数据列的内容(默认返回拥有权限的所有列的数据)。securityColumns
使用查询参数方式逐个传递参数时有效,主动要求脱敏的数据列(已经配置了脱敏规则且拥有完整读权限才可能需要)。sorts
设置查询结果排序,格式:COLUMN1:ASC;COLUMN2:DESC;...
COLUMN1;COLUMN2:DESC;...
(仅限使用查询参数方式non-payload
传递criteria有效)
# 其他查询相关APIs
- 1)查询数据导出API —— 导出CSV/XML/JSON等格式的数据(支持扩展
com.primeton.dataservice.core.spi.converter.DataConverter
接口实现其他文件格式数据导出) - 2)根据条件统计数据量API —— COUNT
- 3)查询SWAGGER文档API —— SWAGGER JSON Document
(1)和(2)API与数据查询API的请求方式和参数基本一致,仅仅是PATH
不同而已。
# 数据脱敏算法
算法实现
FixedLength
定长文本(数值)脱敏规则,参数列表:length=18
(文本长度)、snippets="3-5,6-9,15-17"
(脱敏片段)、strict=true
(严格模式:文本长度必须满足)Pattern
正则脱敏规则,参数列表:pattern="正则表达式"
、placeholder="***"
(正则匹配替换后的占位符)FullName
姓名脱敏规则,参数列表:hiddenFamilyName=true
(隐藏姓氏)、hiddenLastName=true
(隐藏名字),能识别中文姓名 英文姓名(姓氏位置不同)、能识别中文复姓穷举(司马 欧阳 上官等)、支持隐藏姓氏或名字;SimpleAddress
地址简单脱敏规则,隐藏地址中的数字,该脱敏规则无参;BASE64
(加密类型)安全级别较高的脱敏方式,该脱敏规则无参;RSA
(加密类型)安全级别最高的脱敏方式,参数列表:secretKey="TODO // RSA Public/Private Key"
、isPublic=true
(可选参数、默认自动识别配置的秘钥是公钥还是私钥);
扩展方式
- 实现脱敏算法接口:
com.primeton.dataservice.core.spi.security.DesensitizationAlgorithm
; - 扩展的脱敏规则实现类注册为
Spring Bean
,并将其打包成jar
文件,放入数据服务引擎${APP_HOME}/lib/
目录中; - 脱敏算法规则名称必须唯一,
DesensitizationAlgorithm#name()
- 允许覆写数据服务引擎内置的几个脱敏算法,
DesensitizationAlgorithm#name()
&DesensitizationAlgorithm#getOrder()
e.g.
package com.primeton.dataservice.core.spi.security.impl;
import com.primeton.dataservice.core.spi.security.AbstractDesensitizationAlgorithm;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
/**
* 姓名脱敏规则实现 <br>
* - 国际化:识别中文姓名 英文姓名(姓氏位置不同)<br>
* - 中文名:识别中文复姓穷举(司马 欧阳 上官等) <br>
* - 规则化:隐藏姓氏 隐藏名字 <br>
*
* @author CHINESE (mailto:lizw@primeton.com)
*/
@Component
public class FullNameDesensitizationAlgorithm extends AbstractDesensitizationAlgorithm {
private static final String ARG_HIDDEN_FAMILY_NAME = "hiddenFamilyName";
private static final String ARG_HIDDEN_LAST_NAME = "hiddenLastName";
// https://baike.baidu.com/item/%E5%A4%8D%E5%A7%93/62318?fr=aladdin
// https://baijiahao.baidu.com/s?id=1641609835629337012&wfr=spider&for=pc
// TODO FamilyNames as dict file
private static final Set<String> DOUBLE_FAMILY_NAME_LIST = Collections.unmodifiableSet(Arrays.stream((
"欧阳 太史 端木 上官 司马 东方 独孤 南宫 万俟 闻人 夏侯 诸葛 " +
"尉迟 公羊 赫连 澹台 皇甫 宗政 濮阳 公冶 太叔 申屠 公孙 慕容 " +
"仲孙 钟离 长孙 宇文 司徒 鲜于 司空 闾丘 子车 亓官 司寇 巫马 " +
"公西 颛孙 壤驷 公良 漆雕 乐正 宰父 谷梁 拓跋 夹谷 轩辕 令狐 " +
"段干 百里 呼延 东郭 南门 羊舌 微生 公户 公玉 公仪 梁丘 公仲 " +
"公上 公门 公山 公坚 左丘 公伯 西门 公祖 第五 公乘 贯丘 公皙 " +
"南荣 东里 东宫 仲长 子书 子桑 即墨 达奚 褚师 吴铭"
).split(" ")).map(String::trim).filter(StringUtils::isNotEmpty).collect(Collectors.toSet()));
@Override
public String name() {
return "FullName";
}
@Override
public String parse(String text, Map<String, Object> args) {
if (StringUtils.isEmpty(text)) {
return text;
}
boolean hiddenFamilyName = (Boolean) args.getOrDefault(ARG_HIDDEN_FAMILY_NAME, Boolean.FALSE);
boolean hiddenLastName = (Boolean) args.getOrDefault(ARG_HIDDEN_LAST_NAME, Boolean.FALSE);
if (hiddenFamilyName && hiddenLastName) {
return placeholder(text.length());
}
if (!hiddenFamilyName && !hiddenLastName) {
return text;
}
boolean chinese = Pattern.matches("[\\u4e00-\\u9fa5]+", text);
if (chinese) {
if (text.length() < 2) {
return text; // Illegal name
}
if (DOUBLE_FAMILY_NAME_LIST.stream().anyMatch(text::startsWith)) {
if (text.length() == 2) {
return hiddenFamilyName ? placeholder(2) : text; // Illegal name
}
return hiddenFamilyName ? (placeholder(2) + text.substring(2)) : (text.substring(0, 2) + placeholder(text.length() - 2));
}
return hiddenFamilyName ? (placeholder(1) + text.substring(1)) : (text.substring(0, 1) + placeholder(text.length() - 1));
}
// 英文名脱敏
// 英语姓名的一般结构为:名+中间名+姓。如 William Jafferson Clinton
// 分隔符:空格/句号(点)
String separator = text.contains(" ") ? " " : ".";
List<String> snippets = Arrays.stream(text.split(text.contains(" ") ? " " : "\\.")).map(String::trim).
filter(StringUtils::isNotEmpty).collect(Collectors.toList());
if (snippets.size() < 2) {
return text; // Illegal name
}
return hiddenFamilyName ? (snippets.get(0) + separator + placeholder(6)) :
(placeholder(6) + separator + snippets.stream().skip(1L).collect(Collectors.joining(separator)));
}
@Override
public Map<String, Object> example() {
Map<String, Object> args = new HashMap<>();
args.put(ARG_HIDDEN_FAMILY_NAME, false);
args.put(ARG_HIDDEN_LAST_NAME, true);
return args;
}
}
使用方式
- SQL查询服务规格定义的每个
输出参数
都可以按需配置脱敏方式(服务定义默认值); - SQL查询服务API授权为每个客户端单独配置
输出参数
的脱敏方式(允许覆写默认值); - 数据服务平台Dashboard配置生成服务和授权时,动态按需加载脱敏规则的参数表单(如果有参数)进行渲染(UI要做成动态表单方便扩展);
# 安全加固 —— JSON响应报文体加密传输
对响应JSON报文中的content
部分采用以下之类的算法实现进行加密处理,把加密后的报文返回给客户端、客户端使用对应的解密算法进行解密后获得原始数据。
加密实现
BASE64
安全级别低,简单加密方式,该脱敏规则无参;RSA
高安全级别加密方式(基于RSA对称加密技术,支持使用RSA公钥或私钥进行加密),参数列表:secretKey="TODO // RSA Public/Private Key"
、isPublic=true
(可选参数、默认自动识别配置的秘钥是公钥还是私钥);
扩展方式
- 实现报文体加密算法接口:
com.primeton.dataservice.core.spi.security.RSAPayloadEncryption
; - 扩展的加密算法实现类注册为
Spring Bean
,并将其打包成jar
文件,放入数据服务引擎${APP_HOME}/lib/
目录中; - 加密算法规则名称必须唯一,
RSAPayloadEncryption#name()
- 允许覆写数据服务引擎内置的几个加密算法,
RSAPayloadEncryption#name()
&RSAPayloadEncryption#getOrder()
- 对于含有参数的算法实现,请到数据管理平台后端进行配置(以便支持UI自动渲染扩展的算法以及其参数的表单);
package com.primeton.dataservice.core.spi.security;
import java.util.List;
import java.util.Map;
import org.springframework.core.Ordered;
/**
* @author CHINESE (mailto:lizw@primeton.com)
*/
public interface PayloadEncryption extends Ordered {
@Override
default int getOrder() {
return 200;
}
String name();
String encrypt(List<Map<String, Object>> content, Map<String, Object> args);
}
使用方式
在SQL查询服务
客户端授权(或变更)时设置响应报文加密传输方式
(从平台提供或用户扩展的加密算法中选择一个,并填写相关参数)。
# MONGODB Service - Query
# Version
在
PATH
上指定要调用服务的版本,如:
GET /api/v1/mongodb-services/:sid/query
POST /api/v1/mongodb-services/:sid/query
在
HEAD
请求头中指定要调用服务的版本,X-SERVICE-VERSION: 1.2
GET /api/mongodb-services/:sid/query
POST /api/mongodb-services/:sid/query
根据客户端系统订阅的版本进行响应
GET /api/mongodb-services/:sid/query
POST /api/mongodb-services/:sid/query
# Match and JOIN
- 选择器(匹配方式):
$eq
,$gt
,$gte
,$in
,$lt
,$lte
,$ne
,$nin
- 逻辑连接:
$and
,$not
,$nor
,$or
- 更多参考: https://www.mongodb.com/docs/manual/reference/operator/query/#query-and-projection-operators
- e.g.:
{
"name": "鸠摩智"
}
{
"name": {
"$eq": "鸠摩智"
}
}
{
"name": {
"$in": ["鸠摩智", "王语嫣", "慕容复", "段誉"]
}
}
# POST Payload
POST /api/mongodb-services/:sid/query
POST /api/v1/mongodb-services/:sid/query
{
"sorts": [{ // 排序设置
"name": "sno",
"direction": "DESC"
}],
// "securityColumns": ["cid", "phone"], // 拥有完整读权限时可以主动要求脱敏
// "selectedColumns": ["sfzID", "name", "mobile"], // 默认响应所有权限字段,可以选取其权限字段的子集
"criteria": { // 查询条件,保留MONGODB原始支持的查询语法,可以使用逻辑嵌套和各种字段匹配方式 https://www.mongodb.com/docs/manual/reference/operator/query/
"name": {
"$in": ["鸠摩智", "王语嫣", "慕容复", "段誉"]
}
}
}
# 参数解释:
criteria
入参、即查询条件,支持多种匹配方式,参考MONGODB官方资料。selectedColumns
可选参数,默认为空,返回所拥有权限的所有字段column
数据,可以设置仅返回其中某几列的数据(有助于节约带宽、提升查询速度)。securityColumns
可选参数,拥有完整读权限+支持脱敏的字段,缺省以最高权限计算,但支持客户端以低权限(脱敏读)的数据格式返回。支持["*"]
代表其所拥有的权限的所有脱敏列都要脱敏。sorts
可选参数,数据结果数据排序(使用服务定义的排序字段子集)
# GET Payload
GET /api/mongodb-services/:sid/query
GET /api/v1/mongodb-services/:sid/query
# rawCriteriaPayload
对POST
方式提交的查询参数报文使用URLEncoder
进行编码,并以查询参数(rawCriteriaPayload
)的方式传递。效果等同于在报文体里传递Payload
。
// java e.g.
// java.net.URLEncoder.encode(payload)
//
// js e.g.
// encodeURIComponent(payload)
# base64EncodedRawCriteriaPayload
对POST
方式提交的查询参数报文使用Base64Encoder
进行编码,并以查询参数(base64EncodedRawCriteriaPayload
)的方式传递(与前者类似)。效果等同于在报文体里传递Payload
。
# Easy arguments
优先级低于rawCriteriaPayload
和base64EncodedRawCriteriaPayload
。简单查询参数需求可以使用(如简单API测试)。
- 支持使用格式
p.{argName}={value}
的方式传递查询条件($eq
精确匹配,如果是多值则使用$in
进行匹配) - 支持使用格式
p.{argName}=~{value}
的方式传递查询条件($regex
模糊匹配——包含)
# 其他可选参数
skipCountDocument
分页查询数据时,true
允许跳过执行COUNT
查询。在查询条件不变的情况下跳页查询时可以使用这一可选参数,提升查询速度。totalElements
配合skipCountDocument
参数使用,为使得每次返回的分页信息基本正确。onlyCountDocument
true
仅执行COUNT
查询,数据查询API可以仅返回记录总数(Total)。onlyArgDetails
辅助功能,true
返回引擎接收或合成的查询条件,方便分析、调试错误等问题。illegalArgStrategy
非法参数处理策略,默认值ERROR
,可选值IGNORE
。argJoinType
使用查询参数方式逐个传递参数时有效(non-payload),参数逻辑连接关系,默认值AND
,可选值OR
NOR
。selectedColumns
使用查询参数方式逐个传递参数时有效,仅返回这些数据列的内容(默认返回拥有权限的所有列的数据)。securityColumns
使用查询参数方式逐个传递参数时有效,主动要求脱敏的数据列(已经配置了脱敏规则且拥有完整读权限才可能需要)。sorts
设置查询结果排序,格式:COLUMN1:ASC;COLUMN2:DESC;...
COLUMN1;COLUMN2:DESC;...
(仅限使用查询参数方式non-payload
传递criteria有效)
# 其他查询相关APIs
- 1)查询数据导出API —— 导出CSV/XML/JSON等格式的数据(支持扩展
com.primeton.dataservice.core.spi.converter.DataConverter
接口实现其他文件格式数据导出) - 2)根据条件统计数据量API —— COUNT
- 3)查询SWAGGER文档API —— SWAGGER JSON Document
(1)和(2)API与数据查询API的请求方式和参数基本一致,仅仅是PATH
不同而已。
# 数据脱敏算法
与SQL查询服务共用一套,此处不做赘述
# 安全加固 —— JSON响应报文体加密传输
与SQL查询服务共用一套,此处不做赘述
← 一、服务调用说明 1、添加系统与数据源信息 →