第1章 背景

SaCa Dataviz 是一个自助式可视化分析工具,而当应用系统中需要使用本产品时,常常会面临与已有系统集成整合。SaCa Dataviz推荐的集成方式是与已有应用做单点登录(SSO)集成。本文将介绍如何在SaCa DataViz中进行修改,以与已搭建好的CAS(Central Authentication Service) 服务进行集成。

本文中所对应的CAS版本为4.1,其他版本除较早版本外方法应大体相同。推荐使用较新版本进行集成。

本文方法仅限 5.7.03 之后的版本使用,5.7.03 及其之前的版本需按原 CAS集成文档 进行集成。

第2章 集成

本文不涉及CAS端的搭建或定制化内容。在使用本文中的方法进行集成前,请先确保CAS服务已正确搭建,已有系统已可以正常使用CAS完成登录、登出。如果对CAS不了解,可以参考 《Apereo CAS 单点登录系统介绍》 .

2.1 思路

SaCa DataViz采用前后端分离架构,前端为纯HTML,后端为Java Web应用。CAS集成针对的是后端Java端与CAS的集成,前端作为纯展现端可以独立运行,也可以直接集成到应用系统内。

后端集成主要考虑两个点,一是登录流程的处理,二是登录用户集成。SaCa DataViz 后端使用Spring Security 5.5 版本进行身份验证,且本身已经集成了对接CAS 所需的主要内容。两者的处理均基于Spring Security完成。

除后端外,前台有一些配置需要修改,以适应登录跳转。

集成过程需要基于DataViz后端进行少量代码开发。后续配置除个别点外,均依照Spring Security 5.5与CAS集成的一般方法配置,如果配置后某些效果不满足需求,可以在了解集成方法后自行修改实现。

2.2 准备

按照之前添加已有系统的方法,将 DataViz 后台的访问地址加入到 CAS 服务授权service中。如:http://192.168.3.1:8080/dataviz-service.

2.3 登录流程处理

登录流程如果没有特殊需求,主要过程可通过修改配置文件完成。

  • 修改 /WEB-INF/web.xml,添加如下内容
<listener>
    <listener-class>
        org.jasig.cas.client.session.SingleSignOutHttpSessionListener
    </listener-class>
</listener>
  • 修改 applicationContext-security.xml

    该文件位于 WEB-INF/conf/spring/applicationContext-security.xml,包含了全部Spring Security登录验证等相关内容的配置。

    删除以下相关内容:

    • <http use-expressions="true" entry-point-ref="authenticationEntryPoint"> … </http>
    • <beans:bean id="formLoginFilter" class=…> … </beans:bean>
    • <beans:bean id="sessionManagementFilter" class=…> … </beans:bean>
    • <beans:bean id="invalidSessionStrategy" class=…> … </beans:bean>
    • <beans:bean id="logoutSuccessHandler" class=…> … </beans:bean>
    • <beans:bean id="authenticationEntryPoint" class=…> … </beans:bean>
    • <custom-filter position="SESSION_MANAGEMENT_FILTER" ref="sessionManagementFilter"></custom-filter>

    增加以下内容:

<beans:bean id="casPropertyConfigurer"     
    class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <beans:property name="ignoreUnresolvablePlaceholders" value="true" />
        <beans:property name="locations">
            <beans:list>
                <beans:value>WEB-INF/conf/cas.properties</beans:value>
            </beans:list>
        </beans:property>
</beans:bean>

<http use-expressions="true" entry-point-ref="authenticationEntryPoint" create-session="ifRequired">
        <!-- 禁用CSRF Protection,否则无法执行POST请求,会报403错误 -->
        <csrf disabled="true" />
        <!-- 对所有资源,都必须要有USER角色 -->
        <intercept-url pattern="/**" access="hasRole('USER') and @menuSecurity.check(authentication, request)" />
        <!-- 退出配置,如需删除cookie可添加:delete-cookies="JSESSIONID"-->
        <logout logout-success-url="/cas-logout.jsp"/>
        <custom-filter after="FORM_LOGIN_FILTER" ref="internalLoginFilter" />
        <custom-filter position="CAS_FILTER" ref="casFilter" />
        <custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>
        <custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>
        <access-denied-handler ref="accessDeniedHandler"></access-denied-handler>
</http>

<beans:bean id="casInfos" class="com.neusoft.saca.dataviz.authentication.springsecurity.cas.RequestAwareCasInfos">
    <beans:property name="defaultCas">
        <beans:bean class="com.neusoft.saca.dataviz.authentication.springsecurity.cas.CasInfo">
            <beans:property name="serviceProperties">
                <beans:bean class="org.springframework.security.cas.ServiceProperties">
                    <beans:property name="service" value="${sso.cas.localServer}${sso.cas.localServicePath}" />
                    <beans:property name="sendRenew" value="false" />
                </beans:bean>
            </beans:property>
            <beans:property name="loginTarget" value="${sso.cas.defaultTargetRedirect}" />
            <beans:property name="logoutTarget" value="${sso.cas.casServer}/logout?service=${sso.cas.localServer}" />
            <beans:property name="casLogin" value="${sso.cas.casServer}/login" />
            <beans:property name="casAuthn" value="${sso.cas.casServerLocal}" />
        </beans:bean>
    </beans:property>
</beans:bean>

<beans:bean id="casConfiguration" class="com.neusoft.saca.dataviz.authentication.springsecurity.cas.CasConfiguration">
    <beans:constructor-arg ref="casInfos" />
    <beans:property name="authenticationManagerRef" value="authenticationManager" />
    <beans:property name="userIntegrated" value="false"/>
</beans:bean>

<beans:alias name="casEntryPoint" alias="authenticationEntryPoint" />

查找 <authentication-manager alias="authenticationManager">标签,将以下内容添加到该标签内:

    <authentication-provider ref="casAuthenticationProvider"/>
  • 添加cas.properties
    于 WEB-INF/conf 下,添加 cas.properties 文件。参考以下内容。
# cas 服务访问地址
sso.cas.casServer=https://sso.local-example.org:8443/cas

# cas 服务内部地址,用于内部验证通信,当仅在内网使用或没有内外网隔离时可与外部访问地址相同
sso.cas.casServerLocal=https://sso.local-example.org:8443/cas

# DataViz 后台应用访问地址
sso.cas.localServer=http://www.local-example.org:8080/dataviz-service

# DataViz 登录地址,不用修改
sso.cas.localServicePath=/login/cas

# DataViz 登出地址,不用修改
sso.cas.localLogoutPath=/logout/cas

# DataViz 前台访问地址
sso.cas.defaultTargetRedirect=http://www.local-example.org:8080/dataviz/src/index.html

2.3.1 定制修改

上文通过修改配置方式可完成初步的CAS认证集成。按照上文默认的配置方式,在登录系统后,DataViz将使用CAS认证返回的用户ID作为DataVIz系统中的用户ID,且都不是管理员用户。

如果将DataViz系统与CAS端的用户、组织等信息进行了集成,且集成后的用户ID与CAS返回的ID保持一致,则可以直接修改配置应用用户集成后的效果。

2.3.1.1 用户组织集成后的配置

在系统用户ID与CAS用户ID一致的情况下,可简单修改 applicationContext-security.xml,修改新添加的 casConfiguration bean,将userIntegrated 值改为 true,完成集成。

注意:如果在还未做用户集成的情况下,将 userIntegrated改为 true,DataViz可能会因找不到用户信息而无法登录。

2.3.1.2 用户组织集成后用户ID不直接对应CAS用户ID

此节仅适用于 v5.8 及以上版本

如果集成的用户ID与CAS返回的用户ID不一致,但有某种对应关系,可在上述userIntegrated 值改为 true的基础上,手动开发编写一个类(不需依赖DataViz),将CAS 的用户ID的映射为系统集成后的用户ID,再将其放到dataviz-service中,注册为 spring bean 完成集成。

具体操作上:

  1. 开发一个类,实现 java.util.function.Function<String, String>接口;将其注册为 Spring Bean ,且其bean id 或 qualifier 为 casUserId。(可参考下面的示例)

  2. 编译成功后将对应 .class 放到dataviz-service的WEB-INF/classes下或打成 jar 后放到 WEB-INF/lib 下。

示例代码:

import java.util.function.Function;

public class CasUserMapping implements Function<String, String> {

    @Override
    public String apply(String casUserId) {
        return "cas_" + casUserId; // this simply adds a prefix
    }

}

注册 bean 配置(applicationContext-security.xml):

    <beans:bean id="casUserId" class="<实际完整类名>" />

2.3.1.3 其他情况的用户配置

如果CAS集成时上述方式不能满足需求,或者不想进行组织用户集成,但需要登录后需要区分管理员或定制用户名称等信息,则需要在登录流程中添加定制开发,需要进行以下修改。

  • 开发一个 UserDetailsService 实现类

    可以直接从头实现Spring Security 的 org.springframework.security.core.userdetails.UserDetailsService 接口,也可以继承内置的 com.neusoft.saca.dataviz.authentication.springsecurity.DefaultUserDetailService 以快速复用用户组织集成。

    将编译后的class添加到WEB-INF/classes下,或编译打包后添加到WEB-INF/lib下。

    代码示例:

import org.springframework.security.core.userdetails.UserDetails;

import com.neusoft.saca.dataviz.authentication.springsecurity.DefaultUserDetailService;
import com.neusoft.saca.dataviz.common.security.DefaultUser;
import com.neusoft.saca.dataviz.system.bo.UserBO;

public class CasUserDetailsService extends DefaultUserDetailService {

    /**
     * 接口实现。如不使用集成的用户组织信息,需重写此方法返回一个 DefaultUser
     */
    @Override
    public UserDetails loadUserByUsername(String username) {
        // 根据username或去用户信息,可以根据当前用户的信息判断是否为管理员,如果是管理员,userId为用户ID
        // return new DefaultUser(username, "",new SimpleGrantedAuthority("ROLE_ADMIN"));
        return new DefaultUser(username, "");
    }

    /**
     * 父类实现回调方法。如需使用集成的用户组织信息登录,可**仅重写此方法**由cas用户id获得一个 UserBO 即可。否则可忽略此方法。
     */
    @Override
    protected UserBO getUserBO(String loginName) {
        return this.userService.getUserById(loginName);
    }

}
  • 配置实现类

    修改 applicationContext-security.xml,修改新添加的 casConfiguration bean, 添加如下 property 配置:

  <beans:property name="userDetailsService">
    <beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
      <beans:constructor-arg>
          <beans:bean class="<实现类完整类名>" />
      </beans:constructor-arg>
    </beans:bean>
  </beans:property>

2.3.2 报表集成版本

报表集成版本相较一般DataViz中额外增加了一些内容,额外需要一点配置上的修改。

2.3.2.1 applicationContext-security.xml

  • 查找 <http pattern="(?x: /Report-PreviewAction\.do | /Report-.*\.do | /unieap/pages/report/.* | /components/widget/.*)(?:\?.+)?" 部分,在标签内添加以下内容:
    <intercept-url pattern="/**" access="hasRole('USER')"/>
    <logout logout-success-url="/cas-logout.jsp"/>
    <custom-filter position="CAS_FILTER" ref="casFilter"/>
    <custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>
    <custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>

2.4 登录用户集成

CAS集成属于整个系统集成中的登录集成部分,用户在登陆成功之后,前台会获取当前的登录用户的详细信息,系统后台会根据当前session中的用户ID获取当前登录用户的详细信息,由于用户不存在于DataViz本身的支撑库中,所以在CAS集成后可能还需要进行用户的集成。 用户集成在DataViz中主要是用作权限管理等功能,这些功能需要实现用户管理的接口。在登录集成中略过该步骤不会影响DataViz核心功能使用。详细内容可参见 《系统集成》中的 《第4章 用户集成》。

2.5 前台登录跳转

修改前台 /common/config.js 文件,修改以下变量值:

//cas集成配置
window.casConfig = {
  isEnable: true, //是否使用cas登录
  cas_server: "https://sso.local-example.org:8443/cas/login"//cas服务登录地址
};

第3章 其他

3.1 内外网共用

一些场景下需要部署一套 DataViz 环境,并在局域网与公网环境下同时使用。此时CAS、DataViz及其他Web应用会在局域网(即内网)部署,内网服务器间可互通,局域网内直接访问服务器,公网(外网)环境通过有公网地址的服务器上的反向代理转发至内网,实现公网访问。此情况下CAS、DataViz及其他Web应用是通过两套不同的地址访问的。

DataViz 目前可以支持这种使用方式。支持的思路是在原有一般的Dataviz配置CAS方法上通,添加一组新的配置,通过入口请求做区分。建议一般配置设置为内网访问,这里的额外配置设置为外网访问。

DataViz提供了两种添加额外配置的方式:URI路径方式和转发请求头方式,两者择一即可。

3.1.1 URI路径方式

此方式通过对DataViz后台登录路径添加后缀的方法添加新地址,如默认的Dataviz登录路径为/login/cas,新添加的外网访问的登录路径为/login/casWeb

3.1.1.1 CAS ServiceId

举例(正则):

  • 内网 ServiceId: ^http://192.168.0.4:8080/dataviz-service

  • 外网 ServiceId: ^http://www.myorg.org/dataviz-service

3.1.1.2 后端配置

  • 修改 applicationContext-security.xml

    修改 casInfos bean,添加以下 property(注意此配置中的后缀为Web,如使用其他后缀需修改该key值为使用的后缀):

<beans:property name="altCas">
    <beans:map key-type="java.lang.String">
        <beans:entry key="Web">
            <beans:bean class="com.neusoft.saca.dataviz.authentication.springsecurity.cas.CasInfo">
                <beans:property name="serviceProperties">
                    <beans:bean class="org.springframework.security.cas.ServiceProperties">
                        <beans:property name="service" value="${sso.cas.localServer.alt}${sso.cas.localServicePath.alt}" />
                        <beans:property name="sendRenew" value="false" />
                    </beans:bean>
                </beans:property>
                <beans:property name="loginTarget" value="${sso.cas.defaultTargetRedirect.alt}" />
                <beans:property name="logoutTarget" value="${sso.cas.casServer.alt}/logout?service=${sso.cas.localServer.alt}" />
                <beans:property name="casLogin" value="${sso.cas.casServer.alt}/login" />
                <beans:property name="casAuthn" value="${sso.cas.casServerLocal}" />
            </beans:bean>
        </beans:entry>
    </beans:map>
</beans:property>
  • cas.properties

    在 cas.properties 中,参考以下配置添加另外一组访问地址(注意这里的路径配置的是/login/casWeb):

# cas 服务Web访问地址
sso.cas.casServer.alt=https://sso.myorg.org/cas

# DataViz 后台应用Web访问地址
sso.cas.localServer.alt=http://www.myorg.org/dataviz-service

# DataViz Web登录地址
sso.cas.localServicePath.alt=/login/casWeb

# DataViz 前台Web访问地址
sso.cas.defaultTargetRedirect.alt=http://www.myorg.org/dataviz-web/src/index.html

3.1.1.3 前端配置

//cas集成配置,可支持多个网络配置
window.casConfig = {
  isEnable: true, //是否使用cas登录
  //内外网场景下要配置两套访问地址
  addresses: [{//内网配置
    dataviz_web: "http://www.local-example.org:8080/dataviz-web",//dataviz前台内网访问地址
    cas_server: "https://sso.local-example.org:8443/cas/login",//cas服务登录地址
    service_login_path: "/login/cas" //与后台的sso.cas.localServicePath配置保持一致
  }, {//外网配置
    dataviz_web: "http://www.myorg.org/dataviz-web",//dataviz前台外网访问地址
    cas_server: "https://sso.myorg.org/cas/login",//cas服务登录地址
    service_login_path: "/login/casWeb" //与后台的sso.cas.localServicePath.alt配置保持一致
  }]
};

3.1.2 转发请求头方式

此方式下新添加的配置仍使用原来的 /login/cas/logout/cas 路径,但反向代理向内网转发时,需要配置添加请求头,后端以此区分内外网环境。

3.1.2.1 CAS ServiceId

举例(正则):

  • 内网 ServiceId: ^http://192.168.0.4:8080/dataviz-service

  • 外网 ServiceId: ^http://www.myorg.org/dataviz-service

3.1.2.2 反向代理转发配置

以 nginx 为例,为后端转发添加如下配置:

proxy_set_header X-Cas-Alt Web;

3.1.2.3 后端配置

  • 修改 applicationContext-security.xml

    修改 casInfos bean,添加以下两个properties:(注意此配置中的key值应与转发请求头X-Cas-Alt中的值保持一致)

  <beans:property name="altCas">
    <beans:map key-type="java.lang.String">
        <beans:entry key="Web">
            <beans:bean class="com.neusoft.saca.dataviz.authentication.springsecurity.cas.CasInfo">
                <beans:property name="serviceProperties">
                    <beans:bean class="org.springframework.security.cas.ServiceProperties">
                        <beans:property name="service" value="${sso.cas.localServer.alt}${sso.cas.localServicePath.alt}" />
                        <beans:property name="sendRenew" value="false" />
                    </beans:bean>
                </beans:property>
                <beans:property name="loginTarget" value="${sso.cas.defaultTargetRedirect.alt}" />
                <beans:property name="logoutTarget" value="${sso.cas.casServer.alt}/logout?service=${sso.cas.localServer.alt}" />
                <beans:property name="casLogin" value="${sso.cas.casServer.alt}/login" />
                <beans:property name="casAuthn" value="${sso.cas.casServerLocal}" />
            </beans:bean>
        </beans:entry>
    </beans:map>
  </beans:property>

  <beans:property name="useHeaderName" value="X-Cas-Env" />

注意:

此配置中的后缀为Web,如头中使用其他的值则应修改第一个Property对应key值;

第二个 Property useHeaderName 的值为新加的头的名称。建议该值使用默认的 X-Cas-Env 值。如不使用该值还需修改 web.xml。

  • web.xml(可选)

    如果未使用默认的 X-Cas-Env 头名称而使用了其他自定义名称,则需要额外修改web.xml配置,否则不需修改。

    修改方式:查找 crossDomainFilter 过滤器,将自定义的头名称添加到 extraAllowedHeaders 参数值中。

  • cas.properties

    在 cas.properties 中,参考以下配置添加另外一组访问地址:

# cas 服务Web访问地址
sso.cas.casServer.alt=https://www.myorg.org/cas

# DataViz 后台应用Web访问地址
sso.cas.localServer.alt=http://www.myorg.org/dataviz-service

# DataViz Web登录地址,不用修改
sso.cas.localServicePath.alt=/login/cas

# DataViz 前台Web访问地址
sso.cas.defaultTargetRedirect.alt=http://www.myorg.org/dataviz-web/src/index.html

3.1.2.4 前端配置

//cas集成配置,可支持多个网络配置
window.casConfig = {
  isEnable: true, //是否使用cas登录
  //内外网场景下要配置两套访问地址
  addresses: [{//内网配置
    dataviz_web: "http://www.local-example.org:8080/dataviz-web",//dataviz前台内网访问地址
    cas_server: "https://sso.local-example.org:8443/cas/login",//cas服务登录地址
  }, {//外网配置
    dataviz_web: "http://www.myorg.org/dataviz-web",//dataviz前台外网访问地址
    cas_server: "https://sso.myorg.org/cas/login",//cas服务登录地址
  }]
};

results matching ""

    No results matching ""

    results matching ""

      No results matching ""