写点什么

15 分钟部署一个 CAS 服务并对接 Shibboleth-IdP 3.4.6

用户头像
冯骐
关注
发布于: 2021 年 03 月 04 日

前言


这是一个标题党。


CAS 是一个经典的单点登录方案,又有 开源版本 的支持,因此广大提供统一身份认证解决方案的供应商鲜有不支持 CAS 的——至少投标方案上是这样的。尽管如此,实际对接的时候可能会遇到问题,又或者 CAS 不归自己负责,想做个测试又不太方便接入生产环境,总之这时候就特别想要自己部署一个测试的 CAS 来进行验证。


是的,搭一个 CAS 服务器 15 分钟就够了。


注意下文部署的 CAS 仅适合测试,不要拿这个 CAS 直接用作生产环境哦。


cas-overlay


尽管 CAS 的功能极多且复杂,但是如果只考虑测试话,我们可以尽量简化他的配置。我们只引入ldap 和json-service-registry 模块,并打包成 docker 以简化环境配置。


考虑到测试方便,我们把 cas 和 shibboleth-idp 安装在同一台服务器,因此使用了 httpd 来对两者进行代理。


本文假定已经安装好了 shibboleth-idp-3.4.6 ,并使用 httpd 方式代理发布。


  1. 首先拉取 apereo/cas-overlay-template ,由于 6.2.x 尚未正式发布,我切换到 6.1.x 分支。


git clone https://github.com/apereo/cas-overlay-template.gitcd cas-overlay-template/git checkout 6.1
复制代码
  1. 修改 build.gradle ,在 dependencies 内增加 ldap 和 json-service-registry 的编译依赖


dependencies {    // Other CAS dependencies/modules may be listed here...     compile "org.apereo.cas:cas-server-support-json-service-registry:${casServerVersion}"     compile "org.apereo.cas:cas-server-support-ldap:${casServerVersion}"}
复制代码
  1. 安装 docker,详见 Get Docker Engine - Community for CentOS


$ sudo yum install -y yum-utils \  device-mapper-persistent-data \  lvm2
复制代码


$ sudo yum-config-manager \    --add-repo \    https://download.docker.com/linux/centos/docker-ce.repo
复制代码


$ sudo yum install docker-ce docker-ce-cli containerd.io$ sudo systemctl start docker
复制代码

修改 cas-overlay-template/etc/cas/config/cas.properties 配置文件。由于我们使用 httpd 代理 cas 服务,所以我们这里可以让 httpd 卸载掉 https , cas 服务运行在 http 上即可。同时加载 json 目录的服务注册配置。

最后一行表示 cas 所释放的属性,倒数第二行表示 ldap 内属性和 cas 的映射关系。例如如果用 AD 的话,那么这里可以配成 cas.authn.ldap[0].principalAttributeList=employeeType:employeeType,sAMAccountName:uid,此时 cas 所释放的属性名依然是 uid,由 sAMAccountName 映射产生。


server.port=8080server.ssl.enabled=falsecas.server.tomcat.http.enabled=false
cas.server.name=https://idp.exmaple.orgcas.server.prefix=${cas.server.name}/cas
logging.config: file:/etc/cas/config/log4j2.xml
cas.serviceRegistry.initFromJson=falsecas.serviceRegistry.json.location=file:/etc/cas/services
cas.authn.ldap[0].type=AUTHENTICATEDcas.authn.ldap[0].ldapUrl=ldap://ldap.example.org:389cas.authn.ldap[0].useSsl=falsecas.authn.ldap[0].baseDn=dc=example,dc=orgcas.authn.ldap[0].searchFilter=uid={user}cas.authn.ldap[0].bindDn=cn=admin,dc=example,dc=orgcas.authn.ldap[0].bindCredential=passwordcas.authn.ldap[0].principalAttributeList=employeeType:employeeType,uid:uid,sn:sncas.authn.attributeRepository.defaultAttributesToRelease=employeeType,uid,sn
复制代码
  1. 在 cas-overlay-template/etc/cas/services/ 目录内新增 idp-1001.json 文件,注册我们的 idp 服务


{  "@class" : "org.apereo.cas.services.RegexRegisteredService",  "serviceId" : "^(https)://idp.example.org.*"  "name" : "idp",  "id" : 1001,  "evaluationOrder" : 10}
复制代码
  1. 执行 ./docker-build.sh 生成 docker 镜像。Dockerfile 是 cas-overlay-template/Dockerfile 这个文件。实际上就是在容器里使用 ./gradlew clean build 编译 CAS。如果在服务器上准备好了 java 11 环境的话,直接执行 ./gradlew clean build 也是一样的,有兴趣的同学可以试试。

  2. 首次执行可能会有一点慢,耐心等待我们的容器镜像构建完成。


Successfully built 6c1396544479Successfully tagged org.apereo.cas/cas:6.1.4Built CAS image successfully tagged as org.apereo.cas/cas:6.1.4REPOSITORY           TAG                 IMAGE ID            CREATED                  SIZEorg.apereo.cas/cas   6.1.4               6c1396544479        Less than a second ago   247MB
复制代码

由于我们和 idp 装在一起,而 idp 的 tomcat 已经占用了 8080 端口,所以将其映射到 8081 上。


$ docker run -d -p 8081:8080 --name="cas" org.apereo.cas/cas:6.1.4$ docker psCONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                              NAMES8ed61668ad65        org.apereo.cas/cas:6.1.4   "java -server -nover…"   3 seconds ago       Up 2 seconds        8443/tcp, 0.0.0.0:8081->8080/tcp   cas
复制代码

如果修改配置,则重新构建 docker,再重新运行构建好的容器即可。由于底层已经构建过,此时只替换了配置文件,所以速度是很快的。然后停掉当前容器,删除之再重新拉起即可。


$ docker stop cascas$ docker rm cascas$ docker run -d -p 8081:8080 --name="cas" org.apereo.cas/cas:6.1.4
复制代码

这些过程实际上也就是 cas-overlay-template/docker-run.sh 内的内容,大家可以根据实际情况修改后。直接执行该脚本即可。


  1. 修改 httpd 的配置,在 idp.example.org 的对应的 VirtualHost 内,增加下述配置,然后重启 httpd 服务。


        ProxyPreserveHost On        RequestHeader set X-Forwarded-Proto https        RemoteIPHeader X-Forwarded-For
ProxyPass "/cas/" "http://localhost:8081/cas/"

复制代码
  1. 好拉,访问 https://idp.examle.org/cas/login ,看看 cas 是不是已经起来了?


对接 Shibboleth-IdP 3.4.6


我们使用 Unicon/shib-cas-authn3 插件来对接 IdP 和 CAS。由于 IdP 3.4.3 之后有一个内部 API 变更,因此插件的配置有大幅调整,实际上变得更简单了。实测表明新版版的插件(3.3.0)还修复了一些老版本的 bug——比如一个 CAS 属性无法同时映射给两个 IdP 属性的问题。建议大家尽量选择升级 IdP 到 3.4.6 后使用新版插件对接。


准备工作



以下假定 IdP 安装在 /opt/shibboleth-idp/


  • IdP 版本至少 3.4.6


安装


  • 把下载的 no-conversation-state.jsp 放入 /opt/shibboleth-idp/edit-webapp 中

  • 把下载的 cas-client-core-3.6.0.jar 和 shib-cas-authenticator-3.3.0.jar 放入 /opt/shibboleth-idp/edit-webapp/WEB-INF/lib 中

  • 将 /opt/shibboleth-idp/dist/webapp/WEB-INF/web.xml 拷贝到 /opt/shibboleth-idp/edit-webapp/WEB-INF/web.xml


cp /opt/shibboleth-idp/dist/webapp/WEB-INF/web.xml /opt/shibboleth-idp/edit-webapp/WEB-INF/web.xml
复制代码
  • 修改 /opt/shibboleth-idp/edit-webapp/WEB-INF/web.xml 增加以下部分


...    <!-- Servlet for receiving a callback from an external CAS Server and continues the IdP login flow -->    <servlet>        <servlet-name>ShibCas Auth Servlet</servlet-name>        <servlet-class>net.unicon.idp.externalauth.ShibcasAuthServlet</servlet-class>        <load-on-startup>2</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>ShibCas Auth Servlet</servlet-name>        <url-pattern>/Authn/External/*</url-pattern>    </servlet-mapping>...
复制代码
  • 修改 idp.properties 配置文件


idp.authn.flows = External
shibcas.casServerUrlPrefix = https://idp.example.org/casshibcas.casServerLoginUrl = ${shibcas.casServerUrlPrefix}/login
# idp 的地址shibcas.serverName = https://idp.example.org
# 如果不支持 cas3.0 协议,这里修改为 cas20 并取消注释# shibcas.ticketValidatorName = cas30
复制代码

运行 /opt/shibboleth-idp/bin/build.sh 重新编译 IdP ,然后重启 IdP 即可


属性映射


直接映射


AttributeDefinition 中的 xsi:type="SubjectDerivedAttribute" 为从插件中获取属性的配置,例如下面的示例表示将 cas 释放的 sn 映射为 cn


   <AttributeDefinition xsi:type="SubjectDerivedAttribute" id="cn" principalAttributeName="sn">        <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:cn" encodeType="false" />        <AttributeEncoder xsi:type="SAML2String" name="urn:oid:2.5.4.3" friendlyName="cn" encodeType="false" />   </AttributeDefinition>
复制代码

作为引用


如果 IdP 在属性释放时还需要进行一些特殊转换,即 xsi:type="ScriptedAttribute" 或者 xsi:type="Scoped" 等,那么可以先讲属性映射进来,再作为其他 AttributeDefinition 的 Dependency 的引入,例如下面这个示例:先将 employeeType 获取到之后,标注为employeetype,然后引入到AttributeDefinition xsi:type="ScriptedAttribute" 中进行脚本计算。


    <AttributeDefinition xsi:type="ScriptedAttribute" id="eduPersonScopedAffiliation">        <Dependency ref="employeetype" />        <Script><![CDATA[        var localpart = "";        if(employeetype.getValues().get(0)=="01") localpart = "staff";        else if(employeetype.getValues().get(0)=="02") localpart = "student";        else localpart = "other";                eduPersonScopedAffiliation.addValue(localpart + "@%{idp.scope}");            ]]></Script>        <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" encodeType="false" />        <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" friendlyName="eduPersonScopedAffiliation" encodeType="false" />    </AttributeDefinition>    <AttributeDefinition xsi:type="SubjectDerivedAttribute" id="employeetype" principalAttributeName="employeeType"></AttributeDefinition>
复制代码

参考文献


  1. CAS Enterprise Single Sign-On

  2. apereo/cas-overlay-template

  3. Get Docker Engine - Community for CentOS

  4. Unicon/shib-cas-authn3

  5. 上海教育认证中心:IdP-CAS对接


以上

原文于 2020 年 3 月首发于简书,搬家存档。

行文有微调。


发布于: 2021 年 03 月 04 日阅读数: 15
用户头像

冯骐

关注

教育行业码农 2020.06.19 加入

一个教育行业的码农

评论

发布
暂无评论
15 分钟部署一个 CAS 服务并对接 Shibboleth-IdP 3.4.6