CAS 6.x + Delegated Authentication SAML2.0 配置记录

最近领导派了一个活儿, 需要把我们CAS系统的身份识别交给甲方的系统, 甲方的系统是SAML2.0的协议。

由于之前对SAML2.0协议了解不多,折腾了不少时间,在这里记录一下。以后忘掉还可以看看。

首先是关于SAML2.0协议的简单介绍,在B站找到一个还不错的视频。https://www.bilibili.com/video/BV1sA411Y758/

 

1.配置cas.properties,让cas作为 SAML2.0 的 IDP(身份提供方)

  这一步其实没啥用, 因为我是要对接别人的IDP 而不是让自己的cas成为IDP,如果需要自己建一个cas作为IDP的话可以参考

cas.properties:  cas.authn.saml-idp.core.entity-id=${cas.server.name}/idp

build.gradle: implementation "org.apereo.cas:cas-server-support-saml-idp:${project.'cas.version'}"
配置完之后可以打开idp路径查看idp/metadata-> http://localhost:8888/cas/idp/metadata

  上面两个配置完成后其实作为IDP还是不能运作的(未授权的服务),你要对外提供身份验证的话,除了别人配置你作为IDP, 你也需要知道是哪个服务在用你, 你还需要给SP(服务提供方)权限,并且配置sp的metadata

 在/etc/cas/services下面配置sp的信息,测试环境的话可以配置得粗暴一点:

saml2-10004.json:
CAS 6.x + Delegated Authentication SAML2.0 配置记录

{   "@class" : "org.apereo.cas.support.saml.services.SamlRegisteredService",   "serviceId" : ".+",   "name" : "SAMLService",   "id" : 10000003,   "evaluationOrder" : 10,   "metadataLocation" : "file:/etc/cas/config/sp-metadata-local.xml"  }

View Code

 sp-metadata-local.xml: 这个文件一般是sp提供的。里面一般包含EntityID, 签名和加密的共钥,以及支持的NameID格式。需要的属性字段(这里没有配属性字段)。

CAS 6.x + Delegated Authentication SAML2.0 配置记录

<?xml version="1.0" encoding="UTF-8"?> <md:EntityDescriptor ID="_cb7748cb6778495398727ae8bfcaa72e62beea3" entityID="liveramp-sp"                      validUntil="2043-03-16T11:36:25.739Z" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">     <md:Extensions xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport">         <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#hmac-sha256"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#hmac-sha384"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#hmac-sha512"/>         <alg:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"/>         <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>         <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>         <alg:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>     </md:Extensions>     <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false"                         protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.0:protocol urn:oasis:names:tc:SAML:1.1:protocol">         <md:Extensions xmlns:init="urn:oasis:names:tc:SAML:profiles:SSO:request-init">             <init:RequestInitiator Binding="urn:oasis:names:tc:SAML:profiles:SSO:request-init"                                    Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT"/>         </md:Extensions>         <md:KeyDescriptor use="signing">             <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">                 <ds:X509Data>                     <ds:X509Certificate>xxxx                     </ds:X509Certificate>                 </ds:X509Data>             </ds:KeyInfo>         </md:KeyDescriptor>         <md:KeyDescriptor use="encryption">             <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">                 <ds:X509Data>                     <ds:X509Certificate>xxxx                     </ds:X509Certificate>                 </ds:X509Data>             </ds:KeyInfo>         </md:KeyDescriptor>         <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"                                 Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT&amp;logoutendpoint=true"/>         <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"                                 Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT&amp;logoutendpoint=true"/>         <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"                                 Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT&amp;logoutendpoint=true"/>         <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"                                 Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT&amp;logoutendpoint=true"/>         <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>         <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>         <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>         <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>         <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"                                      Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT"                                      index="0"/>     </md:SPSSODescriptor> </md:EntityDescriptor>

View Code

  如果里面含有属性字段, sp-metadata.xml会多出一些内容:

CAS 6.x + Delegated Authentication SAML2.0 配置记录

        <md:AttributeConsumingService index="0">             <md:RequestedAttribute Name="firstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"                                    isRequired="false"/>             <md:RequestedAttribute Name="lastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"                                    isRequired="false"/>             <md:RequestedAttribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"                                    isRequired="false"/>             <md:RequestedAttribute FriendlyName="displayName" Name="urn:oid:2.16.840.1.113730.3.1.241"                                    NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="false"/>             <md:RequestedAttribute FriendlyName="cn" Name="cn"                                    NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="false"/>         </md:AttributeConsumingService>

View Code

 

2. 配置委托认证

  1). cas的委托认证都是通过pac4j去做的, 所以先要加上pac4j的依赖。

   build.gradle里加上依赖:  implementation "org.apereo.cas:cas-server-support-pac4j-webflow:${project.'cas.version'}" 

  2) 修改cas.properties加上委托认证的配置

CAS 6.x + Delegated Authentication SAML2.0 配置记录

cas.authn.pac4j.core.lazy-init=false cas.authn.pac4j.saml[0].client-name=SAML2.0-CLIENT cas.authn.pac4j.saml[0].display-name=SAML  # 如果是文件 file:/xxx/xxx.xml cas.authn.pac4j.saml[0].identity-provider-metadata-path=https://yd-dev.liverampapac.com/sso/idp/metadata   cas.authn.pac4j.saml[0].keystore-path=file:/private/etc/cas/config/saml2.0/cer/samlKeystore.jks cas.authn.pac4j.saml[0].keystore-password=xxx cas.authn.pac4j.saml[0].private-key-password=xxx cas.authn.pac4j.saml[0].service-provider-entity-id=xxxxx # this is important cas.authn.pac4j.saml[0].service-provider-metadata-path=file:/etc/cas/config/saml2.0/sp-metadata.xml # If false global logout may not work, it's depends on IDP settings cas.authn.pac4j.saml[0].sign-service-provider-logout-request=true

View Code

  使用委托认证就是要把认证的行为交给IDP, 而自己成为SP. IDP跟SP之间的通信的安全,或者说他们之前的信任是需要证书支持的,一般在IDP和SP的metadata都会配置证书的公钥,当信息传到自己系统时, 用自己的私钥做验签/解密。

  上面在cas.properties的改动里还有一个 sp-metadata.xml需要我们自己构建(下面这个sp-metadata.xml 最低配置其实只需要配两个公钥就能工作了,其它的cas会帮我们自动生成)

CAS 6.x + Delegated Authentication SAML2.0 配置记录

<?xml version="1.0" encoding="UTF-8"?> <md:EntityDescriptor ID="aabc3592a94845c6b38b88894e6d19415746a23"                      entityID="liveramp-sp"                      xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">     <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false"                         protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.0:protocol urn:oasis:names:tc:SAML:1.1:protocol">         <md:KeyDescriptor use="signing">             <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">                 <ds:X509Data>                     <ds:X509Certificate>xxx                     </ds:X509Certificate>                 </ds:X509Data>             </ds:KeyInfo>         </md:KeyDescriptor>         <md:KeyDescriptor use="encryption">             <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">                 <ds:X509Data>                     <ds:X509Certificate>xxx                     </ds:X509Certificate>                 </ds:X509Data>             </ds:KeyInfo>         </md:KeyDescriptor>         <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT&amp;logoutendpoint=true"/>         <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign" Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT&amp;logoutendpoint=true"/>          <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT&amp;logoutendpoint=true"/>         <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT&amp;logoutendpoint=true"/>         <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>         <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>         <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>         <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>         <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</md:NameIDFormat>         <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT" index="0"/>         <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="http://localhost:8888/cas/login?client_name=IHG-SAML2.0-CLIENT" index="1"/>   <!--   配置在这里 sp/metadata并不会展示和实际起作用 需要在cas.properties中配置     -->         <md:AttributeConsumingService index="0"                                       xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"                                       xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">             <!-- SAML V2.0 attribute syntax -->             <md:RequestedAttribute isRequired="true"                                    NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"                                    Name="cn"                                    FriendlyName="cn"/>             <md:RequestedAttribute                     NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"                     Name="urn:oid:2.16.840.1.113730.3.1.241"                     FriendlyName="displayName"/>             <md:RequestedAttribute Name="firstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"/>             <md:RequestedAttribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"/>             <md:RequestedAttribute Name="lastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"/>         </md:AttributeConsumingService>     </md:SPSSODescriptor> </md:EntityDescriptor>

View Code

  证书的生成如果觉得麻烦的话, 这里有一键生成工具,两个文件放一个目录下面 然后一键生成 ./gen.sh [*name] [*domain] [*passwd] ,生成出来把crt文件的内容放到<X509Certificate>里面。jks文件路径和passwd也都需要配置到

cas.porperties里面.

  gen.sh:

CAS 6.x + Delegated Authentication SAML2.0 配置记录

#!/bin/bash echo "自动创建自定义证书" region=$1 region_suffix=$2 pass=$3  if [[ "X$region" == "X" ]]; then   echo "region is empty"   exit 1 fi  if [[ "X$region_suffix" == "X" ]]; then   echo "region_suffix is empty"   exit 1 fi  if [[ ! -f "openssl.cnf" ]]; then   echo "openssl.cnf file is not exist"   exit 1 fi  mkdir $region  cp openssl.cnf $region/  cd $region  mkdir demoCA  cd demoCA  mkdir private  SAN="[SAN]nsubjectAltName=DNS:*.$region_suffix" subj="/C=CN/ST=NT/L=NT/O=LIVERAMP-CN/OU=TS/CN=$region_suffix"  echo "生成CA根证书" openssl genrsa -des3 -passout pass:$pass -out private/cakey.pem 2048 openssl req -sha256 -new -nodes -key private/cakey.pem -passin pass:$pass    -x509 -days 3650 -out cacert.pem -subj "$subj"  cd ..  echo "生成中间CA证书" openssl genrsa -des3 -passout pass:$pass -out $region.key 2048  openssl req -new -key $region.key    -passin pass:$pass    -subj "$subj" -reqexts SAN    -config <(cat openssl.cnf      <(printf "$SAN"))    -out $region.csr  echo "移除密码" cp $region.key $region.key.org openssl rsa -passin pass:$pass -in $region.key.org -out $region.key  echo "生成x509 crt证书" openssl x509 -req    -days 36500    -in $region.csr    -signkey $region.key    -out $region.crt    -extensions SAN    -extfile <(cat openssl.cnf <(printf "$SAN"))  echo "查看crt证书" openssl x509 -text -in $region.crt echo "生成pkcs12" openssl pkcs12 -passout pass:$pass -export -in $region.crt -inkey $region.key -out $region.p12 -CAfile $region.crt echo "生成JKS" keytool -importkeystore -v -srckeystore $region.p12 -srcstoretype pkcs12 -srcstorepass $pass -destkeystore $region.jks -deststoretype pkcs12 -deststorepass $pass

gen.sh

CAS 6.x + Delegated Authentication SAML2.0 配置记录

HOME            = . oid_section     = new_oids [ new_oids ] tsa_policy1 = 1.2.3.4.1 tsa_policy2 = 1.2.3.4.5.6 tsa_policy3 = 1.2.3.4.5.7  [ ca ] default_ca  = CA_default          [ CA_default ] dir     = ./demoCA       certs       = $dir/certs         crl_dir     = $dir/crl       database    = $dir/index.txt new_certs_dir   = $dir/newcerts certificate = $dir/cacert.pem    serial      = $dir/serial        crlnumber   = $dir/crlnumber crl     = $dir/crl.pem       private_key = $dir/private/cakey.pem x509_extensions = usr_cert name_opt    = ca_default         cert_opt    = ca_default default_days    = 365            default_crl_days= 30             default_md  = default        preserve    = no policy      = policy_match  [ policy_match ] countryName     = match stateOrProvinceName = match organizationName    = match organizationalUnitName  = optional commonName      = supplied emailAddress        = optional  [ policy_anything ] countryName     = optional stateOrProvinceName = optional localityName        = optional organizationName    = optional organizationalUnitName  = optional commonName      = supplied emailAddress        = optional  [ req ] default_bits        = 2048 default_keyfile     = privkey.pem distinguished_name  = req_distinguished_name attributes      = req_attributes x509_extensions = v3_ca string_mask = utf8only  [ req_distinguished_name ] countryName         = Country Name (2 letter code) countryName_default     = AU countryName_min         = 2 countryName_max         = 2 stateOrProvinceName     = State or Province Name (full name) stateOrProvinceName_default = Some-State localityName            = Locality Name (eg, city) 0.organizationName      = Organization Name (eg, company) 0.organizationName_default  = Internet Widgits Pty Ltd organizationalUnitName      = Organizational Unit Name (eg, section) commonName          = Common Name (e.g. server FQDN or YOUR name) commonName_max          = 64 emailAddress            = Email Address emailAddress_max        = 64  [ req_attributes ] challengePassword       = A challenge password challengePassword_min       = 4 challengePassword_max       = 20 unstructuredName        = An optional company name  [ usr_cert ] basicConstraints=CA:FALSE nsComment           = "OpenSSL Generated Certificate" subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer  [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment  [ v3_ca ] subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer basicConstraints = critical,CA:true  [ crl_ext ] authorityKeyIdentifier=keyid:always  [ proxy_cert_ext ] basicConstraints=CA:FALSE nsComment           = "OpenSSL Generated Certificate" subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo  [ tsa ] default_tsa = tsa_config1     [ tsa_config1 ] dir     = ./demoCA       serial      = $dir/tsaserial     crypto_device   = builtin        signer_cert = $dir/tsacert.pem certs       = $dir/cacert.pem signer_key  = $dir/private/tsakey.pem  signer_digest  = sha256          default_policy  = tsa_policy1 other_policies  = tsa_policy2, tsa_policy3   digests     = sha1, sha256, sha384, sha512   accuracy    = secs:1, millisecs:500, microsecs:100   clock_precision_digits  = 0  ordering        = yes tsa_name        = yes ess_cert_id_chain   = no ess_cert_id_alg     = sha1

openssl.cnf

  配置完这些,顺利的话可以在 http://localhost:8888/cas/sp/metadata 看到sp的元数据。 然后将这个元数据提供给IDP。

 接下来在cas登录的时候, 应该可以从右侧看到IDP的名字,点一下试试能不能登录。如果不能的话, 说明配置还有一些问题。

  3. 属性释放 Attribute Release (IDP需要配置),这个是为了本地测试配置的, 实际上如果IDP那边配置好了,是不用管这个的

  作为IDP,当有SP登录成功时, 你需要确定将登录者的哪些属性给到SP, 可以配置services/xx.json 添加以下配置:

 "attributeReleasePolicy": {         "@class": "org.apereo.cas.support.saml.services.PatternMatchingEntityIdAttributeReleasePolicy",         "allowedAttributes" : [ "java.util.ArrayList", [ "cn","sn","email","firstName","lastName"]],         "fullMatch" : "true",         "reverseMatch" : "false",         "entityIds" : ".+"   }

 

4. 属性请求(SP需要配置)

  当SP向IDP请求登录时, 希望IDP返回登录者的一些信息比如email之类的。 配置cas.properties

    cas.authn.pac4j.saml[0].attribute-consuming-service-index=0     cas.authn.pac4j.saml[0].requested-attributes[0].name=FirstName     cas.authn.pac4j.saml[0].requested-attributes[0].nameFormat=urn:oasis:names:tc:SAML:2.0:attrname-format:basic     cas.authn.pac4j.saml[0].requested-attributes[1].name=LastName     cas.authn.pac4j.saml[0].requested-attributes[1].nameFormat=urn:oasis:names:tc:SAML:2.0:attrname-format:basic     cas.authn.pac4j.saml[0].requested-attributes[2].name=email     cas.authn.pac4j.saml[0].requested-attributes[2].nameFormat=urn:oasis:names:tc:SAML:2.0:attrname-format:basic     #eduPersonNickname urn:oid:2.16.840.1.113730.3.1.241     #cas.authn.pac4j.saml[0].requested-attributes[3].name=urn:oid:2.16.840.1.113730.3.1.241     #cas.authn.pac4j.saml[0].requested-attributes[3].nameFormat=urn:oasis:names:tc:SAML:2.0:attrname-format:uri     #cas.authn.pac4j.saml[0].requested-attributes[3].friendlyName=displayName     #cas.authn.pac4j.saml[0].requested-attributes[4].name=cn     #cas.authn.pac4j.saml[0].requested-attributes[4].nameFormat=urn:oasis:names:tc:SAML:2.0:attrname-format:uri     #cas.authn.pac4j.saml[0].requested-attributes[4].friendlyName=cn

发表评论

相关文章