1. Portal集成

1.1. 概述

协同平台的企业门户提供统一的入口和统一的用户,将业务系统、报表通过单点登录和页面集成方式有机的整合在一起。

  • 入口的集成

    通过菜单、空间、关联系统等方式为业务系统提供入口,基于角色进行权限控制,单点登录后进入业务系统。

  • 数据的集成

    通过栏目呈现业务数据和报表。

  • 消息的集成

    接收业务系统的消息,提醒用户进入异构系统进行处理。

同时,协同平台也可以作为栏目或整体被第三方Portal(如Websphere Portal、NC Portal)集成。

1.2. 页签集成

将第三方系统作为系统的一个页签,点击以后单点登录进入第三方系统的指定页面。

平台开发手册.Portal集成.页签集成.

1.2.1. Spring配置文件

实现页签需要建立2个Spring配置文件,下面以nc为例:

1.pluginCfg.xml配置文件

位置:【webapps\seeyon\WEB-INF\cfgHome\plugin\nc1】

<?xml version="1.0" encoding="UTF-8"?>
<plugin>
    <id>nc1</id>
    <name>NC插件</name>
    <category>54321</category>
</plugin>

注意:这里的文件名nc1应该与nc1一致。

2.Bean 配置文件

位置:【webapps\seeyon\WEB-INF\cfgHome\plugin\nc1\spring\spring-nc-manager.xml】

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans default-autowire="byName"> 
  <bean id="erp" class="com.seeyon.ctp.portal.sso.thirdpartyintegration.ThirdpartySpace" init-method="init"> 
    <!-- id必须唯 而且ID必须为数字否则点击页签会报错一 -->  
    <property name="id" value="54321"/>  
    <!-- 页签上显示的名称 -->  
    <property name="name" value="ERP空间"/>  
    <!-- 插件id,必须存在,如果不存在,请按下面的步骤定义一个新的插件 -->  
    <property name="pluginId" value="nc1"/>  
    <!-- 第三方系统登录地址,如果合并3、4步,可以省略 -->  
    <property name="loginURL" value="http://xxx.xxx.xxx.xxx/seeyon/extform.jsp"/>  
    <!-- 点击页签要打开的第三方系统页面地址 -->  
    <property name="pageURL" value="http://xxx.xxx.xxx.xxx/seeyon/info.jsp"/>  
    <!-- 打开方式,值为open时在新窗口打开,为workspace时在A8页面能打开 -->  
    <property name="openType" value="workspace"/>  
    <!-- 排序号 -->  
    <property name="index" value="4"/>  
    <!-- 授权 accessRoles与accessCheck选择其中一个即可-->  
    <property name="accessRoles"> 
      <list> 
        <value>GeneralStaff</value> 
      </list> 
    </property>  

   <!-- <property name="accessCheck" ref="accessCheck"/> -->  
  </bean>  
 <!-- <bean id="accessCheck" class="com.seeyon.ctp.ext.Tab.TestThirdpartyAccessCheck"></bean> -->  
</beans>

注意:上面accessRoles与accessCheck在作用都是授权,根据实际需求选择其一即可。

1.2.2. accessRoles与accessCheck授权区别

1.accessCheck授权:编码控制授权

accessCheck授权需要继承ThirdpartyAccessCheck,如果已经有方法获取当前自定义页签使用人员范围的方法可以直接在这里判断,如下:

import com.seeyon.ctp.portal.sso.thirdpartyintegration;
public class TestThirdpartyAccessCheck
  implements ThirdpartyAccessCheck
{
  public boolean check(long memberId)
  {
    boolean hasErpPermission = BasicInformation.getUserMapper(Long.valueOf(memberId));
    // 返回true的人员显示页签
    return hasErpPermission;
  }
}

2,accessRoles授权:基于角色授权

1.2.3. 注意事项

增加的页签并不会直接显示,需要有权限的人员在【个人事务】-【空间导航设置】中从备选空间进行选择。

1.3. 扩展菜单集成

扩展菜单,增加模块或第三方集成的入口。

1.3.1. 主菜单扩展

接口介绍

1、因V5 RBAC的特殊性,不再支持3.5插件菜单以下两个特性 :

  • 可通过管理员菜单权限管理控制菜单是否显示。
  • 可通过配置固定角色如FormAdmin控制权限。

以上两类需求可以通过超级管理员super-admin增加资源实现。

2、二次开发能够通过插件开发增加主菜单。

3、菜单项可以使用代码在运行期控制是否显示。

4、考虑到开发和实施的分离,为便于实施部署,不采用Web管理界面添加方式。

5、需要同时考虑降低3.5的二次开发迁移工作量。

6、沿袭3.5的实现,只支持一级和二级菜单的定义,不支持更多级别。

通过超级管理员配置来实现系统中主菜单的扩展功能,通过扩展来作为系统各模块或与第三方集成的入口。

支持以下菜单:

  • 前端用户的一级或多级菜单(0-N个)
  • 系统中其他任意角色的一级或多级菜单(0-N个)

配置实现主菜单扩展[V6.0产品不支持此扩展方式]

登录数据维护

开放目前标准产品屏蔽的账户,执行如下预制SQL:

-- 超级管理员人员
INSERT INTO org_member (ID,NAME,CODE,IS_INTERNAL,IS_LOGINABLE,IS_VIRTUAL,IS_ADMIN,IS_ASSIGNED,TYPE,STATE,IS_ENABLE,IS_DELETED,STATUS,SORT_ID,ORG_DEPARTMENT_ID,ORG_POST_ID,ORG_LEVEL_ID,ORG_ACCOUNT_ID,DESCRIPTION,CREATE_TIME,UPDATE_TIME) 
VALUES ('6725175934914479521','超级管理员','','1','1','0','1','1',NULL,'1','1','0','1','-1','-1','-1','-1','-1730833917365171641','','2012-08-27 00:00:00','2012-08-27 00:00:00');
-- 超级管理员账号
INSERT INTO org_principal VALUES ('28','super-admin','tAXR7dJpxqF5GptnLve33dzGGUs=','',NULL,'6725175934914479521','1','2012-08-27 00:00:00','2012-08-27 00:00:00');
-- 超级管理员的关系
INSERT INTO org_relationship (ID,TYPE,SOURCE_ID,OBJECTIVE0_ID,OBJECTIVE1_ID,OBJECTIVE5_ID,SORT_ID,ORG_ACCOUNT_ID,CREATE_TIME,UPDATE_TIME)
VALUES ('-8329422661916671085','Member_Role','6725175934914479521','-1730833917365171641','-3989205346588222483','Member','-1','-1730833917365171641','2012-08-27 00:00:00','2012-08-27 00:00:00');

重启服务后即可使用超级管理员账户登录,登录名为super-admin,密码为123456。

建议在完成菜单配置后,手动删除这三条记录并重启服务。

资源维护

使用超级管理员登录后,进入【产品打包管理】--【产品资源注册】

img

点击【新建】创建资源,截图为资源维护界面

img

自定义的资源保存成功后,在自定义资源列表中可以使用找到该资源,勾选即可进行【编辑】操作。

img

菜单维护

【产品打包管理】--【产品打包】

可以在左侧菜单树通过点击后新建菜单来创建二级菜单,或者直接点击菜单资源树来创建一级菜单。

img

这里可以通过选择是否为虚节点来控制点击是否产生动作,当选择不是虚节点时,需要配置页面资源名称,此时点击选择即可找到上一小节维护的自定义资源。

img

角色与菜单维护

菜单新建完成后,使用单位管理员或集团管理员登录,【组织模型管理】--【角色与权限设置】,可以新建或使用现有角色,通过分配资源来分配刚刚新建的菜单即可。

img

登录拥有该角色的人员,即可看见配置好的菜单,并且点击可以访问配置自定义资源的URL地址,至此菜单的扩展已经全部配置完成。

img

插件开发实现主菜单扩展

主菜单定义

在插件的Spring配置文件中增加下面的bean定义来增加主菜单。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="byName">
    <bean parent="pluginMainMenu">
        <!-- 主菜单ID,唯一的数字串 -->
        <property name="id" value="1111111111" />
        <!-- 主菜单名称 -->
        <property name="name" value="NC组织数据同步" />
        <!-- 主菜单排序号 -->
        <property name="sortId" value="1" />
        <!-- 二级菜单 -->
        <property name="children">
            <list>
                <bean class="com.seeyon.ctp.plugin.PluginMenu">  
                    <!-- 二级菜单ID,唯一的数字串 -->
                    <property name="id" value="1111111112" />
                    <!-- 二级菜单名称 -->
                    <property name="name" value="菜单权限控制" />                
                    <!-- 菜单打开方式 mainfrm:工作区; newWindow:新窗口-- >
                    <property name="target" value="mainfrm" />
                    <!-- 菜单图标 -->
                    <property name="icon" value="/apps_res/plugin/nc/images/2401.gif" />
                    <!-- 菜单地址,出于安全方面考虑此url必须是协同内部界面,如果想跳转请在内部界面实现跳转 -->
                    <property name="url" value="/index/indexController.do?method=searchAll" />
                    <!-- 菜单访问权限的验证类 -->
                    <property name="menuCheck">
                        <bean class="com.seeyon.ctp.menu.check.UserMenuCheck" />
                    </property>
                </bean>
            </list>
        </property>
        <!-- 菜单访问权限的验证类 -->
        <property name="menuCheck">
            <bean class="com.seeyon.ctp.menu.check.UserMenuCheck" />
        </property >
    </bean>
</beans>

每一个插件定义的菜单都是独立的主菜单,暂不支持在指定位置添加子菜单项。

主菜单权限控制

实现一个AbstractMenuCheck的实现类,通过传入的登录人员信息控制是否显示, 并配置到菜单定义的XML中。

集团管理员菜单权限控制代码示例:

package com.seeyon.ctp.menu.check;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class GroupAdminMenuCheck extends AbstractMenuCheck {

    private static final Log log = LogFactory.getLog(GroupAdminMenuCheck.class);

    @Override
    public boolean check(long memberId, long loginAccountId) {
        boolean isAdmin = false;
        //TODO判断是否是集团管理员
        return isAdmin;
    }
}

为了支持原有的userMenus、groupMenus、accountMenus、systemMenus等特性,系统缺省提供以下几个MenuCheck :

1.com.seeyon.ctp.menu.check.GroupAdminMenuCheck 集团管理员菜单权限校验

2.com.seeyon.ctp.menu.check.AccountAdminMenuCheck 单位管理员菜单权限校验

3.com.seeyon.ctp.menu.check.SystemAdminMenuCheck 系统管理员菜单权限校验

4.com.seeyon.ctp.menu.check.AuditAdminMenuCheck 审计管理员菜单权限校验

5.com.seeyon.ctp.menu.check.UserMenuCheck 非管理员菜单权限校验(普通用户)

6.com.seeyon.ctp.menu.check.MenuForAllUsersChecker 针对所有用户菜单全部验证通过

1.4. 添加自定义菜单

1、定义菜单项:

以文档菜单为例,在\webapps\seeyon\WEB-INF\cfgHome\plugin\doc\spring 下新建一个 xml 文件(如 docAddinMenu.xml)。代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="byName" default-lazy-init="false" default-dependency-check="none">
<!-- 自定义加载项菜单,如果要添加多个菜单项,可以在当前的XML文件中定义多个bean,也可 以建立多个XML文件,在menu.xml注册 -->
<bean id="docAddinMenuItem" class="com.seeyon.ctp.common.thirdparty.menu.ThirdpartyAddinMenu" init-method="init">
    <!--菜单添加的功能模块(页面),与页面Controller中定义的的名称相同,可以在多个页面 中添加同一菜单项 -->
    <property name="module">
        <list>
            <value>apps/doc/rightNew</value>
        </list>
    </property>
    <!--菜单文本,在资源文件中的名称。 -->
    <property name="label" value="doc.menu.export.label"/>
    <!--菜单链接,目前只支持javascript,对于文档交换,可以调用thirdPMenuSend方法,它会 将选中的id发送到指定的地址 -->
    <property name="url" value="http://xxxxxx"/>
    <!--显示顺序 -->
    <property name="index" value="0"/>
</bean>
</beans>

2、指定菜单要添加到的页面 修改 docAddinMenu.xml 文件,将要显示的页面列在 module 下面。如:

<property name="module">
    <list>
        <value>apps/doc/rightNew</value>
    </list>
</property>

此段代码中”/doc/rightNew”是根据执行流程来找到要添加菜单页面的地址的。执行流程可以根据页面属性查看页 面请求的地址如 doc.do?meithod=rightNew&…,然后到 webapps\seeyon\WEB-INF\classes 目录下找 urlMapping,找到 prop key=doc.do 对应的控制文件:docController,进入 classes 目录下的 doc-controller.xml 文件根据 id=”docController”的 bean 的 class 值找到处理文件(如 com/seeyon/ctp/doc/controller/DocController),根据处理方法 rightNew(与上面的 doc.do?method=rightNew&…相对应)找到 rightNew。

3.指定菜单文本在资源文件中的名称

修改 docAddminMenu.xml 文件,给 label 加上值。value 值指定的是在页面上要显示的值,这里不直接写上内容是为了后面显示国际化做准备。

<property name="label" value="doc.menu.export.label"/>

4.指定点击菜单后进入的页面地址

修改 docAddinMenu.xml 文件的 url 值,可以是网址,也可以是 javascript。

<property name="url" value="/seeyon/doc/docFile.jsp"/>

5、国际化

在 \webapps\seeyon\WEB-INF\cfgHome\plugin\doc\i18n 文件夹下新建或修改 DocMenuResources_en.properties 和 DocMenuResources_zh_CN.properties 文件,在 DocMenuResources_en.properties 中添加当是英文显示的时候要显示的值。如:

doc.menu.export.label=export

在 DocMenuResources_zh_CN.properties 中添加用汉语要显示的值。如:

doc.menu.export.label=归档到文档系统

用命令将 DocMenuResources_zh_CN.properties 编译,如:

native2ascii 文件名 新文件名

编译结果如:

doc.menu.export.label==\u5F52\u5230\u6863\u6848\u7CFB\u7EDF

1.5. 栏目集成(Portlet)

1.5.1. 栏目开发

Portlet在协同中的概念是首页空间中的一个区域,它可以包含多个Tab(栏目)。Portlet的开发也就是对栏目的开发。

img

1.5.2. IFrame集成

不需要开发,通过配置关联系统和扩展栏目,将第三方系统的页面直接包装为协同平台的栏目。操作步骤详见单点登录一节的关联系统和扩展栏目。

1.5.3. SDK+web service方式集成

实现方式为按照插件规范在平台中开发一个插件,直接连接第三方系统数据库或利用Web Service读取第三方系统的数据,按栏目规范组织好以后在协同系统中展现。

确定栏目数据

栏目中的数据由首页应用来确定

需要提供的接口:

1、数据列表抽取接口,该接口用来获取栏目显示的数据;

2、数据总数统计接口,该接口用来显示栏目数据的总数(如果需求显示数据总数)。

栏目解析类开发

1.Package:com.seeyon.ctp.portal.section

2.解析类需要继承抽象类:com.seeyon.ctp.portal.section.BaseSectionImpl

3.Demo类示例

public class DemoSection extends BaseSectionImpl {

    @Override
    public String getId() {
        //栏目ID,必须与spring配置文件中的ID相同;如果是原栏目改造,请尽量保持与原栏目ID一致
        return "demoSection";
    }

    @Override
    public String getName(Map preference) {
        //栏目显示的名字,必须实现国际化,在栏目属性的“columnsName”中存储
        String name = preference.get("columnsName");
        if(Strings.isBlank(name)){
            return "demoSection";
        }else{
            return name;
        }
    }

    @Override
    public Integer getTotal(Map preference) {
        //栏目需要展现总数据条数时重写
        return null;
    }

    @Override
    public String getIcon() {
        // 栏目图标,暂不需要实现
        return null;
    }

    @Override
    public BaseSectionTemplete projection(Map preference) {
        //栏目解析主方法

        HtmlTemplete ht = new HtmlTemplete();
        StringBuilder html = new StringBuilder();
        html.append("
DemoSection");
        ht.setHeight("230");
        ht.setHtml(html.toString());
        ht.setModel(HtmlTemplete.ModelType.inner);
        ht.setShowBottomButton(true);
        ht.addBottomButton(BaseSectionTemplete.BOTTOM_BUTTON_LABEL_MORE, "javascript:alert('ok');");
        return ht;
    }
}

栏目解析类配置

1.编写好的栏目类需要在对应的插件Spring配置文件夹进行Spring注册配置。

2.约定栏目的Spring文件命名为spring-xxxx-section.xml xxxx为插件Id。

3.栏目属性对照表

属性名 值域 描述
sectionType common, //常用栏目 timeManagement, //时间管理 publicInformation, //公共信息 doc, //文档栏目 formbizconfigs, //表单栏目 forum, //扩展栏目 栏目类型,空间选择栏目时栏目的分类由该属性决定
sectionCategory plan 栏目类型中的分类:常用、计划...
spaceTypes Constants.SpaceType中枚举的类型 允许添加该栏目的空间类型
resourceBundle 示例:com.seeyon.v3x.main.resources.i18n.MainResources 国际化绑定来源,已废弃
properties List 栏目编辑属性

4.空间类型

/**
     * 空间类型,顺序不可改变
     */
    public static enum SpaceType {
        /**
         * 0 个性化个人空间
         */
        personal, //0

        /**
         * 部门空间
         */
        department, //1
        /**
         * 单位空间
         */
        corporation, //2
        /**
         * 集团空间
         */
        group, //3
        /**
         * 自定义团队空间
         */
        custom, //4

        //默认的空间
        /**
         * 默认个人空间
         */
        Default_personal, //5 
        //@Deprecated
        /**
         * 默认部门空间
         */
        Default_department, //6 

        @Deprecated
        Default_custom,  //7 
        /**
         * 第三方系统
         */
        thirdparty, //8 
        /**
         * 默认领导空间
         */
        default_leader,//9 
        /**
         * 个性化领导空间
         */
        leader,//10 
        /**
         * 关联系统
         */
        related_system,//11 
        /**
         * 关联项目
         */
        related_project,//12 
        /**
         * 自定义个人空间
         */
        Default_personal_custom,//13 
        /**
         * 默认外部人员空间
         */
        Default_out_personal,//14 
        /**
         * 个性化个人自定义空间
         */
        personal_custom,//15 
        /**
         * 个性化外部人员空间
         */
        outer,//16  
        /**
         * 自定义单位空间
         */
        public_custom,//17 

        /**
         * 自定义集团空间
         */
        public_custom_group,//18 
        //二级主页空间
        /**
         * 协同工作
         */
        cooperation_work,//19 协同工作
        /**
         * 目标管理
         */
        objective_manage,//20 目标管理
        /**
         * 公文管理
         */
        edoc_manage,//21 公文管理
        /**
         * 会议管理
         */
        meeting_manage,//22 会议管理
        /**
         * 协同驾驶舱
         */
        performance_analysis,//23 协同驾驶舱
        /**
         * 空间模板的默认空模板类型,不是空间类型,内部使用
         */
        template,//空间模板的默认空模板类型,不作为空间类型使用
        /**
         * 表单应用
         */
        form_application,//25 表单应用
        //end 
    }

5.Demo栏目配置示例

<bean id="demoSection" class="com.seeyon.ctp.portal.section.DemoSection" init-method="init">
    <property name="sectionType" value="common" />
    <property name="sortId" value="99" />
</bean>

6.栏目模板列表

模板类 描述
CalendarFourColumnTemplete 日程事件的四列模板:标题 开始时间 结束时间 状态
ChessboardTemplete 棋盘式 左边小图标(默认1616)+右边标题 上边大图标(默认3232)+下边标题 * 标题可以有浮动菜单
ChessMultiRowThreeColumnTemplete 棋盘式 、3列
HtmlTemplete 直接输出HTML代码片断
MonthCalendarTemplate 月历式栏目
MoveMultiRowThreeColumnTemplete 多行3列滚动式
MultiIconCategoryItem 多行的,图标,分类,文本的展现形式
图表是32px 32px的,在左边,右边的上面是分类(Category),下面是若干个项
MultiRowFourColumnTemplete 多行3列模板,依次是:subject createDate createMemberName category
MultiRowThreeColumnTemplete 多行3列模板,依次是:subject createDate category
MultiRowVariableColumnTemplete 成倍行,不定列 模板 适用于 三或四列标准列表模板满足不了需要的情况下 可以自定义列数、宽度、单元格样式、链接地址
MultiSubjectSummary 多行的,显示标题和摘要,常用新闻、公告
OneImageAndListTemplete 图片加列表模板 第一列为图片居左 右边为标题加摘要 下面为列表 列表内容为 标题 发起时间 所属板块 * 列表参数可配置
OneItemUseTwoRowTemplete 两行展现一项 模板 适用于 如集团空间调查栏目 第1行 标题,另起一行 发布时间和类型
OnePictureTemplete 图片滚动式
OneSummaryAndMultiList 显示模式:一条显示为“标题+时间+(类别)+摘要”,下面是若干行列表
PictureTemplete 图片基础模板
PictureTitleAndBriefTemplete 标题加摘要的新闻模板

1.6. 单点登录(SSO)

1.6.1. 协同平台单点登录第三方系统

实现方式一:关联系统+扩展栏目

无需开发,使用关联系统管理登录信息,集成第三方系统页面。

适用于不需要验证码的Web应用,使用目标系统的用户名和密码模拟登录过程。

  • 配置关联系统

    • 以系统管理员system登录协同,使用“关联系统管理”建立一个新的关联系统。关联系统的url为被集成系统的登录链接,将登录需要的信息如用户名和密码定义为关联系统参数

      img

    • 建立扩展栏目

      由第三方系统管理验证信息的栏目有两种:数据集成型栏目(SSOWebContentSection)和功能操作型栏目(SSOIframeSection)

![img](http://open.seeyon.com/seeyon/webhelp/APIdoc/content/resources/images/sso4.png)

img

  • 用户登录后,在个人空间中配置关联系统参数,填写集成系统的登录信息(用户名和密码)。 由关联系统维护被集成系统的用户名、密码以及Session
  • 说明:被集成系统的修改:登录不成功,需要在登录请求的response中增加header项:LoginError=*,否则平台无法判断是否成功登录
  • 从V7.0开始,关联系统的URL中可以调用公式,实现一些更复杂的单点登录,请参见公式组件

实现方式二:Ticket

实现步骤:

  • 添加页签

    新建插件sso:按下面的定义,在协同的webapps\seeyon\WEB-INF\cfgHome\plugin\sso目录下新建一个XML文件pluginCfg.xml,例如:

    <?xml version="1.0" encoding="UTF-8"?>
    <plugin>
        <id>sso</id>
        <name>单点登录模块</name>
        <category>11001</category>
    </plugin>
    

    然后在协同的webapps\seeyon\WEB-INF\cfgHome\plugin\sso\spring目录下新建一个XML文件,如myspace.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans default-autowire="byName">
        <!-- id必须唯一 -->
        <bean id="sinaNewsSpace" class="com.seeyon.v3x.common.thirdparty.ThirdpartySpace" init-method="init">
            <!--注意如果OA的版本是V6.0及其以上,上面class修正为:com.seeyon.ctp.portal.sso.thirdpartyintegration.ThirdpartySpace -->
            <!-- id必须唯一或者不写后台自动生成,必须为数字 -->
            <property name="id" value="-2327812443752403806"/>
            <!-- 页签上显示的名称 -->
            <property name="name" value="Sina News"/>
            <!-- 插件id,必须存在,如果不存在,请按下面的步骤定义一个新的插件 -->
            <property name="pluginId" value="sso"/>
            <!-- 第三方系统登录地址,如果合并3、4步,可以省略 -->
            <property name="loginURL" value="http://xxx.xxx.xxx.xxx/ssologin.jsp"/>
            <!-- 点击页签要打开的第三方系统页面地址 -->
            <property name="pageURL" value="http://xxx.xxx.xxx.xxx/main.jsp"/>
            <!-- 打开方式,值为open时在新窗口打开,为workspace时在协同页面能打开 -->
            <property name="openType" value="open"/>
            <!-- 排序号 -->
            <property name="index" value="4"/>
        <!-- 授权 accessRoles与accessCheck选择其中一个即可-->  
        <property name="accessRoles"> 
          <list> 
            <value>GeneralStaff</value> 
          </list> 
        </property>  
    
       <!-- <property name="accessCheck" ref="accessCheck"/> -->  
      </bean>  
     <!-- <bean id="accessCheck" class="com.seeyon.ctp.ext.Tab.TestThirdpartyAccessCheck"></bean> -->  
    </beans>
    

    注意事项:集成的第三方系统,必须先在协同平台中定义插件

  • myspace.xml参数说明
名称 备注
id 唯一标示,这了在XML里必须设置为数字型
name 名称
pluginId 插件ID
loginURL 在进入第三方指定页面前,先跳转到loginURL进行握手 第三方系统根据Ticket回调获取协同身份信息,进行单点登录,并注册Ticket和登录用户的映射, 平台带着Ticket跳转到第三方系统的"页面地址" (pageURL)
pageURL 第三方指定到达的目的页面。
openType 打开方式
accessRoles与accessCheck OA用户对于第三方页签显示权限,详情请见页签集成
  • 配置第三方系统,ThirdpartySpace:名称、登录地址、页面地址、打开方式【详情请见1.5.1. 页签集成】

  • 登录过程

    在协同中点击"空间页签"

    平台产生Ticket,并维护在内存中

    平台带着Ticket访问第三方系统的"登录地址"(loginURL)进行握手 第三方系统根据Ticket回调获取协同身份信息,进行单点登录,并注册Ticket和登录用户的映射, 平台带着Ticket跳转到第三方系统的"页面地址" (pageURL)

说明

第三方系统完全依赖和信任协同的身份验证,Ticket由协同平台发放

第三方系统使用与协同完全相同的登录名或者进行二次开发,自己维护协同登录名与第三方系统用户的映射表

如果当前用户已经登录第三方系统,将跳过② ③,直接到第4步,使用相同的Ticket

  • 认证ticket/获取身份信息的接口系统提供Servlet:http://a8:80/seeyon/thirdpartyController.do?ticket=**;平台将通过response header的LoginName返回登录名

  • 如果第三方回调后认为有异常,请在response header增加名称为SSOLogoutError的信息

  • 在上面myspace.xml中(loginURL)获取当前人员登录名例程如下:

    //获取ticket
    String ticketinfo=request.getParameter("ticket");
    //通过ticket/获取身份信息的接口获取登录信息
    String LoginNameUrl="http://127.0.0.1/seeyon/thirdpartyController.do?ticket="+ticketinfo;
    //通过HttpClientUtil发送请求
    HttpClientUtil u = new HttpClientUtil();
    //获取登录名
    String LoginName=u.getContent(LoginNameUrl).toString();
    

第三方系统退出时,要通知协同,地址是http://a8:80/seeyon/thirdparty.do?method=logoutNotify&ticket=**

根据情况可将 ③ ④步合并 (省略loginURL)

1.6.2. 第三方系统单点登录协同平台

Ticket中请不要使用特殊字符和中文,平台不支持使用中文作为Ticket!

必须在配置工具的系统参数设置中配置协同平台的外网访问地址internet.site.url,否则,生成的url为空。

注意:【V6.1 ticket必须加密,不加密则无法正常登录】

应用场景

  1. 从第三方Portal打开协同

  2. 从第三方集成的协同栏目打开链接

关键步骤

下面描述了单点登录的典型开发关键步骤,细节可参阅后续章节

  1. 配置外网访问地址internet.site.url,如不配置,webService导出的数据的url为空

img

  1. 建立一个插件,作为单点登录配置和Class的容器。参见本地开发接口的插件化章节。

建立握手类,例如

public class MySSOLoginHandshake  extends SSOLoginHandshakeAbstract {
    // “ticket” 就是ticket取得的参数值
    public String handshake(String ticket) {
        if(ticket==null||ticket.equals("")) {
            return null;
        }
        HttpClient hc = new HttpClient();
        //对应下图(实现原理)中 步骤4:协同平台与第三方系统握手,根据ticket获取loginName
        loginName = hc.get("http://第三方系统/getLoginIdByTicket?ticket="+ticket);

        // 返回ticket对应的协同登录名
        return loginName;
    }
    public void logoutNotify(String ticket) {
        HttpClient hc = new HttpClient();
        //用户退出协同时,通知第三方系统。
        loginName = hc.get("http://第三方系统/logout?ticket="+ticket);
    }
}
  1. 建立握手类Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="byName">
<bean id="samplesso" class="com.seeyon.ctp.portal.sso.SSOLoginContext">
    <property name="name" value="sample"/>
    <property name="forward" value="true"/>
    <property name="handshake">
        <!-- 使用自己的握手实现 -->
        <bean class="com.seeyon.apps.plugin.MySSOLoginHandshake" />
    </property>
</bean>
</beans>

实现原理

从第三方系统登录到协同,这种认证是完全信任第三方系统的,单点登录有两种效果:

  • 单点登录成功后直接打开协同主页面(缺省模式)。
  • 单点登录成功后并不打开协同主页面,平台维护ticket信息和登录用户信息,为以后请求服务作认证使用。

比如:请求获得协同待办事项列表服务。需要配置SSOLoginContext.xml中一个属性如下: 单点登录的前提是外部系统使用和协同一致的登录名或维护了两个系统的登录名映射,也就是说,外部系统必须知道它的当前用户在协同中的登录名。 请注意,为了安全,登录过程必须由第三方系统的服务器发出,不允许第三方系统客户端直接单点登录。 登录过程:

三方登录时序图

  1. 获取当前用户的ticket:

    用户登录外部平台,发出要访问协同平台的请求。

  2. 生成ticket:

    外部平台生成ticket,ticket不能为中文,也不能使用协同平台的登录名,最理想的ticket是类似sessionId的随机字符串。并在第三方系统服务器中,记录 ticket 和 ctp平台中的loginName的关联关系。

  3. 登录:外部平台带着ticket、from(握手bean的name,如下文中的gke)和UserAgentFrom跳转到协同平台

    UserAgentFrom是一个枚举代表着不同端的登录(默认值:pc)。
    
     UserAgentFrom枚举值如下:
    
         pc     //pc端
         phone    //移动端
         ucpc    //ucpc
         wechat    //微信端
    
     登录URL如下所示:
    
     /seeyon/login/sso?from=gke&ticket=ticket***&UserAgentFrom=pc
    
  4. handshark 握手请求:

    发起一个请求,从第三方系统中获取ticket对应的loginName。

  5. 返回ticket对应loginName:

    外部平台将ticket对应的协同登录名告知协同协同平台。如果没有获取到loginName,表示握手失败。

  6. SSOOK:

    平台执行其他相关操作,完成ticket与用户信息绑定的注册(并在内存中存储ticket和username映射)。在response header中增加SSOOK。

  7. 返回ticket

  8. 协同平台通过ticket参数,调用登录操作,完成用户登录

    示例:
         /seeyon/main.do?method=login&ticket=[ticket]&ssoFrom=gke&UserAgentFrom=pc
    
  9. 定时发送心跳,保证用户在线

    各个端的心跳接口如下:

     通用心跳接口(保持当前用户登录的端的心跳):/seeyon/getAJAXOnlineServlet?V=[随机数]
     致信端心跳接口:/seeyon/uc/rest.do?method=showUpdateSystemHistoryMessages&timestamp=[随机数]
     M3端心跳接口: /seeyon/rest/m3/message/classification
    

退出过程:

  • 用户退出协同时,会通过SSOLoginHandshakeInterface的logoutNotify()通知到第三方系统
  • 用户退出第三方系统时,访问协同平台的/seeyon/login/ssologout?from=&ticket=&userAgentFrom**通知协同平台** !!!重要,为了安全,必须logout。

接口说明:

  • 外部平台需要有协同登录名的映射表或者使用相同的登录名
  • SSOLoginHandshakeInterface与from映射,需要配置或二次开发。
  • 配置:系统提供通用的com.seeyon.ctp.portal.sso.SSOLoginHandshakeServletImpl,配置url(外部系统中传入ticket返回协同登录名的页面地址)和logoutUrl即可。 二次开发:实现接口SSOLoginHandshakeAbstract,自己编写握手逻辑,比如下面的GKEA8SSOLoginImpl。
  • 平台的验证系统完全依赖和信任第三方系统(存在风险,因此,只在服务器之间握手至关重要)

配置文件说明:在插件的spring目录增加xml配置文件

SSOLoginContext.xml示例:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="byName">
<bean id="gke" class="com.seeyon.ctp.portal.sso.SSOLoginContext">
    <property name="name" value="gke"/>
    <!-- 单点登录成功是否跳转到首页 -->
    <property name="forward" value="false"/>
    <!-- 握手配置 -->
    <property name="handshake">
        <!-- 使用系统实现的缺省握手类 -->
        <bean class="com.seeyon.ctp.portal.sso.SSOLoginHandshakeServletImpl">
            <!-- 第三方系统页面,传入ticket,返回协同登录名 -->
            <property name="url" value="http://第三方系统:8080/checkTicket"/>
            <!-- 第三方系统页面单点登录登出地址 -->
            <property name="logoutUrl" value="http://第三方系统:8080/ssologout"/>
        </bean>
    </property>
</bean>
</beans>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="byName">
<bean id="gke" class="com.seeyon.ctp.portal.sso.SSOLoginContext">
    <property name="name" value="gke"/>
    <property name="handshake">
        <!-- 使用自己的握手实现 -->
        <bean class="com.seeyon.v3x.plugin.gke.sso.GKEA8SSOLoginImp" />
    </property>
</bean>
</beans>

二次开发需要实现SSOLoginHandshakeAbstract的handshake和logoutNotify方法

/**
* 通过握手获取平台的认证信息
* @param ticket 平台传过来的令牌信息
* @return 返回当前登录者的登录名
*/
public String handshake(String ticket);

/**
 * A8退出时通知外部平台
 * 
 * @param ticket 平台传过来的令牌信息
 */
public void logoutNotify(String ticket);

代码示例

从GKE登录到协同平台:

public class GKEA8SSOLoginImp  extends SSOLoginHandshakeAbstract {
    // “ticket” 就是ticket取得的参数值
    public String handshake(String ticket) {
        if(ticket==null||ticket.equals("")) {
            return null;
        }
        String userName="";
        // 这个是gke自己特定的ticket格式:{GID},{passport}
        String[] r=ticket.split(",");
        if(r==null||r.length!=2) {
            return null;
        }
        userName=this.checkPassport(r[0], r[1]);
        return userName;
    }
    public void logoutNotify(String ticket) {

    }

    private String checkPassport(String GID,String passPort) {
        StringBuffer sb = new StringBuffer();
        sb.append("<?xml version="1.0" encoding="UTF-8" ?>");
        sb.append("<request type="login" subtype="passport" msid="">");
        sb.append("<message>");
        sb.append("<user");
        sb.append(" GID=""+GID+""  gid=""  zoneid="">");
        sb.append("<passport>");
        sb.append(passPort);
        sb.append("</passport>");
        sb.append("</user>");
        sb.append("</message>");
        sb.append("</request>");  
        // 向GKE发出请求,取得协同登录名
        return getGKEResponse(postGKERequest(sb.toString()));
}

单点登录的URL包装

单点登录成功获取Ticket以后,要想使用ticket访问协同平台内部地址,必须对URL进行包装

String ticket = request.getParameter("ticket");
String url="collaboration/collaboration.do?method=summary&openFrom=listPending&affairId=-932843950278492";
TicketInfo ticketInfo = com.seeyon.ctp.portal.sso.SSOTicketBean.getTicketInfo(ticket);
if(ticketInfo != null) {
  response.sendRedirect(com.seeyon.ctp.portal.sso.SSOTicketBean.makeURLOfSSOTicket(ticket, url));
}

示例:单点登录+事项列表场景

  1. 通过单点登录接口的tourl参数,直接登录OA并且打开tourl对应OA地址页面。

    完整示例:

    OA中某待办协同地址:
    http://ip:端口/seeyon/collaboration/collaboration.do?method=summary&openFrom=listPending&affairId=-1036595126906172786
    对应单点登录URL:
    http://ip:端口/seeyon/login/sso?from=sample&ticket=lsm1&UserAgentFrom=pc&tourl=%2fseeyon%2fcollaboration%2fcollaboration.do%3Fmethod%3Dsummary%26openFrom%3DlistPending%26affairId%3D-1036595126906172786
    

    从上面的示例中可以看出tourl是OA中具体链接的【/seeyon/collaboration/collaboration.do?method=summary&openFrom=listPending&affairId=-1036595126906172786】部分做了转码。

  2. 通过事项接口获取列表URL后直接打开OA地址页面。

    看了上面的示例可能会有人问如何获取OA中具体协同的打开地址呢?

    现在OA平台接口有2种方式可以获取事项URL。

    第一种方式:SOAP接口的exportPendingList方法:获取待办事项列表。这个接口需要要单点登录成功以后才会导出事项 URL地址。

    第二种方式:REST接口中【门户集成】-【事项接口】(注意在V7.0版本,事项接口才可以导出URL地址),可以获取事项的URL地址。

    注意:通过以上两种方式获取到URL地址以后,必须要单点登录成功以后,才能再请求获取到的URL地址。而如果是通过tourl方式打开URL,则是单点登录与打开URL同时请求,不需要单独再做单点登录请求。

其他

部署说明:单点登录的开发建立的java文件放在com.seeyon.ctp.portal.sso包下

跳转到协同页面

将协同页面集成为第三方系统菜单项:

单点登录以后,如果要实现在第三方系统打开协同平台任意页面的功能,可以使用下面的跳转链接:

/seeyon/a8genius.do?method=window&url=xxx

例如:

/seeyon/a8genius.do?method=window&url=calEvent.do%3Fmethod%3DhomeEntry

可以跳转到协同平台的日程计划页面/seeyon/calEvent.do?method= homeEntry 如果不进行跳转,协同平台页面可能找不到依赖的javascript脚本,导致脚本错误,无法使用。

results matching ""

    No results matching ""