插件代码标准
插件:package.json
必须,插件配置描述文件
{
"entrance": "", /*后台管理入口*/
"mobile-entrance": "", /*移动后台管理入口*/
"jump-prefixes": { /*用于生成跳转 url 并附带 call_token 的链接地址配置项*/
"common": "", /*通用地址,如果下述项有未配置的,以 common 为准*/
"mobile-backend-page": "", /*后台页跳转地址*/
"backend-page": "" /*后台页跳转地址*/
"page": "", /*前台页跳转地址,暂无应用*/
},
"proxy-prefixes": { /*代理配置信息*/
"common": "", /*通用地址,如果下述项有未配置的,以 common 为准*/
"mobile-backend-page": "", /*后台页转发原始地址*/
"backend-page": "", /*后台页转发原始地址*/
"backend-api": "", /*后台接口转发原始地址*/
"page": "", /*前台页转发原始地址*/
"api": "" /*前台接口转发原始地址*/
},
"oauth-callback-url": "" /*OAuth 回调地址(必须以https://开头),可不填*/
}
例如:
快站地址 kuaizhan.com ;
某插件 code 为 example,域名为 example.com ;
某快站站点域名为 test.kuaizhan.com;
该插件提交的配置信息如下:
{
"entrance": "/admin/",
"mobile-entrance": "/admin/mobile/",
"jump-prefixes": {
"mobile-backend-page": "http://m.example.com",
"backend-page": "http://example.com",
},
"proxy-prefixes": {
"common": "http://example.com",
"mobile-backend-page": "http://example.com/mobile-backend",
"backend-page": "http://example.com/backend",
"backend-api": "http://example.com/bcapi",
"page": "http://example.com/front_page",
"api": ""
}
}
-
0.因存在
jump-prefixes配置,当站长在建站后台点击插件“进入”时将访问http://www.kuaizhan.com/plugin/backend?site_id=xxxxxx&plugin_id=example
该页面会显示快站导航工具条,并在页面中放置一个
iframe,iframe 地址是 http://example.com/admin/?call_token=xxxxxxxxxxx 。call_token用于验证请求是否合法,在后面会描述。 -
1.若不存在
jump-prefixes配置,当站长在建站后台点击插件“进入”时将访问http://www.kuaizhan.com/plugin/page-proxy/example/admin/
而快站服务器会访问 http://example.com/backend/admin/ (以同样的 HTTP 请求 method,包含 url query string,包含特定的 cookie 指明站点身份,去掉其他 headers),并将原始内容(去掉 Set-Cookie 等 headers)加上快站后台的头尾后返回站长。
-
2.当站长访问页面
http://www.kuaizhan.com/plugin/page-proxy/example/any/page
而快站服务器会访问 http://example.com/backend/any/page (以同样的 HTTP 请求 method,包含 url query string,包含特定的 cookie 指明站点身份,去掉其他 headers),并将原始内容加上快站后台的头尾后返回给站长。
-
3.当站长调用接口
http://www.kuaizhan.com/plugin/api-proxy/example/one/api
而快站服务器会访问 http://example.com/bcapi/one/api (以同样的 HTTP 请求 method,包含 url query string,包含特定的 cookie 指明站点身份,去掉其他 headers),并将原始内容(去掉 Set-Cookie 等 headers)直接返回给站长。
-
4.当用户访问站点页面
http://test.kuaizhan.com/plugin/page-proxy/example/page.html
而快站服务器会访问 http://example.com/front_page/page.html (以同样的 HTTP 请求 method,包含 url query string,包含特定的 cookie 指明用户身份,去掉其他 headers),并将原始内容加上该站点的头尾后返回给用户。
如果不希望展示站点头部栏或导航栏,则传递参数
kz-header或kz-nav为 no ,或者在返回结果中带上X-Kz-No-Header或X-Kz-No-Nav头信息并内容不为空即可。 -
5.当用户调用接口
http://test.kuaizhan.com/plugin/api-proxy/example/hello_api
因
api配置为空,所以快站使用common配置,快站服务器会访问 http://example.com/hello_api (以同样的 HTTP 请求 method,包含 url query string,包含特定的 cookie 指明用户身份,去掉其他 headers),并将原始内容(去掉 Set-Cookie 等 headers)直接返回给用户。
安全验证:call_token
call_token 是快站做上述页面、接口的跳转、转发时,自动生成的一串字符串,用于将当前调用者身份、站点 id、用户 id、时间戳、随机串等信息告诉第三方系统,并会使用约定的 app_key 和 app_secret 做签名,以免请求被伪造。
call_token 的计算依赖于具体插件的 app_key 和 app_secret ,具体内容请咨询快站开发团队。
call_token 分为 payload 和 signature 两部分,完整结构为:payload.signature
其中,payload 是构造字符串 kuaizhan:site_id:user_id:timestamp:nonce 被 url-safe-base64 计算后的字符串(因调用方是快站,所以第一项为 kuaizhan 如果是第三方调用快站的接口,则第一项应为约定的 app_key)。
signature 则为构造字符串 app_key:site_id:user_id:timestamp:nonce 使用 HMAC-SHA1 算法以约定的 app_secret 为 key 计算后的哈希值,再使用 url-safe-base64 计算后的字符串。请注意计算 signature 时永远使用 app_key 做字符串第一项。
call_token 示例:
a3VhaXpoYW46NzEyODM5NzU5NTozNDoxNDMxNjgyNzU3OlhkQXludWREVUdOd2l4QUU.MmE4ZmIwOGRjZWYxYWJlYmZhOTgwNzdhNDc5YjFkNTUyODBmNzE4MA
其中 payload 部分 decode 之后为:
kuaizhan:7128397595:34:1431682757:XdAynudDUGNwixAE
url-safe-base64 算法为:普通 base64 计算后,用 - 和 _ 符合分别替换 + 和 / 符号,并去掉结尾的 = 符号。encode 和 decode 的 php 示例代码(来自 php manual )如下:
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function base64url_decode($data) {
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
当采用 iframe 方式嵌入后台页时,call_token 存在于 iframe url 的 query string 中。当采用 proxy 方式调用页面或接口时,快站会在请求第三方时将 call_token 放在 HTTP 的 Cookie 中,键值为 kz_call_token 。
出于安全考虑,请第三方系统务必验证 call_token 的合法性,包括:
-
使用规则计算签名,检查签名是否正确;
-
只接受
timestamp在当前时间一定范围内的请求,超出范围的视为过时call_token; -
记录当前时间下一定时间范围内的
nonce,不允许接下来一定时间范围内的nonce出现重复。
OAuth服务
只适用于提供了 oauth-callback-url 的插件。
数据字典
* <site_id> 快站用户站点ID, BIGINT(10)
* <plugin_id> 插件ID
* <app_key> 由快站分配,用于唯一标识使用OAuth服务的应用
* <app_secret> 有快站分配,用于验证应用身份,切记保密
* <scope_list> 所需权限列表,每个权限是一个固定字符串,每个权限直接以英文空格隔开
* <access_token> oauth服务标识所授予权限的随机唯一字符串,有效期为7天
* <refresh_token> 用于在<access_token>过期时,重新获取<access_token>唯一随机字符串,<refresh_token>有效期为永不过期
* <code> 插件方后台获取<access_token>、<refresh_token>的凭证,是一个随机唯一的字符串,有效期为10分钟,且不可重复使用。
用户授权过程
- 将用户重定向至快站账号授权页面
/plugin/oauth-authorize?site_id=<site_id>&plugin_id=<plugin_id>&scope=<scope_list>。 - 用户会在该授权页面上看到插件所请求的权限信息,如果用户同意授权,则输入用户本人账号及密码点击登录授权。
- 快站验证用户所输入账号密码如有误,会以弹窗的方式提示用户。如果用户账号及密码验证无误,快站会生成所授权权限对应的
<access_token>,<refresh_token>并生成一个唯一的随机码<code>, 将用户浏览器重定向到oauth-callback-url所指定的地址,并带上参数code=<code> - 插件方在
oauth-callback-url中调用/oauth/token接口获取<access_token>,<refresh_token>,并存储这些token以备用。
scope列表
- user.info 获取当前用户信息
调用OAuth接口
获取到access_token后,即可使用使用access_token调用相关oauth接口,详见oauth接口文档说明。
刷新access_token
在access_token过期后,需使用refresh_token重新获取access_token,详见接口2.重新获取access_token接口。
相关接口列表
1.通过code获取access_token接口
- 协议: HTTPS
- 方法: POST
- URL: /oauth/token
- 参数:
- grant_type 固定值authorization_code
- code
<code>详见数据字典
- 请求头
- Authorization 值为
"Basic" + " " + base64("<app_key>"+":"+"<app_secret>"), 加号表示字符串连接。
- Authorization 值为
-
返回:
-
获取成功时返回:
- 返回码 200
- 返回内容示例:
{ "access_token": "0d526b30c03532038cb7638acab0609969866fc7", "expires_in": 604800, "token_type": "bearer", "scope": "user.info", "refresh_token": "f8e8c5fa97cc3cf74afff85468d3ae883f5b4325" }* 其中:expires_in的值为acces_token有效期; scope的值为该access_token对应的权限列表 -
异常情况:
- 返回码 >= 400, < 500
- 返回数据示例:
{ "error":"invalid_grant", "error_description":"Authorization code doesn't exist or is invalid for the client" }
-
2.重新获取access_token接口
- 协议: HTTPS
- 方法: POST
- URL: /oauth/token
- 参数:
- grant_type 固定值refresh_token
- refresh_token
<refresh_token>详见数据字典
- 请求头
- Authorization 值为"Basic " + base64("
"+":"+" "), 加号表示字符串连接,注意Basic后面有一空格。
- Authorization 值为"Basic " + base64("
-
返回:
-
获取成功时返回:
- 返回码 200
- 返回内容示例:
{ "access_token": "062ebe8a495473ca504ddec58a47dbdfca3b9173", "expires_in": 604800, "token_type": "bearer", "scope": "user.info" }* 其中:expires_in的值为acces_token有效期; scope的值为该access_token对应的权限列表 -
异常情况:
- 返回码 >= 400, < 500
- 返回数据示例:
{ "error": "invalid_grant", "error_description": "Invalid refresh token" }
-
3.获取用户信息接口
- 协议: HTTPS
- 方法: GET
- URL: https://api.kuaizhan.com/oauth/user-info
- 参数:
- access_token
<access_token>详见数据字典
- access_token
- 请求头
- 无
-
返回:
-
获取成功时返回:
- 返回码 200
- 返回内容示例:
{ "ret": 0, "msg": "", "data": { "user_id": "3505" } }* 其中:ret为0时表示操作成功。非0 为操作失败, msg在操作失败时提供错误信息。data为改操作返回数据。 -
异常情况:
- 返回码 200
- 返回数据示例:
{ "ret": 1, "msg": "invalid access_token", "data": {} }
-
组件:基础 package.json
{
"name": "divider", /*组件标识,填写组件所在目录的名称*/
"title": "分隔符", /*中文名*/
"image_icon": "", /*组件的图标*/
"html": "" /*用于将用户数据输出为html的模板*/
}
组件:扩展 package.json
说明:在描述了基础package.json之后,如果使用可配置的基类configurableComponent,可以通过添加配置项,自动生成配置框UI,
- 默认数据配置项,通过配置默认数据,可以定义用户产生数据的名称,默认值,数据类型,验证规则,例如:
"data_config": {
"width": { /*数据名*/
"type": "string", /*数据类型*/
"required": true, /*必填*/
"default": "50%", /*默认值*/
"vd_rules": { /*校验规则*/
"match": "/^[0-9]{1,3}%$/"
}
}
}
关于更多 vd_rules 的规则,参阅 开发API > VD
- UI渲染配置项,通过配置 widgets 属性,将自动创建配置窗口的UI,例如:
"widgets": {
"style":[{ /*样式选项卡*/
"data_name":"width", /*绑定到的data_config 中的name*/
"label": "宽度", /*显示的标签*/
"type": "slider", /*UI 类型*/
"opt": { /* UI 选项,根据不同的UI类型,选项也不同 */
"min": 0,
"max": 100,
"unit": "%"
}
}],
"property":[ /*属性选项卡*/
]
}
关于更多 widgets 的规则,参阅 开发API > UI
组件: editing.js
说明:editing.js为编辑器下组件的预览、配置控制类,在提交组件时,必须包括该文件
框架代码如下:
define(['configurableComponent'], function( Component) {
'use strict';
//初始化组件类,参数为组件配置,如果组件第一次创建,将传递空配置,如果组件为已经创建到视图窗口,重新加载,将传递已保存的配置
//覆盖接口函数
return Component.extend({
});
});
组件: portal.js
说明:portal.js为发布后加载执行的JS程序,非必须。
框架代码如下:
/*
*发布后执行JS
*/
define(['zepto'], function($) {
return {
//输出到发布页面,当用户正式发布后,调用此函数创建视图。
onAfterRender: function(el) {
}
}
});
注意: 如果需要在发布后加载portal.js 还需要在组件的package.json 中的html 数据 增加 {{progdata}} 标签。例如:
"html": "<div {{{_progdata_}}}></div>",
在页面构建时,这个标签会被替换为组件的 meta 内容
组件: style.css
说明:style.css 为组件的样式文件,在编辑器状态以及发布状态下都会加载,在编写 style.css 时,请参考如下标准:
-
禁止定义全局标签样式
-
禁止使用 !important
-
所有html结构需要包裹在一个外层容器中,外层容器的class 为 mod-插件名-组件名
例如:在插件sys-plugins 中,有组件button. 在编写html结构时,需要如下代码:
<div class="mod-sys-plugins-button"><button class="test-button">按钮</button></div>
对应的样式应该编写如下格式
.mod-sys-plugins-button{
}
.mod-sys-plugins-button .test-button{
width:80px;
height:30px;
}
组件: editing.css
说明: editing.css 为在编辑器中特殊的样式,在发布后页面不会加载,在编写 editing.css 时,请参考如下标准:
-
禁止定义全局标签样式
-
禁止使用 !important
-
所有html结构需要包裹在一个外层容器中,外层容器的class 为 mod-插件名-组件名
-
编辑器样式中需要使用
.phone-editing前缀 ,配置框中使用.configurator前缀
例如:在插件sys-plugins 中,有组件button. 在编写html结构时,需要如下代码:
<div class="mod-sys-plugins-button"><button class="test-button">按钮</button></div>
如果在预览框需要另外的样式显示,对应的样式应该编写如下格式
.phone-editing .mod-sys-plugins-button{
}
.phone-editing .mod-sys-plugins-button .test-button{
width:80px;
height:30px;
}
