SaaS应用介绍
在应用托管体系中,并未所有应用的分发形式都是依照Kubernetes方式,对每一次应用向最终用户的交付形态,都是交付一个真实的物理运行空间。还有这里要提到的SaaS类应用。此类应用,其分发形式,仅仅是在原有系统中,分配了一个账户,交付给客户。因而,为了兼容单租户形式的独立托管部署和这类多租户的SaaS系统,平台笼统了与这类SaaS系统的交互行为,应用为满足这个交互行为,需要完本钱文所提的对接要求。
整体链路
功能边境
租户管理
IoT平台管理阿里云租户与应用内租户的映射关系(支持一个阿里云租户拥有同一个应用的多个租户),SaaS管理应用内部的租户构造
组织架构
本期无需对接IoT平台的组织架构体系,SaaS内部自行管理组织架构,但是两边的组织架构需要可以定位到同一个员工身份, 目前通过员工电话作为识别的唯一标识;
权限管理
IoT平台管理租户及其组织架构中的员工是否有权限访问应用,SaaS管理租户及组织架构中员工访问功能列表;
对接开发
第一步:AppKey的获取
托管应用自动产生
应用部署在应用托管平台中,系统会为部署的每个应用颁发AppKey,AppKey通过环境变量方式注入至业务代码运行的容器中,供代码获取并使用;
人工创建
这种场景适用于您自行通过外置效劳器的方式来搭建SaaS效劳的形式。创建AppKey的过程如下问:
登录卖家中心,在“应用接入”->”应用管理”->“创建应用”账号分发类型:
选择云端外部接入的形式:
第二步:应用开发
应用开发,是指依照平台对SaaS了应用的对接要求,要求应用提供指定的接口,或者平台为应用提供必要的接口。目前,平台要求应用提供的接口有3个(消费租户接口、回收租户接口、免密登录接口)。
另外,为了实现对接应用与平台之间的账户互认,平台提供了获取当前用户(包括子账户)的手机号。
接口开发1: 消费租户
场景描绘
用户(阿里云账户体系)在物联网市场下单购置应用后需要通知SaaS为当前用户开立一个可访问SaaS效劳的租户(账户),用户可以通过点击物联网市场中已购置的应用或者直接访问SaaS提供的公网域名来访问应用,当用户购置的应用到期后系统会自动发起SaaS租户的回收操作。所以您需要实现CreateInstance接口,来实现用户购置应用场景。
超时时间
默认接口超时时间为5秒。
调用时序
接口说明CreateInstance消费效劳
Protocol
HTTPS
Method
POST
恳求参数
参数
类型
必填
描绘
id
String
是
该次访问唯一标示符(幂等验证ID)
tenantId
String
是
IoT平台标识一个租户的唯一ID
appId
String
是
应用唯一ID,一个租户可以反复购置一款软件,每次购置appId都不同
appType
String
是
“TRYOUT”:试用,用户可以有一个短暂的试用期,该功能在商品上架时ISV可以选择是否提供试用逻辑
“PRODUCTION”:正式购置,用户正式下单购置
moduleAttribute
String
否
JSON字符串,主要包含上架时候配置的额外计费项参数列表。JSON转化成Map之后,示例:{“key1”:”rjmjz2”,”key2”:”2”,”key3”:”rjmjz2”}
详见下方 参数介绍【moduleAttribute】
返回参数
参数
类型
描绘
code
Integer
调用胜利返回200;若调用失败返回203;
message
String
code返回203时填写详细失败原因,code返回200填写success
userId
String
SaaS标识一个租户的唯一ID,对应的appid不同返回的userid也不同
注:同一个用户购置同一款SaaS软件,会屡次恳求该接口,恳求参数中appId不同,saas应用需要返回不同的userId
验签说明
参见【验签说明】
返回示例
{
"code":200,
"message":"success",
"userId":"my saas user id"
}
参数介绍【moduleAttribute】
step1: 卖家在停止商品上架时指定额外计费项,当前示例中指定该收费项参数为”service_door”
step2: 买家在市场下单购置商品时输入的额外计费项,当前示例中输入购置数量为200
step3:买家下单后iot平台调用消费租户接口时传送的moduleAttribute参数值为{“service_door”:”200”}
接口开发2: 回收租户
场景描绘
用户(阿里云账户体系)在物联网市场下单购置应用后需要通知SaaS为当前用户开立一个可访问SaaS效劳的租户(账户),用户可以通过点击物联网市场中已购置的应用或者直接访问SaaS提供的公网域名来访问应用,当用户购置的应用到期后系统会自动发起SaaS租户的回收操作。所以您需要实现DeleteInstance接口,来实现用户购置应用到期回收场景;
超时时间
默认接口超时时间为5秒;
调用时序
接口说明DeleteInstance注销效劳
Protocol
HTTPS
Method
POST
恳求参数
参数
类型
必填
描绘
id
String
是
该次访问唯一标示符(幂等验证ID)
tenantId
String
是
IoT平台标识一个租户的唯一ID
userId
String
是
SaaS标识一个租户的唯一ID
appId
String
是
应用唯一ID,一个租户可以反复购置一款软件,每次购置appId都不同
返回参数
参数
类型
描绘
code
Integer
调用胜利返回200;若调用失败返回203;
message
String
code返回203时填写详细失败原因,code返回200填写success
验签说明
参见【验签说明】
返回示例{
"code":200,
"message":"success"
}
接口开发3: 免密登录
场景描绘
用户(阿里云账户体系)在物联网市场下单购置应用后需要通知SaaS为当前用户开立一个可访问SaaS效劳的租户(账户),用户可以通过点击物联网市场中已购置的应用来访问应用,这时用户无需再次输入SaaS的账户/密码即可进入SaaS系统停止业务操作,所以您需要实现GetSSOUrl接口,来实现用户访问应用时的免登场景;
安全说明
SaaS返回的SSOUrl必需满足以下两种安全战略中的一种
每次生成的Url只允许**一次使用**
限制临时token的有效时间范围(推荐30秒)
超时时间
默认接口超时时间为5秒。
调用时序
注: SaaS生成的token信息需要带有验证用户身份及时限性,生成和验证的过程全部由SaaS完成,IoT只做免登URL的透传;
接口说明GetSSOUrl消费效劳
Protocol
HTTPS
Method
POST
恳求参数
参数
类型
必填
描绘
id
String
是
该次访问唯一标示符(幂等验证ID)
tenantId
String
是
IoT平台标识一个租户的唯一ID
tenantSubUserId
String
否
IoT平台租户组织架构中的员工唯一ID,当员工账号免登时填写
appId
String
是
应用唯一ID,一个租户可以反复购置一款软件,每次购置appId都不同
userId
String
是
SaaS标识一个租户的唯一ID
返回参数
参数
类型
描绘
code
Integer
调用胜利返回200;若调用失败返回203;
message
String
code返回203时填写详细失败原因,code返回200填写success
ssoUrl
String
免登url,需要带有验证用户身份及时限性的token信息
验签说明
参见【验签说明】
返回示例{
"code":200,
"message":"success",
"ssoUrl":"https://www.test123.com/login.html?ssoToken=xdasfdasdfasfdasfdaf&checkToken=ddddddd"}
}
接口提供1: 手机获取
场景描绘
SaaS效劳假设需要租户或者组织架构中员工手机号,可以通过该接口获取;
受权过程: IoT平台受权appkey调用该接口
权限展示:该权限属于用户隐私数据,所以用户在市场购置时会提示用户当前应用会读取他的手机,并且SaaS不可泄露用户手机号信息;
安全说明
处于安全考虑,单用户手机号只允许获取一次,假设有业务需求请获取后存储于您的系统中,并在相关性协议条款允许范围内使用该信息;
调用时序
接口说明GetUsERPhone手机号获取效劳
protocol
httpMethod
apiVer
url
host
HTTPS
POST
1.0.0
/app/user/info/get
api.link.aliyun.com
恳求参数
参数
类型
必填
描绘
tenantId
String
是
IoT平台标识一个租户的唯一ID
appId
String
是
应用唯一ID,一个租户可以反复购置一款软件,每次购置appId都不同
tenantSubUserId
String
否
IoT平台租户组织架构中的员工唯一ID,当员工账号免登时填写。
userId
String
是
SaaS返回的租户唯一ID
返回参数
参数
类型
描绘
id
String
IoT平台返回的调用唯一ID
code
Integer
调用胜利返回200;若调用失败返回错误码;
message
String
code返回非200时填写详细失败原因,code返回200填写success
data
“data”:{
“phone”:”13000000000”
}
当tenantSubUserId未填写时返回租户tenantId对应手机号,当tenantSubUserId填写时返回租户下组织架构中tenantSubUserId的手机号
加签说明
参见【验签说明】
返回示例
{
"id":"dj182318jjkhsljkdhfjahjdhfla"
"code":200,
"message":"success",
"data":{
"phone":"13000000000"
}
}
错误码列表
错误码
错误信息
描绘
200
success
胜利
400
request error
恳求错误
401
request auth error
恳求认证错
403
request forbidden
恳求被制止
404
service not found
效劳未找到
429
too many requests
太多恳求
460
request parameter error
恳求参数错误
500
service error
效劳端错误
503
service not available
效劳不可用
签名机制
加签
您可以直接下载签名的多语言demo,您可以在demo代码中找到加签的逻辑,假设一下列表不包含您的开发语言,您也可以通过阅读【加签说明】来自行开发加签逻辑;
验签
以下将为您介绍如何通过AppKey&AppSecret来对HTTP/HTTPS恳求做加签,验签的过程需要您依照以下加签过程重新加签,假设您计算的签名和恳求的签名一致即可认为验签胜利;
签名内容StringstringToSign=
HTTPMethod+"\n"+
Accept+"\n"+//建议显示设置 Accept Header。当 Accept 为空时,部分 Http 客户端会给 Accept 设置默认值为 /,导致签名校验失败。
Content-MD5+"\n"
Content-Type+"\n"+
Date+"\n"+
Headers+
Url
HTTPMethod 为全大写,如 POST。
Accept、Content-MD5、Content-Type、Date 假设为空也需要添加换行符”\n”,Headers假设为空不需要添加”\n”。
1.1. Content-MD5
Content-MD5 是指 Body 的 MD5 值,只要当 Body 非 Form 表单时才计算 MD5,计算方式为:
// bodyStream 为字节数组。
Stringcontent-MD5=Base64.encodeBase64(MD5(bodyStream.getbytes("UTF-8")));
1.2. Headers
Headers 是指参与 Headers 签名计算的 Header 的 Key、Value 拼接的字符串,建议对 X-Ca 开头以及自定义Header 计算签名,注意如下参数不参与 Headers 签名计算:X-Ca-Signature、X-Ca-Signature-Headers、Accept、Content-MD5、Content-Type、Date。
Headers 组织方法: 先对参与 Headers 签名计算的 Header的Key 依照字典排序后使用如下方式拼接,假设某个 Header 的 Value 为空,则使用 HeaderKey + “:” + “\n”参与签名,需要保留 Key 和英文冒号。
Stringheaders=
HeaderKey1+":"+HeaderValue1+"\n"+
HeaderKey2+":"+HeaderValue2+"\n"+
...
HeaderKeyN+":"+HeaderValueN+"\n"
将 Headers 签名中 Header 的 Key 使用英文逗号分割放到 Request 的 Header 中,Key为:X-Ca-Signature-Headers。
1.3. Url
Url 指 Path + Query + Body 中 Form 参数,组织方法:对 Query+Form 参数依照字典对 Key 停止排序后依照如下方法拼接,假设 Query 或 Form 参数为空,则 Url = Path,不需要添加 ?,假设某个参数的 Value 为空只保留 Key 参与签名,等号不需要再参与签名。
Stringurl=
Path+
"?"+
Key1+"="+Value1+
"&"+Key2+"="+Value2+
...
"&"+KeyN+"="+ValueN
注意这里Query或Form参数的Value可能有多个,多个的时候只取第一个 Value 参与签名计算。
2. 计算签名
MachmacSha256=Mac.getInstance("HmacSHA256");
// secret为AppSecret
byte[]keyBytes=secret.getBytes("UTF-8");
hmacSha256.init(newSecretKeySpec(keyBytes,0,keyBytes.length,"HmacSHA256"));
Stringsign=newString(Base64.encodeBase64(hmacSha256.doFinal(stringToSign.getBytes("UTF-8")),"UTF-8"));
3. 传送签名
将计算的签名结果放到 Request 的 Header 中,Key为:X-Ca-Signature。
4. 签名排查
当签名校验失败时,API网关会将效劳端的 StringToSign 放到 HTTP Response 的 Header 中返回到客户端,Key为:X-Ca-Error-Message,只需要将本地计算的 StringToSign 与效劳端返回的 StringToSign 停止对比即可找到问题;
假设效劳端与客户端的 StringToSign 一致请检查用于签名计算的密钥是否正确;因为 HTTP Header 中无法表示换行,因而 StringToSign 中的换行符都被过滤掉了,对比时请忽略换行符。
5. Java验证示例
privateApiRequesttOApiRequest(HttpServletRequestrequest){
ApiRequestapiRequest=newApiRequest(HttpMethod.POST_FORM,request.getRequestURI());
//验签
StringsignHeaders=request.getHeader("X-Ca-Signature-Headers");
ListsignHeaderList=newArrayList<>(16);
if(StringUtils.isNotBlank(signHeaders)){
signHeaderList=Arrays.asList(signHeaders.split(","));
for(StringheaderName:signHeaderList){
if(StringUtils.isNotBlank(headerName)){
apiRequest.addHeader(headerName,request.getHeader(headerName));
}
}
}
apiRequest.addHeader("Accept",request.getHeader("Accept"));
apiRequest.addHeader("Content-MD5",request.getHeader("Content-MD5"));
apiRequest.addHeader("Content-Type",request.getHeader("Content-Type"));
apiRequest.addHeader("Date",request.getHeader("Date"));
MapqueryParams=newHashMap<>(8);
MaprequestParams=request.getParameterMap();
if(requestParams.size()>0){
for(Map.Entryparam:requestParams.entrySet()){
queryParams.put(param.getKey(),param.getValue().length>0?param.getValue()[0]:"");
apiRequest.addParam(param.getKey(),param.getValue().length>0?param.getValue()[0]:"",
ParamPosition.QUERY,true);
}
}
returnapiRequest;
}
第四步:接口受权
选择版本管理下的“模型与权限”,假设您需要访问租户的手机号等信息,则需要开通对应效劳
|