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

前言

这是一个标题党。

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

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

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

cas-overlay

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

考虑到测试方便,我们把 casshibboleth-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.git
cd cas-overlay-template/
git checkout 6.1
  1. 修改 build.gradle ,在 dependencies 内增加 ldapjson-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
  1. 修改 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=8080
server.ssl.enabled=false
cas.server.tomcat.http.enabled=false

cas.server.name=https://idp.exmaple.org
cas.server.prefix=${cas.server.name}/cas

logging.config: file:/etc/cas/config/log4j2.xml

cas.serviceRegistry.initFromJson=false
cas.serviceRegistry.json.location=file:/etc/cas/services

cas.authn.ldap[0].type=AUTHENTICATED
cas.authn.ldap[0].ldapUrl=ldap://ldap.example.org:389
cas.authn.ldap[0].useSsl=false
cas.authn.ldap[0].baseDn=dc=example,dc=org
cas.authn.ldap[0].searchFilter=uid={user}
cas.authn.ldap[0].bindDn=cn=admin,dc=example,dc=org
cas.authn.ldap[0].bindCredential=password
cas.authn.ldap[0].principalAttributeList=employeeType:employeeType,uid:uid,sn:sn
cas.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 镜像。Dockerfilecas-overlay-template/Dockerfile 这个文件。实际上就是在容器里使用 ./gradlew clean build 编译 CAS。如果在服务器上准备好了 java 11 环境的话,直接执行 ./gradlew clean build 也是一样的,有兴趣的同学可以试试。
  2. 首次执行可能会有一点慢,耐心等待我们的容器镜像构建完成。
Successfully built 6c1396544479
Successfully tagged org.apereo.cas/cas:6.1.4
Built CAS image successfully tagged as org.apereo.cas/cas:6.1.4
REPOSITORY           TAG                 IMAGE ID            CREATED                  SIZE
org.apereo.cas/cas   6.1.4               6c1396544479        Less than a second ago   247MB

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

$ docker run -d -p 8081:8080 --name="cas" org.apereo.cas/cas:6.1.4
$ docker ps
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                              NAMES
8ed61668ad65        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 cas
cas
$ docker rm cas
cas
$ 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.jarshib-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/cas
shibcas.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" 等,那么可以先讲属性映射进来,再作为其他 AttributeDefinitionDependency 的引入,例如下面这个示例:先将 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对接

转载授权

CC BY-SA

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容