第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 完成集成。
具体操作上:
开发一个类,实现
java.util.function.Function<String, String>
接口;将其注册为 Spring Bean ,且其bean id 或 qualifier 为 casUserId。(可参考下面的示例)编译成功后将对应 .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服务登录地址
}]
};