IdP认证对接方案4-企业微信

       感谢首师大张刚刚老师的文档勘误

       作为IdP对接ldap、oauth、CAS之后的第4种身份认证方式,“IdP+企业微信”对接方案适用于学校内部准备采用企业微信完成CARSI认证的情况。所采用IdP版本为3.4.7。具体对接工作包括:

  1. 学校在本校的企业微信中申请“CARSI服务”应用;
  2. 配置“CARSI服务”应用;
  3. 安装配置CARSI IdP;

  4. IdP运行测试。

1、申请企业微信应用“CARSI资源服务”

        进入企业微信,应用管理,自建,选择创建应用。如果已经申请过应用,直接进入应用详情页面

        创建完成后,进入应用详情,记录AgentId和Secret,后续IdP安装与配置需要用到这两个参数。

        进入我的企业,获取企业ID。后续IdP安装与配置需要用到这个参数。

2、配置“CARSI资源共享”应用

        进入应用详情,功能,将工作台应用主页修改成https://dspre.carsi.edu.cn/Shibboleth.sso/Login?SAMLDS=1&target=https%3A%2F%2Fdspre.carsi.edu.cn%2Fwxds&entityID=https%3A%2F%2F{SERVERNAME}%2Fidp%2Fshibboleth。

        注意:请将{SERVERNAME}替换成IdP域名,如idp.xxx.edu.cn。

        进入应用详情,开发者接口,企业微信授权登录,授权回调域名改成IdP域名,例如idp.xxx.edu.cn。

进入应用详情,开发者接口,网页授权及JS-SDK,设置改成IdP域名,例如idp.xxx.edu.cn

3、安装配置CARSI IdP

3.1 准备IdP系统环境(CentOS8)

  • 系统环境:CentOS 8 64bit 最小安装,内存≥4G,硬盘≥50G;
  • IdP域名(网络中心维护,建议:idp.xxx.edu.cn;图书馆维护,建议:idp-lib.xxx.edu.cn),以及对应的https证书,域名一经确定,安装配置后无法修改;
  • 网络可通达校园网外网,如果机器前面有防火墙,需要开通机器的TCP 80,443端口;
  • 时间同步服务器,以ntp.aliyun.com为例,可根据校园网网络情况进行调整;

3.2 手动安装(CentOS8)

1. 基础配置(网络配置、修改主机名称、关闭selinux、配置时间同步)

#修改默认密码
[root@www ~]# passwd
#输入两次新密码

#配置网络
[root@www ~]# vi /etc/sysconfig/network-scripts/ifcfg-ens160
BOOTPROTO=static
ONBOOT=yes
IPADDR=IP地址
NETMASK=子网掩码
GATEWAY=默认网关
DNS1=DNS服务器

#修改
IPV6INIT=yes
IPV6_AUTOCONF=no
#新增
IPV6ADDR=IPv6地址,例如xxxx:xxxx:xxxx:xxxx::xxxx/64
IPV6_DEFAULTGW=IPv6网关,例如xxxx:xxxx:xxxx:xxxx::1

[root@www ~]# nmcli c reload

#修改主机名
[root@www ~]# vi /etc/hostname
#将localhost.localdomain改成主机域名,例如idp.xxx.xxx.xxx
[root@www ~]# hostname xxx.xxx.xxx.xxx
#修改hostname立即生效

#关闭SELinux
[root@www ~]#setenforce 0

#关闭开机启动SELinux
[root@www ~]# vi /etc/selinux/config
# line 7:修改为
SELINUX=disable

# 查看当前selinux状态
[root@www ~]# getenforce
Permissive #表示selinux已关闭

#配置时间同步
[root@www ~]# vi /etc/chrony.conf
#注释掉
#pool 2.centos.pool.ntp.org iburst
#新增时间同步服务器
pool ntp.aliyun.com iburst
#重启时间同步服务
[root@www ~]# systemctl restart chronyd
[root@www ~]# systemctl enable chronyd
#手动同步时间
[root@www ~]# chronyc -a makestep
200 OK #同步成功
#设置时区
[root@www ~]# timedatectl set-timezone Asia/Shanghai
#查看时间同步结果
[root@www ~]# timedatectl
               Local time: Thu 2020-07-02 14:51:24 CST
           Universal time: Thu 2020-07-02 06:51:24 UTC
                 RTC time: Thu 2020-07-02 06:51:23
                Time zone: Asia/Shanghai (CST, +0800)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

2.开放本机端口

        IdP本机开放80、443端口,443端口提供web服务。80端口用于某些HTTPS证书在更新时的连接性测试(如Let's Encrypt),可根据实际情况选择是否开启。

        本机防火墙上开放相应端口(http和https分别对应80和443)

        注意:8443端口在SAML1协议中使用,新版IdP和SP采用SAML2协议,可以停止SAML1协议的支持,无需开启8443端口

[root@www ~]# firewall-cmd --add-service=http --permanent
[root@www ~]# firewall-cmd --add-service=https --permanent

        刷新本机防火墙

[root@www ~]# firewall-cmd --reload

        如果本机前面配置有其他防火墙,请联系防火墙管理员开通:外部服务器可访问本机80 、443端口(TCP端口)。

3.安装IdP运行环境

        安装tomcat和nginx,tomcat是idp的运行容器,同时为了便于日志收集以及https证书配置,安装nginx提供web服务,用户直接访问nginx,nginx再将请求转发到tomcat,并返回该请求的响应结果。

1)安装JDK

[root@www ~]# dnf -y install java-11-openjdk java-11-openjdk-devel
[root@www ~]# cat > /etc/profile.d/java.sh <<'EOF'
export JAVA_HOME=$(dirname $(dirname $(readlink $(readlink $(which java)))))
export PATH=$PATH:$JAVA_HOME/bin
EOF
[root@www ~]# source /etc/profile.d/java.sh
[root@www ~]# java --version
openjdk 11.0.5 2019-10-15 LTS
OpenJDK Runtime Environment 18.9 (build 11.0.5+10-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.5+10-LTS, mixed mode, sharing)

2)安装配置Tomcat

[root@www ~]# curl -O https://downloads.apache.org/tomcat/tomcat-9/v9.0.39/bin/apache-tomcat-9.0.39.tar.gz
[root@www ~]# tar zxvf apache-tomcat-9.0.39.tar.gz
[root@www ~]# mv apache-tomcat-9.0.39 /usr/libexec/tomcat9
[root@www ~]# useradd -M -d /usr/libexec/tomcat9 tomcat
[root@www ~]# chown -R tomcat. /usr/libexec/tomcat9
[root@dlp ~]# vi /usr/lib/systemd/system/tomcat9.service
# create new
[Unit]
Description=Apache Tomcat 9
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/libexec/tomcat9/bin/startup.sh
ExecStop=/usr/libexec/tomcat9/bin/shutdown.sh
RemainAfterExit=yes
User=tomcat
Group=tomcat

[Install]
WantedBy=multi-user.target

[root@dlp ~]# systemctl enable --now tomcat9

#tomcat配置
#新建idp.xml
[root@www ~]# vi /usr/libexec/tomcat9/conf/Catalina/localhost/idp.xml
<Context docBase="/opt/shibboleth-idp/war/idp.war"
                privileged="true"
                antiResourceLocking="false"
                antiJARLocking="false"
                unpackWAR="false"
                swallowOutput="true" />

[root@www ~]# vi /usr/libexec/tomcat9/conf/server.xml
#在倒数第3行</Engine>前面,增加下述配置。从而将传递给应用的客户端ip和请求协议替换为X-Forwarded-For和X-Forwarded-Proto中的值。
   <Valve className="org.apache.catalina.valves.RemoteIpValve"
           internalProxies="127.0.0.1|0:0:0:0:0:0:0:1"  
           remoteIpHeader="x-forwarded-for"
           protocolHeader="x-forwarded-proto"
    />

3)安装配置Nginx

[root@www ~]# dnf -y install nginx
[root@www ~]# vi /etc/nginx/nginx.conf
# line 41: change to your hostname
server_name www.srv.world;
#配置http自动跳转至https
在listen       [::]:80 default_server;后加上
return       301 https://$host$request_uri;
#将默认的https配置部分取消注释,其中/etc/nginx/fullchain.pem请替换成证书的绝对路径,/etc/nginx/privkey.pem请替换成证书key的绝对路径
    server {
        listen       443 ssl http2 default_server;
        listen       [::]:443 ssl http2 default_server;
        server_name  idp.xxx.edu.cn;
        root         /usr/share/nginx/html;

        ssl_certificate "/etc/nginx/fullchain.pem";
        ssl_certificate_key "/etc/nginx/privkey.pem";
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
        ssl_ciphers PROFILE=SYSTEM;
        ssl_prefer_server_ciphers on;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }
[root@www ~]# systemctl enable --now nginx

#新建idp.xml
[root@www ~]# vi /etc/nginx/nginx.conf
#在server标签里面新增
      location /idp {
        proxy_pass      http://localhost:8080/idp;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
      }

4. 安装配置IdP

1)安装IdP包

[root@www ~]# curl -O https://shibboleth.net/downloads/identity-provider/3.4.7/shibboleth-identity-provider-3.4.7.tar.gz
[root@www ~]# tar zxvf shibboleth-identity-provider-3.4.7.tar.gz
[root@www ~]# cd shibboleth-identity-provider-3.4.7
[root@www ~]# ./bin/install.sh
Source (Distribution) Directory (press <enter> to accept default): [/root/inst/shibboleth-identity-provider-3.x.x] #默认回车
Installation Directory: [/opt/shibboleth-idp] #默认回车
Hostname: [idp.xxx.edu.cn] enter #确认是修改后的域名,无误后回车
SAML EntityID: [https://域名/idp/shibboleth] #默认回车
Attribute Scope: [xxx.edu.cn] #输入学校域名,如xxx.edu.cn 回车
Backchannel PKCS12 Password: #创建后台证书密码
Re-enter password: #再输入一遍
Cookie Encryption Key Password: #创建Cookie加密密码
Re-enter password: #再输入一遍
Warning: /opt/shibboleth-idp/bin does not exist.
Warning: /opt/shibboleth-idp/dist does not exist.
Warning: /opt/shibboleth-idp/doc does not exist.
Warning: /opt/shibboleth-idp/system does not exist.
Warning: /opt/shibboleth-idp/webapp does not exist.
Generating Signing Key, CN = 域名 URI = https://域名/idp/shibboleth ...
...done
Creating Encryption Key, CN = 域名 = https://域名/idp/shibboleth ...
...done
Creating Backchannel keystore, CN = 域名 URI = https://域名/idp/shibboleth ...
...done
Creating cookie encryption key files...
...done
Rebuilding /opt/shibboleth-idp/war/idp.war ...
...done
BUILD SUCCESSFUL
#安装成功

2)配置IdP

        Tomcat 默认没有提供 Java Server Tag Library,这使得 IdP3 的 status 页面无法显示。解决的办法是下载 jstl的jar包,然后放在/opt/shibboleth-idp/edit-webapp/WEB-INF/lib/ 内,然后需要重新 Build 一下 idp。在 idp.home 的目录下,./bin/build.sh 即可

        将no-conversation-state.rar解压缩后的no-conversation-state.jsp放到/opt/shibboleth-idp/edit-webapp里面,将json-20200518.jarshib-cas-authenticator-3.3.0.jarcas-client-core-3.6.0.jar放到/opt/shibboleth-idp/edit-webapp/WEB-INF/lib目录下,将web.rar解压缩后的web.xml放到/opt/shibboleth-idp/edit-webapp/WEB-INF/下

[root@www ~]# cd /opt/shibboleth-idp/edit-webapp/WEB-INF/lib/
[root@www ~]# curl -O https://build.shibboleth.net/nexus/service/local/repositories/thirdparty/content/javax/servlet/jstl/1.2/jstl-1.2.jar
[root@www ~]# cd /opt/shibboleth-idp/bin
[root@www ~]# ./build.sh
Installation Directory: [/opt/shibboleth-idp] #回车
Rebuilding /opt/shibboleth-idp/war/idp.war ...
...done
BUILD SUCCESSFUL

        更新idp.properties

[root@www ~]# vi /opt/shibboleth-idp/conf/idp.properties
#修改
idp.authn.flows=External
#新增,将{APPID}替换成应用的secret,将{APPSECRET}替换成企业ID,将{AgentID}替换成应用的agentid,将{SERVERNAME}替换成idp的域名
shibcas.oauth2UrlPrefix = https://open.weixin.qq.com/connect/oauth2/authorize
shibcas.oauth2LoginUrl = ${shibcas.oauth2UrlPrefix}?appid={APPSECRET}&response_type=code&scope=snsapi_base&state=STATE
shibcas.oauth2LoginUrlh5 = https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid={APPSECRET}&agentid={AgentID}&state=STATE
shibcas.serverName = https://{SERVERNAME}
shibcas.oauth2TokenUrl = https://qyapi.weixin.qq.com/cgi-bin/gettoken
shibcas.oauth2ResourceUrl = https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo
shibcas.oauth2GetUserUrl = https://qyapi.weixin.qq.com/cgi-bin/user/get
shibcas.oauth2clientid = {APPSECRET}
shibcas.oauth2clientsecret = {APPID}
shibcas.oauth2redirecturi = https%3a%2f%2f{SERVERNAME}%2fidp%2fAuthn%2fExternal%3fconversation%3de1s1
shibcas.getdepartmentUrl = https://qyapi.weixin.qq.com/cgi-bin/department/list

2.1)属性定义

        配置属性释放,用以下内容替换/opt/shibboleth-idp/conf/attribute-resolver.xml文件。

        修改<AttributeDefinition xsi:type="ScriptedAttribute" id="eduPersonScopedAffiliation">中的<Script>部分,根据position的实际取值进行映射的调整。关于身份属性取值,需要将本地用户身份的取值,对应到CARSI联盟标准取值,包括:faculty(教师),student(学生),staff(教工),employee(雇员),member(各类人员,包括faculty、student、staff、employee),alum(校友),affiliate(附属人员或临聘,常用),other(CARSI补充,不建议优先使用)。建议配置时尽可能细化用户身份分类,避免后期修改配置。

        注意:默认使用企业微信的position表示用户身份。

<?xml version="1.0" encoding="UTF-8"?>
<AttributeResolver
        xmlns="urn:mace:shibboleth:2.0:resolver"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd">
 
    <!-- ========================================== -->
    <!--      Attribute Definitions                 -->
    <!-- ========================================== -->
 
    <AttributeDefinition xsi:type="ScriptedAttribute" id="eduPersonScopedAffiliation">
        <Dependency ref="position" />
        <Script><![CDATA[
        var localpart = "";
        if(typeof(position)=="undefined"){
            localpart = "member";
        }else{
            if(position.getValues().get(0)=="staf") localpart = "staff";
            else if(position.getValues().get(0)=="std") localpart = "student";
            else localpart = "member";
        }
        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="position" principalAttributeName="position"></AttributeDefinition>
 
    <AttributeDefinition xsi:type="Scoped" id="eduPersonPrincipalName" scope="%{idp.scope}">
        <InputAttributeDefinition ref="uid" />
        <AttributeEncoder xsi:type="SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonPrincipalName" encodeType="false" />
        <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" friendlyName="eduPersonPrincipalName" encodeType="false" />
    </AttributeDefinition>
 
    <AttributeDefinition id="uid" xsi:type="PrincipalName">
        <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:uid" encodeType="false" />
        <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.1" friendlyName="uid" encodeType="false" />
    </AttributeDefinition>
 
    <AttributeDefinition id="eduPersonTargetedID" xsi:type="SAML2NameID" nameIdFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
        <InputDataConnector ref="ComputedIDConnector" attributeNames="computedID"/>
        <AttributeEncoder xsi:type="SAML1XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" encodeType="false"/>
        <AttributeEncoder xsi:type="SAML2XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" friendlyName="eduPersonTargetedID" encodeType="false"/>
    </AttributeDefinition>
 
    <AttributeDefinition id="eduPersonEntitlement" xsi:type="Simple">
        <InputDataConnector ref="staticAttributes" attributeNames="eduPersonEntitlement" />
        <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonEntitlement" encodeType="false"/>
        <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.7" friendlyName="eduPersonEntitlement" encodeType="false"/>
    </AttributeDefinition>
 
    <DataConnector id="ComputedIDConnector" xsi:type="ComputedId" generatedAttributeID="computedID" salt="xxxxxxxxxxxxxxxxxxxx" encoding="BASE64">
        <InputAttributeDefinition ref="eduPersonPrincipalName" />
    </DataConnector>
 
    <DataConnector id="staticAttributes" xsi:type="Static">
        <Attribute id="eduPersonEntitlement">
        <Value>urn:mace:dir:entitlement:common-lib-terms</Value>
        </Attribute>
    </DataConnector>
 
</AttributeResolver>

2.2)释放用户属性

2.3)IdP隐私保护配置

2.4)Metadata配置

        下载https://dspre.carsi.edu.cn/carsifed-metadata-pre.xml 文件,放入/opt/shibboleth-idp/metadata文件夹,并且修改文件的所属用户和组。

[root@www ~]# chown -R tomcat.tomcat /opt/shibboleth-idp

        将联盟提供的metadata验证证书dsmeta.pem放入/opt/shibboleth-idp/credentials目录下。修改metadata-providers.xml,maxRefreshDelay="PT10M"表示metadata最长更新时间10分钟。

[root@www ~]# vi /opt/shibboleth-idp/conf/metadata-providers.xml

#在</MetadataProvider>内新增
<MetadataProvider id="HTTPMetadata"
          xsi:type="FileBackedHTTPMetadataProvider"
          backingFile="/opt/shibboleth-idp/metadata/carsifed-metadata-pre.xml"
          minRefreshDelay="PT5M"
          maxRefreshDelay="PT10M"
          metadataURL="https://dspre.carsi.edu.cn/carsifed-metadata-pre.xml"> 

         <MetadataFilter xsi:type="SignatureValidation" certificateFile="/opt/shibboleth-idp/credentials/dsmeta.pem" />
         <MetadataFilter xsi:type="EntityRoleWhiteList">
             <RetainedRole>md:SPSSODescriptor</RetainedRole>
         </MetadataFilter>
     </MetadataProvider>

        重启tomcat和apache

[root@www ~]# chown -R tomcat.tomcat /opt/shibboleth-idp
#启动tomcat和apache
[root@www ~]# systemctl restart tomcat9
[root@www ~]# systemctl restart nginx

2.5)日志功能

[root@www ~]# vi /opt/shibboleth-idp/conf/audit.xml

#line 18 替换
<entry key="Shibboleth-Audit" value="%T|%b|%I|%SP|%P|%IdP|%bb|%III|%u|%ac|%attr|%n|%i|%a|%s|" />

#取消注释 
<bean id="shibboleth.AuditDateTimeFormat" class="java.lang.String" c:_0="YYYY-MM-dd'T'HH:mm:ss.SSSZZ" />
<util:constant id="shibboleth.AuditDefaultTimeZone" static-field="java.lang.Boolean.TRUE" />

[root@www ~]# mkdir /usr/share/nginx/html/auditlog
[root@www ~]# vi /etc/nginx/nginx.conf

#在location /idp前面增加
    location /auditlog {
        allow 115.27.243.6;
        deny all;
    }

#新建
[root@www ~]# vi /usr/share/nginx/html/auditlog/auditlog.sh
rm -rf /usr/share/nginx/html/auditlog/auditlog-`date -d -24hours +%Y-%m-%d-%H`.log
grep `date -d -1hours +%Y-%m-%dT%H` /opt/shibboleth-idp/logs/idp-audit.log > /usr/share/nginx/html/auditlog/auditlog-`date -d -1hours +%Y-%m-%d-%H`.log

#添加定时任务
[root@www ~]# crontab -e
0 */1 * * * sh /usr/share/nginx/html/auditlog/auditlog.sh >/dev/null 2>&1

#重启tomcat和apache
[root@www ~]# systemctl restart tomcat9
[root@www ~]# systemctl restart nginx

4、IdP运行测试

1)向CARSI联盟提交IdP配置信息(上传IdP Metadata)

        将/opt/shibboleth-idp/metadata/idp-metadata.xml文件下载到本地。

        登陆 CARSI会员自服务系统 用户名为申请时填的学校域名,密码为申请时填的项目负责人的手机号。

        在“我的CARSI→我的IdP”中,选择“上传Metadata”完成该文件的上传,上传成功后该页面会显示“已提供”。

2)PC端测试步骤

3)企业微信端测试

版权所有©北京大学计算中心