1.5.6.2.3. 实现原理

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

  • 单点登录成功后直接打开协同主页面(缺省模式)。

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

    比如:请求获得协同待办事项列表服务。需要配置SSOLoginContext.xml中一个属性如下: <property name="forward" value="false"/>

单点登录的前提是外部系统使用和协同一致的登录名或维护了两个系统的登录名映射,也就是说,外部系统必须知道它的当前用户在协同中的登录名。

请注意,为了安全,登录过程必须由第三方系统的服务器发出,不允许第三方系统客户端直接单点登录。

登录过程:

  1. 请求

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

  2. 生成

    外部平台生成ticket,ticket不能为中文,也不能使用协同平台的登录名,最理想的ticket是类似sessionId的随机字符串。

  3. 登录

    外部平台带着ticket和from(握手bean的name,如下文中的gke)跳转到协同平台

    /seeyon/login/sso?from={from}&ticket={ticket}

    如果跳转到协同时登录正常,会在response header中增加SSOOK,否则增加SSOError,跳转的URL示例如下:

    /seeyon/login/sso?ticket="+encodeURIComponent("<%=request.getParameter("ticket")%>")+"&from=gke
  4. 握手

    平台根据from(spring bean的id)取得握手bean ,在spring配置文件中bean定义格式示例如下:

    <bean id="gke" class="com.seeyon.ctp.portal.sso.SSOLoginContext">
        <property name="name" value="gke" />
        <property name="ticketName" value="ticket" />
        ...
    </bean>
  5. 告知

    握手bean以ticket值为参数调用外部平台接口,外部平台将ticket对应的协同登录名告知协同协同平台。

  6. SSOOK

    平台执行其他相关操作,完成登录过程,并在内存中存储ticket和username映射,在response header中增加SSOOK。

退出过程:

  • 用户退出协同时,会通过SSOLoginHandshakeInterface的logoutNotify()通知到第三方系统

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

接口说明:

  • 外部平台需要有协同登录名的映射表或者使用相同的登录名

  • JAAS的DefaultLoginModule验证用户名(不验证密码)

  • 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()));
}

部署说明:单点登录的开发建立的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脚本,导致脚本错误,无法使用。