1. 基础约束¶
插件工程命名 开发插件时,首先要新建一个插件的子module,插件工程的名字统一为 sync-plugin-xxxx,名称统一小写,如果名称是2个单词,建议用中线“-”分开。例如:IDP4Scim 命名为:sync-plugin-idp4-scim;
插件包依赖 新建子模块也是maven工程,该工程的父模块为:idp4-sync ;其依赖的工程只能有:sync-core ,不建议依赖其他工程
插件包命名统一为:com.idsmanager.idp.sync.plugin.xxxx ;例如IDP4的插件包命名为:com.idsmanager.idp.sync.plugin.idp4
插件包统一依赖核心包sync-core,最低版本为:4.22.0-GA
2. 自定义来源插件¶
demo地址:https://github.com/aliyun-idaas/connector-source-demo
2.1. 工程说明¶
插件中主要分为 “数据实体、数据映射、配置文件、同步逻辑”模块。 其中各模块中重要的类有: DemoSourceConnectionMetadata.java 数据源连接的基础方法类,同步引擎根据数据源提供的文档实现各种查询方法。 MetaBaseConstant.java 该类主要是定义插件的名称,中文名,初始化默认映射名称及描述信息。 _DemoConfiguration.java 为 数据源各项页面配置,页面提交的数据源配置参数会落到该类。 DemoAccountEntity.java DemoOrganizationEntity.java 数据源账户和组织机构的字段。 DemoSourceDataPullClient.java** ** 数据源拉取数据类,其中会实现数据源API文档中拉取数据的逻辑,包括增量全量逻辑。 DemoAttributeGetter.java 来源字段映射为目标字段的类。
2.2. 开发流程¶
2.2.1. 基本配置修改¶
2.2.1.1. 修改工程包名、类名、pom修改¶
修改模块名称,将sync-plugin-source-demo工程中的demo,修改为具体业务名称,例如:
修改包名及相关类名
修改pom.xml中的artifactId
2.2.1.2. 定义SourceConnectionMetadata类(例如DemoSourceConnectionMetadata)¶
2.2.1.2.1. 定义插件主类型及子类型¶
定义其自定义主类型和子类型,其子类型在MetaBaseConstant.java中定义,在定义子类型后在Connector中选择来源目标时其类型显示为MINORTYPE_NAME_CN(MINORTYPE_NAME)
注意:MetaBaseConstant也是插件中定义常量的类,如果有自定义的常量,也写在该类中
2.2.1.2.2.定义SCHEMA参数¶
在resource/schema下有相关的来源source_【业务】.json文件,在里面根据schema规范添加相应的字段及类型
2.2.1.2.3. SCHEMA定义后的参数校验¶
在SourceConnectionMetadata类中重写实现doValidate方法,在其中实现校验参数的逻辑
2.2.1.2.4. SCHEMA定义后的参数赋值¶
在SourceConnectionMetadata类中重写实现parseJsonConfig方法,在其中实现校验参数的逻辑
2.2.1.2.5. 定义拉取模式¶
在SourceConnectionMetadata类中重写实现loadPullScheme方法,在其中实现校验参数的逻辑,一共分为三种模式: CLASSIC(经典):组织机构->用户;机构和用户在拉取时,无法区分上下级; AD_SCHEME(活动目录模式)(默认):自顶向下递归全量拉取组织机构,增量拉取删除的用户,增量拉取删除的机构,增量拉取未删除的用户 PAGE_SCHEME(分页模式):分页实现拉取组织、用户数据 设置为何种模式在后面的拉取实现中在相关方法中进行实现。
2.2.1.2.6. 定义其他参数¶
在SourceConnectionMetadata类中还可定义相关的插件描述,icon图标设置等
2.2.2. 实现拉取方法的编写¶
2.2.2.1. 修改同步拉取数据方法¶
拉取数据源的数据操作实现都在**DemoSourceDataPullClient**。
context 数据推送上下文对象,可以获取当前正在推送的任务、来源、目标、任务批次ID等信息,也能获取当前的同步引擎核心对象的实例(DataTransformEngine) ,里边封装了很多目标插件需要的接口,具体可查看代码注释。
数据拉取的具体业务逻辑,不同插件要求各部相同,就需要插件的实现者,根据项目需求,酌情实现。这里强调一点:**无论同步哪一种对象,目标系统一定要提供一个该对象的唯一不变字段,用于我们识别同一个对象;具体有以下4个方法
**pullRegularData **
拉取常规的指定对象类型的数据。一次性拉取数据无序。对应“经典目录模式(无序)”,对应上述CLASSIC模式
pullFullRegularOneLevelChildren(默认)
全量拉取指定的组织和用户。 同步引擎会从根一层一层调用该方法,直至数据不再有下级数据,适用于活动目录类型的数据源。(保证数据质量,不要尾首相接,导致无限循环) 拉取人员时会先一层一层先拉组织,再同时拉取组织下的人员。对应“活动目录模式(有序)”,对应上述AD_SCHEME模式
pullFullRegularChildrenByPage:
分页模式拉取数据,,对应上述PAGE_SCHEME模式
2.2.3. 来源数据转换¶
2.2.3.1. 定义来源接受数据¶
修改DemoSourceAccountEntity, DemoSourceOrganizationEntity类改为相应来源数据接收类,里面首先定义来源的数据字段,类的属性为具体字段。 DemoSourceBaseEntity为定义继承的抽象类,定义了一些默认参数,一般情况下可以不用管。 DemoTargetAccountEntity和DemoTargetOrganizationEntity为同步到目标所需要的实际字段,同时有一些同步必须的字段,实现了TargetDataItem的接口,需要返回什么值,详见相应方法上的**TODO**。
2.2.4. 修改字段映射和模版映射¶
2.2.4.1. 定义需要映射的字段¶
在SourceDefaultAttributeDefiner中(如DemoSourceDefaultAttributeDefiner)去定义需要映射的字段,该处分为组织、组、用户三类映射
2.2.4.2. 定义默认初始化自动映射模版¶
在attribute包下建立一个来源到目标的初始关系映射类(如DemoToIDP4Initializer)去定义需要映射的字段,首先继承FieldMappingTemplateInitializer在构造方法中定义来源及目标的主子类型,然后在后面根据需要的类型,处理组织、组、用户三类需要的字段映射。
¶
3. 自定义目标插件¶
demo地址:https://github.com/aliyun-idaas/connector-target-demo
3.1. 基本配置修改¶
3.1.1. 修改工程包名、类名、pom修改¶
修改模块名称,将sync-plugin-target-demo工程中的demo,修改为具体业务名称
修改包名及相关类名
修改pom.xml中的artifactId
3.1.2. 定义TargetConnectionMetadata类(例如DemoTargetConnectionMetadata)¶
3.1.2.1. 定义插件主类型及子类型¶
定义其自定义主类型和子类型,其子类型在MetaBaseConstant.java中定义,在定义子类型后在Connector中选择来源目标时其类型显示为MINORTYPE_NAME_CN(MINORTYPE_NAME) 注意:MetaBaseConstant也是插件中定义常量的类,如果有自定义的常量,也写在该类中
3.1.2.2. 定义SCHEMA参数¶
在resource/schema下有相关的来源target_【业务】.json文件,在里面根据schema规范添加相应的字段及类型
3.1.2.3. SCHEMA定义后的参数校验¶
在TargetConnectionMetadata类中重写实现doValidate方法,在其中实现校验参数的逻辑
3.1.2.4. SCHEMA定义后的参数赋值¶
在TargetConnectionMetadata类中重写实现parseJsonConfig方法,在其中实现校验参数的逻辑
3.1.2.5. 定义其他参数¶
在TargetConnectionMetadata类中还可定义相关的插件描述,icon图标设置等
3.2. 实现拉取方法的编写¶
3.2.1. 修改同步推送方法¶
ApiResult push(TargetDataItem item, DataTransformContext context, DataRelation relation) throws SCIMException; 该方法用于实现真正的向目标推送数据。
TargetDataItem item 里边是具体需要进行推送的数据对象的实例,已经完成字段映射。
这个对象是通过newTargetDataItem方法,由引擎模块sync-engine实例化的一个对象; 如果当前推送的是用户,那么 item 就是映射后的用户的具体信息;如果是机构,那么item 就是映射后的机构的具体信息。
context 数据推送上下文对象,可以获取当前正在推送的任务、来源、目标、任务批次ID等信息,也能获取当前的同步引擎核心对象的实例(DataTransformEngine) ,里边封装了很多目标插件需要的接口,具体可查看代码注释。
DataRelation relation 数据映射对象。很多场景下,同一个对象(例如用户),在来源和目标两个系统里的唯一标识,并不相同,IDsConnector支持维护映射关系。如果relation 对象不为空,代表系统中已经存在了映射关系。
例如同步到钉钉的时候,组织的唯一id是钉钉自己生成的,无法使用idp的外部id,又因为id中已经创建好了组织,钉钉的唯一id无法回写到idp中。那么这个组织唯一id的关联关系,就可以使用设个对象进行维护,并写入到数据库中,这样就能在做更新或删除操作时,能正确的找到钉钉的唯一id来执行相应操作。
数据推送的具体业务逻辑,不同插件要求各部相同,就需要插件的实现者,根据项目需求,酌情实现。这里强调一点:**无论同步哪一种对象,目标系统一定要提供一个该对象的唯一不变字段,用于我们识别同一个对象;
强烈建议,目标系统要提供根据该唯一标识字段,查询用户是否存在的接口,针对新增和更新操作,我们都是优先查询对象是否存在,存在则更新,不存在则新增(无论来源传递的操作是新增还是更新)。**同步任务中有个配置叫“是否开启检查模式”【 DataTransformTask类的 boolean isCheckModel() 方法】,在demo中,有使用示例。
插件中主要分为 “数据实体、数据映射、配置文件、同步逻辑”模块。 其中各模块中重要的类有: DemoTargetConnectionMetadata.java 目标系统连接的基础方法类,同步引擎根据目标系统提供的文档实现各种查询方法。 MetaBaseConstant.java 该类主要是定义插件的名称,中文名,初始化默认映射名称及描述信息。 _DemoConfiguration.java _ 目标系统各项页面配置,页面提交的目标系统配置参数会落到该类。 DemoTargetAccountEntity.java DemoTargetOrganizationEntity.java 目标系统账户和组织机构的字段。 DemoTargetDataPushClient.java** ** 目标系统推送数据类,其中会实现目标系统API文档中推送数据的逻辑,包括增量全量逻辑。 DemoAttributeSetter.java 把来源的属性映射为目标字段的属性。
3.3. 修改字段映射和模版映射¶
3.3.1. 定义需要映射的字段¶
在TargetDefaultAttributeDefiner中(如DemoTargetDefaultAttributeDefiner)去定义需要映射的字段,该处分为组织、组、用户三类映射
3.3.2.定义默认初始化自动映射模版¶
在attribute包下建立一个来源到目标的初始关系映射类(如IDP4ToDemoFiledsInitializer)去定义需要映射的字段,首先继承FieldMappingTemplateInitializer在构造方法中定义来源及目标的主子类型,然后在后面根据需要的类型,处理组织、组、用户三类需要的字段映射。
4. 插件schema接口说明¶
自定义的来源插件和目标插件,需要填写的表单,由后端提供相应的schema来实现,存储到数据库时,也是以json串的形式进行存储。
4.1. 接口定义:¶
4.1.1. 获取源插件schema¶
4.1.1.1. 基本信息¶
Path:/api/sync/v1/connection/source/schema/{minorType} **Method:**GET
4.1.1.2. 请求参数:¶
名称 |
类型 |
是否必须 |
说明 |
---|---|---|---|
minorType |
String |
是 |
插件名字 |
4.1.1.3. 返回数据¶
名字 |
类型 |
备注 |
---|---|---|
data |
object |
schema内容 |
message |
string |
错误信息 |
success |
boolean |
是否成功 |
code |
string |
错误码 |
4.1.1.4. 返回数据示例:¶
{
"status": 0,
"message": "操作成功",
"requestId": "FCD94521-2BBF-4345-9902-BD8C09C8E81D",
"data": {
"detailsData": [
{
"name": {
"en": "enterpriseId",
"zh": "enterpriseId"
},
"show": false,
"description": {
"en": "",
"zh": ""
},
"key": "instanceId"
},
{
"name": {
"en": "uuid",
"zh": "uuid"
},
"show": false,
"description": {
"en": "",
"zh": ""
},
"key": "applicationUuid"
},
{
"name": {
"en": "Application Logo",
"zh": "应用图标"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "logoUuid"
},
{
"name": {
"en": "Application ID",
"zh": "应用ID"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "purchaseId"
},
{
"name": {
"en": "Application Name",
"zh": "应用名称"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "name"
},
{
"name": {
"en": "Login URL",
"zh": "登录 URL"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "loginPageUrl"
},
{
"name": {
"en": "Form Submit URL",
"zh": "登录提交 URL"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "loginUrl"
},
{
"name": {
"en": "Username Name Attribute",
"zh": "登录名属性名称"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "usernameField"
},
{
"name": {
"en": "Password Name Attribute",
"zh": "登录密码属性名称"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "passwordField"
},
{
"name": {
"en": "Logon Button Name Attribute",
"zh": "登录按钮属性名称"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "submitField"
},
{
"name": {
"en": "Other Logon Information",
"zh": "登录其他信息"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "othersPageContent"
},
{
"name": {
"en": "Logon Success Page",
"zh": "登录成功跳转地址"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "loginSuccessPage"
},
{
"name": {
"en": "Application Status",
"zh": "应用状态"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "enabled",
"textFormat": "enabled"
},
{
"name": {
"en": "Account Linking Type",
"zh": "账户关联方式"
},
"show": false,
"description": {
"en": "",
"zh": ""
},
"key": "templateDto.templateName"
},
{
"name": {
"en": "Creator",
"zh": "创建人"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "creator"
},
{
"name": {
"en": "CreateTime",
"zh": "创建时间"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "createTime"
}
]
},
"success": true,
"code": "0"
}
4.1.2. 获取目标插件schema¶
4.1.2.1. 基本信息¶
Path:/api/sync/v1/connection/target/schema/{minorType} **Method:**GET
4.1.2.2. 请求参数:¶
名称 |
类型 |
是否必须 |
说明 |
---|---|---|---|
minorType |
String |
是 |
插件名字 |
4.1.2.3. 返回数据¶
名字 |
类型 |
备注 |
---|---|---|
data |
object |
schema内容 |
message |
string |
错误信息 |
success |
boolean |
是否成功 |
code |
string |
错误码 |
4.1.2.4. 返回数据示例:¶
{
"status": 0,
"message": "操作成功",
"requestId": "FCD94521-2BBF-4345-9902-BD8C09C8E81D",
"data": {
"detailsData": [
{
"name": {
"en": "enterpriseId",
"zh": "enterpriseId"
},
"show": false,
"description": {
"en": "",
"zh": ""
},
"key": "instanceId"
},
{
"name": {
"en": "uuid",
"zh": "uuid"
},
"show": false,
"description": {
"en": "",
"zh": ""
},
"key": "applicationUuid"
},
{
"name": {
"en": "Application Logo",
"zh": "应用图标"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "logoUuid"
},
{
"name": {
"en": "Application ID",
"zh": "应用ID"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "purchaseId"
},
{
"name": {
"en": "Application Name",
"zh": "应用名称"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "name"
},
{
"name": {
"en": "Login URL",
"zh": "登录 URL"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "loginPageUrl"
},
{
"name": {
"en": "Form Submit URL",
"zh": "登录提交 URL"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "loginUrl"
},
{
"name": {
"en": "Username Name Attribute",
"zh": "登录名属性名称"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "usernameField"
},
{
"name": {
"en": "Password Name Attribute",
"zh": "登录密码属性名称"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "passwordField"
},
{
"name": {
"en": "Logon Button Name Attribute",
"zh": "登录按钮属性名称"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "submitField"
},
{
"name": {
"en": "Other Logon Information",
"zh": "登录其他信息"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "othersPageContent"
},
{
"name": {
"en": "Logon Success Page",
"zh": "登录成功跳转地址"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "loginSuccessPage"
},
{
"name": {
"en": "Application Status",
"zh": "应用状态"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "enabled",
"textFormat": "enabled"
},
{
"name": {
"en": "Account Linking Type",
"zh": "账户关联方式"
},
"show": false,
"description": {
"en": "",
"zh": ""
},
"key": "templateDto.templateName"
},
{
"name": {
"en": "Creator",
"zh": "创建人"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "creator"
},
{
"name": {
"en": "CreateTime",
"zh": "创建时间"
},
"show": true,
"description": {
"en": "",
"zh": ""
},
"key": "createTime"
}
]
},
"success": true,
"code": "0"
}
4.2. 后端插件文件说明:¶
在插件代码目录resources目录下,建立schema目录,按照下图所示,建立schema文件:
其中:
source_xxx.json 为源schema定义文件
target_xxx.json 为目标schema定义文件
xxx为插件ID,和MinorType类中定义的label一直,注意大小写
注意启用了shema配置的插件,插件命名必须为插件ID_SCHEMA
文件内容请参考sync-plugin-amdp项目,示例如下:
{
"form":{
"baseURL": "",
"authType": "oauth2",
"clientId": "",
"clientSecret": "",
"apiVersion":""
},
"template":{
"formData": [
{
"key": "baseURL",
"name": { "zh": "baseURL", "en": "baseURL" },
"type": "input",
"readonly": false,
"placeholder": { "zh": "请输入Basic URL", "en": "请输入Basic URL" },
"rules": { "required": true, "message": "请输入Basic URL", "trigger": "blur" },
"show": true,
"send": true
},
{
"key": "authType",
"name": { "zh": "认证方式", "en": "认证方式" },
"type": "radio",
"options": [
{ "label": "oauth2", "value": { "zh": "OAuth2", "en": "OAuth2" }, "disabled": false }
],
"placeholder": { "zh": "请输入Basic URL", "en": "请输入Basic URL" },
"rules": { "required": true, "message": "请输入Basic URL", "trigger": "blur" },
"show": true,
"send": true
},
{
"key": "clientId",
"name": { "zh": "Client ID", "en": "Client ID" },
"type": "input",
"readonly": false,
"placeholder": { "zh": "请输入Client ID", "en": "请输入Client ID" },
"rules": { "required": true, "message": "请输入Client ID", "trigger": "blur" },
"show": true,
"send": true
},
{
"key": "clientSecret",
"name": { "zh": "Client Secret", "en": "Client Secret" },
"type": "input",
"readonly": false,
"placeholder": { "zh": "请输入Client Secret", "en": "请输入Client Secret" },
"rules": { "required": true, "message": "请输入Client Secret", "trigger": "blur" },
"show": true,
"send": true
},
{
"key": "apiVersion",
"name": { "zh": "版本号", "en": "版本号" },
"type": "input",
"readonly": false,
"placeholder": { "zh": "请输入IDP4版本号", "en": "请输入IDP4版本号" },
"rules": { "required": true, "message": "请输入IDP4版本号", "trigger": "blur" },
"show": true,
"send": true
}
]
}
}
5、打包及调试¶
5.1 插件打包¶
5.1.1 无三方依赖¶
DEMO插件工程使用Maven进行管理,在开发插件后,需要使用Maven命令来进行打包生成应用插件jar, 在工程的pom.xml文件所在目录,使用Maven 命令:
mvn clean package
运行成功后在target目录以sync-plugin-{type:[source/target/空(源和目标可放一起)]}-{应用}-{version}.jar
的文件生成即是插件jar。
考虑到Connector插件开发时有可能依赖其他第三方lib来完成插件开发(即在pom.xml中会加入第三方依赖dependency),在打包时需要分两种情况来处理,具体如下:
5.1.2 有三方依赖包¶
对于有第三方lib依赖的Connector插件,需要配置使用Maven的assembly插件来将指定的lib一起打包到一个jar中。 假设我们需要依赖一个叫 jjwt的第三方lib库, 首先在 pom.xml中加入依赖,如下图:
再在src/mian/assembly
目录增加assembly-plugin.xml
插件配置(在DEMO工程中已经有此插件配置,注释取消即可),如下图:
标签中的内容格式为:{groupId}:{artifactId}。 最后再使用Maven的打包命令生成对应的插件jar文件即可,如下图:
**注意:**此时生成的插件完整jar文件为:sync-plugin-demo-{version}-jar-with-plugin.jar
,即图中的sync-plugin-source-demo-1.0.0-jar-with-plugin.jar
文件(此时的sync-plugin-source-demo-1.0.0.jar
只包含工程本身代码,在没有额外第三方依赖,也就是只依赖了Connector核心包,这个插件包也是可以使用的)。
5.2 插件上传¶
使用ssh文件上传工具,这里我使用的xftp,登录到服务器上,到Connector插件目录(插件目录是Connector工作目录下的plugins文件夹${workdir}/plugins),假如Connector的工作目录为:/data/applications/idp4-microservice/connector
对应的Connector插件目录:/data/applications/idp4-microservice/connector/plugins
,这里对应上传打成的jar包到该目录下
注意:若已经有相同的插件存在,需要移除之前的插件,若版本号变大,没有删除之前相同的插件,可能导致新上传的插件不可用。
5.3 插件调试¶
5.3.1. Connector插件本地调试¶
若有 Connector 源码可以在本地调试;
先在本地 mvn clean install
开发的插件,再在 sync-application 模块的 pom.xml 中引入该插件,即可运行或调试。
**注意:**本地调试没有通过后,需删除插件在sync-application 模块的 pom.xml的引入,Connector只用于本地调试,不要做代码提交。
5.3.2. Connector插件远程调试(推荐)¶
需要先有一套完整的 已经完成插件化改造的Connector 运行环境,上传插件到插件目录后,Connector服务需要开启远程调试:在启动命令增加以下参数:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9006
以docker-compose部署为例在command指令下增加上述参数,如下:
再重启connecor
#停止connecor容器
docker stop connecor
#使用docker-compse 启动 connecor
docker-compose --compatibility -f properties/docker-compose.yml.template up -d connecor