CAS 单点登录系列


小蚂蚁-CAS单点登录系列(1)-基础知识 1.单点登录 1.1.概述 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。 SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。 1.2.技术实现机制 当用户第一次访问应用系统 1 的时候,因为还没有登录,会被引导到认证系统中进行登录;根据 用户提供的登录信息,认证系统进行身份效验,如果通过效验,应该返回给用户一个认证的凭据-- ticket;用户再访问别的应用的时候,就会将这个 ticket 带上,作为自己认证的凭据,应用系统接 受到请求之后会把 ticket 送到认证系统进行效验,检查 ticket 的合法性。如果通过效验,用户就可 以在不用再次登录的情况下访问应用系统 2 和应用系统 3 了。 1.3.要实现 SSO 需要以下主要的功能 所有应用系统共享一个身份认证系统; 所有应用系统能够识别和提取 ticket 信息; 应用系统能够识别已经登录过的用户,能自动判断当前用户是否登录过,从而完成单点登录的功 能。 其中,统一的身份认证系统最重要,认证系统的主要功能是将用户的登录信息和用户信息库相比较, 对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志(ticket),返还给用户。另 外,认证系统还应该对 ticket 进行效验,判断其有效性。整个系统可以存在两个以上的认证服务器, 这些服务器甚至可以是不同的产品。认证服务器之间要通过标准的通讯协议,互相交换认证信息,就 能完成更高级别的单点登录。 2.数字签名 2.1.概念 数字签名(又称公钥数字签名、电子签章)是一种类似写在纸上的普通的物理签名,但是使用了 公钥加密领域的技术实现,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个 用于签名,另一个用于验证。 2.2.主要功能 保证信息传输的完整性、发送者的身份认证、防止交易中的抵赖发生。 数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送 的公钥才能解密被加密的摘要信息,然后用 HASH 函数对收到的原文产生一个摘要信息,与解密的摘 要信息对比。如果相同,则说明收到的信息是完整的,在传输过程 中没有被修改,否则说明信息被 修改过,因此数字签名能够验证信息的完整性。 数字签名是个加密的过程,数字签名验证是个解密的过程。 2.3.数字签名的流程 3.数字证书 3.1.概念 数字证书就是互联网通讯中标志通讯各方身份信息的一系列数据,提供了一种在Internet 上验 证您身份的方式,其作用类似于司机的驾驶执照或日常生活中的身份证。它是由一个由权威机构---CA 机构,又称为证书授权(Certificate Authority)中心发行的,人们可以在网上用它来识别对方的 身份。数字证书是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。最 简单的证书包含一个公开密钥、名称以及证书授权中心的数字签名。 3.2.颁发过程 数字证书颁发过程一般为:用户首先产生自己的密钥对,并将公共密钥及部分个人身份信息传 送给认证中心。 认证中心在核实身份后,将执行一些必要的步骤,以确信请求确实由用户发送而来, 然后,认证中心将发给用户一个数字证书,该证书内包含用户的个人信息和他的公钥信息,同时还附 有认证中心的签名信息。用户就可以使用自己的数字证书进行相关的各种活动。数字证书由独立的证 书发行机构发布。数字证书各不相同,每种证书可提供不同级别的可信度。可以从证书发行机构获得 您自己的数字证书。 3.3.证书格式 目前数字证书的格式普遍采用的是 X.509 V3国际标准,内容包括证书序列号、证书持有者名称、 证书颁发者名称、证书有效期、公钥、证书颁发者的数字签名等。 数字证书主要由以下两部分组成 : 1、证书数据,包括以下内容 : * 版本信息,用来与 X.509 的将来版本兼容; * 证书序列号,每一个由 CA 发行的证书必须有一个惟一的序列号; * CA 所使用的签名算法; * 发行证书 CA 的名称; * 证书的有效期限; * 证书主题名称; * 被证明的公钥信息,包括公钥算法、公钥的位字符串表示; * 包含额外信息的特别扩展。 2、发行证书的 CA 的数字签名 4.CA 认证中心 4.1.概念 CA 中心又称 CA 机构,即证书授权中心(Certificate Authority ),或称证书授权机构,作为电 子商务交易中受信任的第三方,承担公钥体系中公钥的合法性检验的责任。CA 中心为每个使用公开 密钥的用户发放一个数字证书,数字证书的作用是证明证书中列出的用户合法拥有证书中列出的公开 密钥。CA 机构的数字签名使得攻击者不能伪造和篡改证书。在 SET 交易中,CA 不仅对持卡人、商户 发放证书,还要对获款的银行、网关发放证书。它负责产生、分配并管理所有参与网上交易的个体所 需的数字证书,因此是安全电子交易的核心环节。 4.2.根证书 根证书是 CA 认证中心给自己颁发的证书,是信任链的起始点。安装根证书意味着对这个CA 认证 中心的信任。 从技术上讲,证书其实包含三部分,用户的信息,用户的公钥,还有 CA 中心对该证书里面的信 息的签名,要验证一份证书的真伪(即验证 CA 中心对该证书信息的签名是否有效),需要用 CA 中 心的公钥验证,而 CA 中心的公钥存在于对这份证书进行签名的证书内,故需要下载该证书,但使用 该证书验证又需先验证该证书本身的真伪,故又要用签发该证书的证书来验证,这样一来就构成一条 证书链的关系,这条证书链在哪里终结呢?答案就是根证书,根证书是一份特殊的证书,它的签发者 是它本身,下载根证书就表明您对该根证书以下所签发的证书都表示信任,而技术上则是建立起一个 验证证书信息的链条,证书的验证追溯至根证书即为结束。所以说用户在使用自己的数字证书之前必 须先下载根证书。 5.HTTPS 5.1.概念 HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标 的 HTTP 通道,简单讲是 HTTP 的安全版。即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,因此加密 的详细内容就需要 SSL。它是一个 URI scheme(抽象标识符体系),句法类同 http:体系。用于安全 的 HTTP 数据传输。https:URL表明它使用了 HTTP,但HTTPS 存在不同于 HTTP 的默认端口及一个加密 /身份验证层(在 HTTP 与 TCP 之间)。这个系统的最初研发由网景公司进行,提供了身份验证与加密 通讯方法,现在它被广泛用于万维网上安全敏感的通讯,例如交易支付方面。 它的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种 就是确认网站的真实性。 5.2.HTTPS 和 HTTP 的区别 1、https 协议需要到 ca 申请证书,一般免费证书很少,需要交费。 2、http 是超文本传输协议,信息是明文传输,https 则是具有安全性的 ssl 加密传输协议。 3、http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。 4、http 的连接很简单,是无状态的,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认 证的网络协议 要比 http 协议安全。 5.3.HTTPS 解决的问题 1、信任主机的问题. 2、通讯过程中的数据的泄密和被窜改 6.SSL 6.1.概念 SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security, TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS 与 SSL 在传输层对网络连接进行加 密。 为 Netscape 所研发,用以保障在 Internet 上数据传输之安全,利用数据加密(Encryption)技术,可 确保数据在网络上之传输过程中不会被截取及窃 听。 当前版本为 3.0。它已被广泛地用于 Web 浏览器与服务器之间的身份认证和加密数据传输。 SSL 协议位于 TCP/IP 协议与各种应用层协议之间,为数据通讯提供安全支持。SSL 协议可 分为 两层: SSL 记录协议(SSL Record Protocol):它建立在可靠的传输协议(如 TCP)之上,为高层 协议提供数据封装、压缩、加密等基本功能的支持。 SSL 握手协议(SSL Handshake Protocol): 它建立在 SSL 记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、 交换加密密钥等。 6.2.SSL 协议提供的服务主要有 1)认证用户和服务器,确保数据发送到正确的客户机和服务器; 2)加密数据以防止数据中途被窃取; 3)维护数据的完整性,确保数据在传输过程中不被改变。 6.3.SSL 协议的工作流程 服务器认证阶段:1)客户端向服务器发送一个开始信息“Hello”以便开始一个新的会话连接; 2)服务器根据客户的信息确定是否需要生成新的主密钥,如需要则服务器在响应客户的“Hello”信 息时将包含生成主密钥所需的信息;3)客户根据收到的服务器响应信息,产生一个主密钥,并用服 务器的公开密钥加密后传给服务器;4)服务器恢复该主密钥,并返回给客户一个用主密钥认证的信 息,以此让客户认证服务器。 用户认证阶段:在此之前,服务器已经通过了客户认证,这一阶段主要完成对客户的认证。经认证的 服务器发送一个提问给客户,客户则返回(数字)签名后的提问和其公开密钥,从而向服务器提供认 证。 从 SSL 协议所提供的服务及其工作流程可以看出,SSL 协议运行的基础是商家对消费者信息保 密的承诺,这就有利于商家而不利于消费者。 7.PKI 7.1.概念 PKI(Public Key Infrastructure)即“公钥基础设施”,是一种遵循既定标准的密钥管理平台, 它能够为所有网络应用提供加密和数字签名等密码服务及所必需的密钥和证书管理体系,简单来说, PKI 就是利用公钥理论和技术建立的提供安全服务的基础设施。当然,没有好的非对称算法和好的密 钥管理就不可能提供完善的安全服务,也就不能叫做 PKI。也就是说,该定义中已经隐含了必须具有 的密钥管理功能。PKI 技术是信息安全技术的核心,也是电子商务的关键和基础技术。 PKI 的基础技术包括加密、数字签名、数据完整性机制、数字信封、双重数字签名等。 它由公开密钥密码技术、数字证书、证书发放机构(CA)和关于公开密钥的安全策略等基本成分共同 组成的。PKI 是利用公钥技术实现电子商务安全的一种体系,是一种基础设施,网络通讯、网上交易 是利用它来保证安全的。 8.密码体制 密码体制分为私用密钥加密技术(对称加密)和公开密钥加密技术(非对称加密)。 8.1.对称加密算法(对称密钥) 对称密钥加密又叫专用密钥加密,即发送和接收数据的双方必使用相同的密钥对明文进行加密和 解密运算。对称密钥加密算法主要包括:DES、3DES、IDEA、FEAL、BLOWFISH 等。 8.2.非对称加密算法(公开密钥加密、公钥加密) 非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥 与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应 的私有密钥才能解密;如果用私 有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的 密钥,所以这种算法叫作非对称 加密算法。 非对称加密算法实现机密信息交换的基本过程是:甲方 生成一对密钥并将其中的一把作为公用密钥向其它方公开;得到该公用密钥的乙方使用该密钥对机密 信息进行 加密后再发送给甲方;甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。 算法:RSA、Elgamal、背包算法、Rabin、HD,ECC(椭圆曲线加密算法)。使用最广泛的是 RSA 算法, Elgamal 是另一种常用的非对称加密算法。 9.X.509 9.1.简介 数字证书的格式遵循 X.509 标准。X.509 是由国际电信联盟(ITU-T)制定的数字证书标准。 X.509 被广泛使用的数字证书标准,是由国际电联电信委员会(ITU-T)为单点登录(SSO-Single Sing-on)和授权管理基础设施(PMI-Privilege Management Infrastructure)制定的PKI 标准。X.509 定义了(但不仅限于)公钥证书、证书吊销清单、属性证书和证书路径验证算法等证书标准。 9.2.证书扩展文件名 .cer, .crt - 通常被用于二进制的 DER 文件格式 (同于.der), 不过也被用于 Base64 编码的文 件 (例如 .pem). .P7B - 同于 .p7c .P7C - PKCS#7 证书格式,仅仅包含证书和 CRL 列表信息,没有私钥。 .PFX - 同于 .p12 .P12 - PKCS#12 文件, 包含证书(公钥)和私钥(受密码保护),已经完整的证书链信。 PKCS#7 是一种将数据加密和签名(正式名称是“enveloping”)的技术标准。 它描述数字证书 的语法和其他加密消息——尤其是,数据加密和数字签名的方法,也包含了算法。但 PKCS#7 不包含 私钥信息。 PKCS#12 定义了一个用于保存私钥和对应公钥证书的文件格式,并由对称密钥加密保护。PKCS#12 通常采用 PFX,P12 作为文件扩展名。 PKCS#12 文件可以存放多个证书,并由密码保护,通常用于 WINDOWS IIS,也能够被当作Java Keysotre文件使用,用于 Tomcat,Resign,Weblogic,Jboss 等, 不能被用于 Apache。 10.证书管理工具 10.1.keytool 工具 keytool 是 Java 中的密钥和数字证书管理工具。它使用户能够管理自己的公钥/私钥对及相关 证书,用于(通过数字签名)自我认证(用户向别的用户/服务认证自己)或数据完整性以及认证服 务。它还允许用户储存他们的通信对等者的公钥(以证书形式)。 keytool 将密钥和证书储存在一个所谓的密钥仓库中。缺省的密钥仓库实现将密钥仓库实现为 一个文件。它用口令来保护私钥。 keytool 用于数字证书的申请、导入、导出和撤销等操作。 参考资料 : java keytool 使用总结:http://yushan.javaeye.com/blog/434955 // 删除 keytool -delete -alias tomcatsso -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit keytool -delete -alias tomcatsso -storepass changeit // 删除 keytool -delete -alias root -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit // 生成私钥 keytool -genkey -keyalg RSA -alias root -dname "cn=mycas.cn" -storepass changeit -keystore root.keystore // 导出证书 keytool -export -alias root -file root.crt -storepass changeit -keystore root.keystore // 导入证书 keytool -import -alias root -file root.crt -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit // 查看 keytool -list -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit 更多参数 ,请在命令行下执行:keytool -help 10.2.openssl 工具 OpenSSL(http://www.openssl.org/)是一个开放源代码软件包,由 Eric A. Young 和 Tim J.Hudson 等人编写,实现了 SSL 及相关加密技术,是最常用的证书管理工具。OpenSSL 功能远胜于 KeyTool,可用于根证书、服务器证书和客户证书的管理。 10.2.1 .openssl 使用说明 (Windows32,笔者下载使用:Win32OpenSSL_Light-1_0_0a.exe) 1. 环境变量 设置系统变量 OpenSSL_Home,并将其指向 OpenSSL 的安装目录(C:\OpenSSL)。 OpenSSL_Home=C:\OpenSSL 同时,将其执行目录(%OpenSSL_Home%\bin;)加入系统变量 Path 中。 Path=%OpenSSL_Home%/bin;%MAVEN_HOME%/bin;%FLEX_HOME%/bin 2. 工作目录 打开 OpenSSL 配置文件 openssl.cfg(%OpenSSL_Home%\bin\openssl.cfg),找到配置[CA_default] 注意变量 dir,它指向的是 CA 工作目录,本文将路径 D:/ca 作为 CA 工作目录,对变量 dir 做相应修 改。对于其他变量,我们无需修改。 建立 CA 工作目录后,我们需要构建一些子目录,用于存放证书、密钥等。 1)构建 CA 子目录 echo 构建已发行证书存放目录 certs mkdir certs echo 构建新证书存放目录 newcerts mkdir newcerts echo 构建私钥存放目录 private mkdir private echo 构建证书吊销列表存放目录 crl mkdir crl 2)构建相关文件 echo 构建索引文件 index.txt echo 0>index.txt echo 构建序列号文件 serial echo 01>serial 3. 构建根证书 // 在构建根证书前,需要构建随机数文件(.rand) openssl rand -out private/.rand 1000 // 构建根证书私钥(ca.key.pem),这时我们需要输入根证书密码“123456” openssl genrsa -aes256 -out private/ca.key.pem 2048 // 生成根证书签发申请文件(ca.csr),这时我们需要输入根证书密码“123456” openssl req -new -key private/ca.key.pem -out private/ca.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=*.zlex.org" // 得到根证书签发申请文件后,我们可以将其发送给 CA 机构签发。当然,我们也可以自行签发根证 书。 // 签发根证书,这时我们需要输入根证书密码“123456” openssl x509 -req -days 10000 -sha1 -extensions v3_ca -signkey private/ca.key.pem -in private/ca.csr -out certs/ca.cer // OpenSSL 产生的数字证书不能在 Java 语言环境中直接使用,需要将其转化为 PKCS#12 编码格式。 // 根证书转换,这时我们需要输入根证书密码“123456” openssl pkcs12 -export -cacerts -inkey private/ca.key.pem -in certs/ca.cer -out certs/ca.p12 // 个人信息交换文件(PKCS#12)可以作为密钥库或信任库使用,我们可以通过 KeyTool 查看该密钥 库的详细信息。 keytool -list -keystore certs/ca.p12 -storetype pkcs12 -v -storepass 123456 4. 构建服务器证书 // 服务器证书的构建与根证书构建相似,首先需要构建服务器私钥,这时我们需要输入服务器证书 密码“123456” openssl genrsa -aes256 -out private/server.key.pem 2048 // 完成服务器证书密钥构建后,我们需要产生服务器证书签发申请,这时我们需要输入服务器证书 密码“123456” openssl req -new -key private/server.key.pem -out private/server.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=www.zlex.org" // 我们已经获得了根证书,可以使用根证书签发服务器证书 openssl x509 -req -days 3650 -sha1 -extensions v3_req -CA certs/ca.cer -CAkey private/ca.key.pem -CAserial ca.srl -CAcreateserial -in private/server.csr -out certs/server.cer // 将 OpenSSL 产生的数字证书转化为 PKCS#12 编码格式,这时我们需要输入服务器证书密码“123456” openssl pkcs12 -export -clcerts -inkey private/server.key.pem -in certs/server.cer -out certs/server.p12 现在,我们已经构建了服务器证书(server.cer),并可使用该证书构建基于单向认证的网络交互平 台。 5. 构建客户证书 // 客户证书的构建与服务器证书构建基本一致,首先需要构建私钥 // 产生客户私钥,这时我们需要输入客户证书密码“123456” openssl genrsa -aes256 -out private/client.key.pem 2048 // 生成客户证书签发申请,这时我们需要输入客户证书密码“123456” openssl req -new -key private/client.key.pem -out private/client.csr -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=zlex" // 签发客户证书。如果在第 2 点配置有问题,有可能报错: I am unable to access the ./demoCA/newcerts directory ./demoCA/newcerts: No such file or directory openssl ca -days 3650 -in private/client.csr -out certs/client.cer -cert certs/ca.cer -keyfile private/ca.key.pem // 客户证书转换 openssl pkcs12 -export -inkey private/client.key.pem -in certs/client.cer -out certs/client.p12 CAS单点登录系列(2)-初步认识CAS 1.CAS 介绍 CAS(Central Authentication Service,即中央认证服务),是 Yale 大学发起的一个开源单点登录项目,旨在为 应用系统提供一种可靠的单点登录方法。于 2004 年 12 月正式成为 JA-SIG 的一个项目。 2.CAS 特点  开源的企业级单点登录解决方案  CAS 被设计成一个独立的 Web 应用程序(cas.war)  CAS Client 支持非常多的客户端(这里指单点登录系统中的各个 Web 应用),包括 Java、.Net、PHP、Perl、Apache、 uPortal 等  能够与 uPortal, BlueSocket, TikiWiki, Mule, Liferay, Moodle 等集成使用。 3.CAS 原理和协议 从结构上看,CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server 需要独立部署, 主要负责对用户的认证工作;CAS Client 负责处理对客户端受保护资源的访问请求,需要登录时, 重定向到 CAS Server。 图 1 是 CAS 最基本的协议过程: CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问 受保护资源的每个 Web 请求,CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket, 如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户在第 3 步中输入 认证信息,如果登录成功,CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket, 并缓存以待将来验证,之后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新产生的 Ticket 过后,在第 5, 6 步中与 CAS Server 进行身份合适,以确保 Service Ticket 的合法性。 在该协议中,所有与 CAS 的交互均采用 SSL 协议,确保,ST 和 TGC 的安全性。协议工作过程中会有 2 次重定 向的过程,但是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的。 另外,CAS 协议中还提供了 Proxy (代理)模式,以适应更加高级、复杂的应用场景,具体介绍可以参考 CAS 官方网 站上的相关文档。 以上大部分内容摘自 :http://www.ibm.com/developerworks/cn/opensource/os-cn-cas/index.html 4.准备工作 JA-SIG官方网站:http://www.jasig.org/cas CAS Server下载地址:http://www.jasig.org/cas/download CAS Client下载地址:http://downloads.jasig.org/cas-clients/ 官方CAS Client for Java 3.1 文档:https://wiki.jasig.org/display/CASC/CAS+Client+for+Java+3.1 Tomcat 6 下载地址:http://archive.apache.org/dist/tomcat/tomcat-6/ 在此,笔者分别使用 cas-server-3.3.3、cas-client-3.1.10(支持单点登出)和 Tomcat 6.0.18。 5.部署 CAS Server 解压 cas-server-3.3.3 包,将 modules/cas-server-webapp-3.3.3.war 复制到 Tomcat 的 webapps 目录下,并重 命名为 cas3.war,启动 Tomcat。启动完毕后,访问地址:http://localhost:8080/cas3/login,我们将看到中央认证服 务中用于收集用户凭证的入口界面: 默认时,只要用户输入相同的用户名和密码,就可以登录到 CAS 服务器中。比如输入用户名/密码:admin/admin, 单击“登录”后将成功进入以下成功提示界面: 当成功登录到 CAS 服务器后,浏览器会存储 CAS 服务器返回的一个 Cookie,其名字默认为 “CASTGC”。当目标 Web 应用实施了 CAS SSO 之后,它们便需要借助于这一 Cookie 实现 Web SSO 会 话。由此建立了用户与 CAS 服务器之间的信任关系。 此时,如果用户需要退出,可以通过 http://localhost:8080/cas3/logout 退出 CAS 服务器。退出操作将导致浏 览器中存在的 CASTGC Cookie 被销毁,即销毁 CAS 与当前用户已建立的信任关系(Web SSO 会话)。事实上,由于我们使 用的是 HTTP 协议操控 logout,因此浏览器中的 CASTGC Cookie 并未被销毁。如果借助于 HTTPS 操控 logout,则浏览器 中的 CASTGC Cookie 便会被销毁掉。在未启用 HTTPS 时,如果使用 IE,可以通过关闭浏览器以达到销毁 CASTGC Cookie 的目的。 CAS单点登录系列(3)-简单实施SSO 章分类:Java编程 默认时,为了启用 Web SSO,开发者必须开启 HTTPS 传输通道。由于传回 CASTGC Cookie 到 CAS 服务器需要走 HTTPS 通道,因此开发者必须准备好 X.509 CA 证书。当然,您也可以选择修改这一默认行为,但从安全性的角度考虑,不推荐 这样做。 1.使用 keytool 生成根证书 1.1.查看 jre 信任的证书 (1)查看 jre 中所有信任的证书信息 keytool -list -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit (2)查看别名为 root 的证书信息 keytool -v -list -alias root -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit 1.2.删除 jre 中别名为 root 的证书 keytool -delete -alias root -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit 1.3.开始生成根证书 确认 jre 中不存在别名为 root 的证书之后,我们开始生成根证书。 (1)生成密钥库文件 root.keystore keytool -genkey -keyalg RSA -alias root -dname " CN=localhost, OU=javaeeOU, O=javaee, L=GuangZhou, ST=GuangDong, C=CN" -storepass changeit -keystore root.keystore 使用默认密码,直接回车。 (2)导出别名为 root 的证书,证书名 root.crt keytool -export -alias root -file root.crt -storepass changeit -keystore root.keystore (3)将证书导入到 jre 信任证书库中 keytool -import -alias root -file root.crt -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit (4)查看别名为 root 的证书信息,确认成功导入到 jre 信任证书库中 keytool -v -list -alias root -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit 2.启用 HTTPS 2.1.在 Tomcat 中启用 HTTPS 配置 打开 Tomcat 目录下的 conf/server.xml 文件,添加 Connector 代码如下: Html 代码 1. Html 代码 1. 其中: keystoreFile 指向密钥库文件 root.keystore keystorePass 默认为 changeit truststoreFile 指向 jre 信任的证书库文件 2.2.安装证书 启动 Tomcat,访问地址:https://localhost:8443/cas3,浏览器将会返回证书错误界面: 单击“继续浏览此网站(不推荐)”链接,查看并将证书导入到浏览器中: 单击“查看证书”,弹出证书安装界面: 单击“安装证书”,进入证书导入向导界面: 选择“受信任的根证书颁发机构”,接下来只需要一路选择“下一步”或者“是”直至导入成功。 证书导入成功后,重新浏览器并访问地址:https://localhost:8443/cas3,将会看到类似于下图的锁图标,并可单击该 图标查询证书信息,这表示证书已安装成功,正确启用了 HTTPS。 3.对 Web 应用实施 SSO Web 应用采用 Tomcat 自带的 servlet 例子 Hello World,默认访问路径为 http://localhost:8080/examples/servlets/servlet/HelloWorldExample。在没有对该 servlet 实施 SSO 之前,启动 Tomcat 后访问结果如下图所示: 接下来,我们将对该 servlet 实施 SSO,配置步骤如下: 3.1.添加 Cas Client 相应 Jar 包 只需要添加两个 Jar 包:cas-client-core-3.1.10.jar、commons-logging-1.0.4.jar,将其拷贝到 Tomcat 安装目 录下的 webapps\examples\WEB-INF\lib 目录下。 3.2.配置 web.xml 文件 对于 Cas Client 在 web.xml 文件中的配置,主要包括两个方面,一个是过滤器的配置,另一个是监听器的配置。 3.2.1.过滤器配置 主要有以下 5 个过滤器: 1.SingleSignOutFilter 2.AuthenticationFilter 3.TicketValidationFilter (whichever one is chosen) 4.HttpServletRequestWrapperFilter 5.AssertionThreadLocalFilter 对于这 5 个过滤器,在 web.xml 中的配置必须严格按照以上顺序进行声明。 其中,AuthenticationFilter、TicketValidationFilter 是必须配置的,其他 3 个过滤器是可选的。特别的,如果 配置了 SingleSignOutFilter,必须配合 SingleSignOutHttpSessionListener 这一监听器进行使用。 3.2.1.1.SingleSignOutFilter 该过滤器用于实现单点登出功能,可选配置。配置如下: Xml 代码 1. 2. CAS Single Sign Out Filter 3. 4. org.jasig.cas.client.session.SingleSignOutFilter 5. 6. 7. 8. CAS Single Sign Out Filter 9. /servlets/servlet/HelloWorldExample 10. Xml 代码 1. 2. CAS Single Sign Out Filter 3. 4. org.jasig.cas.client.session.SingleSignOutFilter 5. 6. 7. 8. CAS Single Sign Out Filter 9. /servlets/servlet/HelloWorldExample 10. 3.2.1.2.AuthenticationFilter 该过滤器负责用户的认证工作,必须启用它。配置如下: Xml 代码 1. 2. CAS Authentication Filter 3. 4. org.jasig.cas.client.authentication.AuthenticationFilter 5. 6. 7. casServerLoginUrl 8. https://localhost:8443/cas3/login 9. 10. 11. serverName 12. localhost:8080 13. 14. 15. 16. CAS Authentication Filter 17. /servlets/servlet/HelloWorldExample 18. Xml 代码 1. 2. CAS Authentication Filter 3. 4. org.jasig.cas.client.authentication.AuthenticationFilter 5. 6. 7. casServerLoginUrl 8. https://localhost:8443/cas3/login 9. 10. 11. serverName 12. localhost:8080 13. 14. 15. 16. CAS Authentication Filter 17. /servlets/servlet/HelloWorldExample 18. 3.2.1.3.TicketValidationFilter 该过滤器负责对 Ticket 的校验工作,必须启用它。配置如下: Xml 代码 1. 2. CAS Validation Filter 3. 4. org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter 5. 6. 7. casServerUrlPrefix 8. https://localhost:8443/cas3 9. 10. 11. serverName 12. localhost:8080 13. 14. 15. 16. CAS Validation Filter 17. /servlets/servlet/HelloWorldExample 18. Xml 代码 1. 2. CAS Validation Filter 3. 4. org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter 5. 6. 7. casServerUrlPrefix 8. https://localhost:8443/cas3 9. 10. 11. serverName 12. localhost:8080 13. 14. 15. 16. CAS Validation Filter 17. /servlets/servlet/HelloWorldExample 18. 3.2.1.4.HttpServletRequestWrapperFilter 该过滤器负责实现 HttpServletRequest 请求的包裹,比如允许开发者通过 HttpServletRequest 的 getRemoteUser()方法 获得 SSO 登录用户的登录名,可选配置。配置如下: Xml 代码 1. 2. CAS HttpServletRequest Wrapper Filter 3. 4. org.jasig.cas.client.util.HttpServletRequestWrapperFilter 5. 6. 7. 8. CAS HttpServletRequest Wrapper Filter 9. /servlets/servlet/HelloWorldExample 10. Xml 代码 1. 2. CAS HttpServletRequest Wrapper Filter 3. 4. org.jasig.cas.client.util.HttpServletRequestWrapperFilter 5. 6. 7. 8. CAS HttpServletRequest Wrapper Filter 9. /servlets/servlet/HelloWorldExample 10. 3.2.1.5.AssertionThreadLocalFilter 该过滤器使得开发者可以通过 org.jasig.cas.client.util.AssertionHolder 来获取用户的登录名。比如 AssertionHolder.getAssertion().getPrincipal().getName()。配置如下: Xml 代码 1. 2. CAS Assertion Thread Local Filter 3. 4. org.jasig.cas.client.util.AssertionThreadLocalFilter 5. 6. 7. 8. CAS Assertion Thread Local Filter 9. /servlets/servlet/HelloWorldExample 10. Xml 代码 1. 2. CAS Assertion Thread Local Filter 3. 4. org.jasig.cas.client.util.AssertionThreadLocalFilter 5. 6. 7. 8. CAS Assertion Thread Local Filter 9. /servlets/servlet/HelloWorldExample 10. 3.2.2.监听器配置 当配置了 SingleSignOutFilter 过滤器,即对 Web 应用实施单点登出功能时,需要在 web.xml 中添加以下监听器: Xml 代码 1. 2. 3. org.jasig.cas.client.session.SingleSignOutHttpSessionListener 4. 5. Xml 代码 1. 2. 3. org.jasig.cas.client.session.SingleSignOutHttpSessionListener 4. 5. 3.2.3.启动应用 配置完成后,重新启动 Tomcat,并访问地址 http://localhost:8080/examples/servlets/servlet/HelloWorldExample,由于该 servlet 已受到 Cas 的保护, 所以我们会被引导到 Cas 登录页面。 只有通过 Cas 认证,才能真正的访问到 http://localhost:8080/examples/servlets/servlet/HelloWorldExample 这个 servlet。 o filter和listener代码.zip (538 Bytes) CAS单点登录系列(4)-使用RDBMS认证 在实际应用中,用户认证信息一般会存放在 RDBMS 或 LDAP 中,这样能够更好的保证系统的安全性。本节将采用 RDBMS 的 存储方式,通过一个简单的实例展示其使用方法。 1.准备环境 操作系统:32 位 Window XP CAS 服务器:Cas Server 3.3.3 CAS 客户端:Cas Client 3.1.10 Web 服务器:Tomcat 6.0.18 数据库:MySQL 5.0 2.准备数据库 2.1.创建数据库 create database cas CHARSET=utf8; 2.2.创建用户表 CREATE TABLE `users` ( `id` int(11) NOT NULL auto_increment, `username` varchar(50) NOT NULL, `password` varchar(50) NOT NULL, `sex` varchar(2) NOT NULL, `age` int(4) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 2.3.准备数据 INSERT INTO `users` VALUES ('1', 'admin', '21232f297a57a5a743894a0e4a801fc3', '男', '26'); INSERT INTO `users` VALUES ('2', 'javaee', '21232f297a57a5a743894a0e4a801fc3', '男', '26'); 密码均为 admin 。 3.添加数据库信息 打开 cas3 的 WEB-INF/cas.properties 文件,添加数据库相关信息: jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/cas?useUnicode=true&characterEncoding=utf-8&autoReconnect=t rue jdbc.username=root jdbc.password=root cas.auth.sql=select u.password from users u where lower(u.username) = lower(?) 其中,url、username、password 根据实际情况进行修改。 4.添加 Jar 包 MySQL 数据库 JDBC 支持包:mysql-connector-java-5.1.9.jar DBCP 连接池支持包:commons-pool-1.2.jar、commons-dbcp- 1.2.1.jar QueryDatabaseAuthenticationHandler 使用 Spring JDBC 支持包:spring-jdbc-2.5.6.jar JDBC 认证处理器实现支持:cas-server-support-jdbc-3.3.3.jar 5.配置数据库认证 5.1.认证管理器 在讲解如何使用数据库认证之前,让我们先来了解一下 AuthenticationManagerImpl 对象 authenticationManager。authenticationManager 对象用于认证用户信息,在用户登录 CAS 时,将 会使用该认证管理器进行认证。 AuthenticationManagerImpl 实现了 AuthenticationManager 接口,接口定义如下: public interface AuthenticationManager { String AUTHENTICATION_METHOD_ATTRIBUTE = "authenticationMethod"; Authentication authenticate(final Credentials credentials) throws AuthenticationException; } AuthenticationManagerImpl 借助于 AuthenticationHandler 集合 List和 CredentialsToPrincipalResolver 集合 List完成对用户的认证工作。AuthenticationHandler 负责完成 用户的认证工作,而 CredentialsToPrincipalResolver 负责构建认证结果。 对于配置了多个 AuthenticationHandler 的实现,一旦其中一个认证处理器认证通过,其他认 证处理器将不再执行。配置了多个 CredentialsToPrincipalResolver 的处理方式与 AuthenticationHandler 一致。 至此,我们认识到如果要实现 RDBMS 认证,必须提供相应的 AuthenticationHandler 实现方式。那么具体需要如 何来实现,CAS 为我们提供了基本的 JDBC 支持包 cas-server-support-jdbc-3.3.3.jar。在 CAS 中引入该包后,使用其 实现类 org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler 将满足我们的需要。接下来,我们开始进 行配置: 5.2.修改配置文件 deployerConfigContext.xml 打开 cas3 的 WEB-INF/deployerConfigContext.xml 文件 5.2.1.添加 dbcp 数据源配置 Xml 代码 1. 2. 3. 4. 5. 6. Xml 代码 1. 2. 3. 4. 5. 6. 5.2.2.添加 MD5 加密编码器 Xml 代码 1. 2. 3. Xml 代码 1. 2. 3. 5.2.3.添加 RDBMS 认证处理器 替换 authenticationHandlers 中的 SimpleTestUsernamePasswordAuthenticationHandler 认证处理器,代码如下: Xml 代码 1. 2. 3. 4. 5. Xml 代码 1. 2. 3. 4. 5. 5.2.4.验证配置 经过以上几个步骤,对于数据库认证的配置已经完成。接下来,我们启动 Tomcat 进行测试。启 动完毕后,使用 admin 或 javaee 用户登录 CAS 认证服务器,如果登录成功,我们将会在 Tomcat 日志 中看到类似信息: 从日志信息中可以看到,我们的确成功的借助于 QueryDatabaseAuthenticationHandler 对用户进行了认证。 CAS单点登录系列(5)-简单实施SSO之二 在本系列文章的第 3 篇中,讲解了使用 CAS 实施单点登录的简单过程。 首先,CAS 客户端的配置使用最简单的配置方式,全部配置都放在 web.xml 文件中。虽然这种配 置方式很明了,但存在几个缺点。这种配置方式不仅使 web.xml 文件显得臃肿,而且相关属性的配置 不够灵活,不易于管理。 其次,采用了 keytool 生成证书。尽管直接借助于 keytool 能够获得自签名的 X.509 证书,但 keytool 工具不支持签署其他证书。因此,在需要签署其他证书的时候,必须使用其他证书管理工具。 鉴于以上问题,本节将采用 Spring 过滤链的配置方式配置 CAS Client,将臃肿的配置信息从 web.xml 中抽取出来,简化 web.xml 文件。使用 OpenSSL 管理证书,以弥补 keyool 的种种不足。 1.准备环境 操作系统:32 位 Window XP CAS 服务器:Cas Server 3.3.3 CAS 客户端:Cas Client 3.1.10 Web 服务器:Tomcat 6.0.18 数据库:MySQL 5.0 JDK:jdk1.5.0_10 2.基础环境准备 JDK 安装后,复制两份 jdk1.5.0_10,分别命名为 jdk_cas 和 jdk_client 。 同样,复制两份 Tomcat 6.0.18,分别命名为 apache-tomcat-cas 和 apache-tomcat-client 。 配置 Tomcat 对应的 JDK。打开 apache-tomcat-cas/bin/catalina.bat 文件,在 rem Guess CATALINA_HOME if not defined这行之前添加:set JAVA_HOME=您的安装路径/jdk_cas ,然后保存。 同样方式配置 apache-tomcat-client ,这样启动 apache-tomcat-cas 将使用 jdk_cas,启动 apache-tomcat-client 将使用 jdk_client。 3.生成证书 OpenSSL 工具的安装配置请参考本系列文章的第 1 篇,接下来将生成根证书和服务器证书。 创建三个目录 C:/openssl、C:/openssl/root、C:/openssl/server。然后在命令行下进入 C:/openssl 目录。 (注:整个过程中涉及到密码的地方均使用 password ) 3.1.创建根证书 //创建私钥 openssl genrsa -out root/root-key.pem 1024 //创建证书请求 openssl req -new -out root/root-req.csr -key root/root-key.pem //证书信息,按顺序输入,最后一个选项 An optional company name 直接回车 CN GuangDong GuangZhou JavaEECas Programmer root root@163.com password //自签署根证书 openssl x509 -req -in root/root-req.csr -out root/root-cert.pem -signkey root/root-key.pem -days 3650 //将根证书导出成浏览器支持的.p12(PKCS12)格式 openssl pkcs12 -export -clcerts -in root/root-cert.pem -inkey root/root-key.pem -out root/root.p12 3.2.创建服务器证书 //创建私钥 openssl genrsa -out server/server-key.pem 1024 //创建证书请求 openssl req -new -out server/server-req.csr -key server/server-key.pem //证书信息 CN GuangDong GuangZhou JavaEECas Programmer localhost localhost@163.com password //签署服务器证书 openssl x509 -req -in server/server-req.csr -out server/server-cert.pem -signkey server/server-key.pem -CA root/root-cert.pem -CAkey root/root-key.pem -CAcreateserial -days 3650 //将客户证书导出成浏览器支持的.p12(PKCS12)格式 openssl pkcs12 -export -clcerts -in server/server-cert.pem -inkey server/server-key.pem -out server/server.p12 4.启用 HTTPS 4.1.在 apache-tomcat-cas 中启用 HTTPS 打开 apache-tomcat-cas/conf/server.xml,在下面添加以下 代码: Xml 代码 1. Xml 代码 1. 其中,keystoreFile 和 truststoreFile 根据实际情况进行修改。 4.2.导入证书 为了使客户端信任 CAS Server,我们必须在客户端导入根证书。操作步骤: 1、打开 Internet 选项->证书->受信任的根证书颁发机构->导入->下一步,选择 root.p12 文件 2、输入密码 3、 选择证书存储位置 接着“下一步”直到导入成功。 4、查看已导入的证书 4.3.测试 HTTPS 启动 Tomcat,访问 https://localhost:8443/,如果能够看到以下界面,地址栏右边出现锁图 标,则说明 HTTPS 启用成功。 5.使用RDBMS认证 我们将采用“小蚂蚁-CAS单点登录系列(4)-使用RDBMS认证 ”一节所使用的cas3 作为CAS认证服 务器,将其拷贝到apache-tomcat-cas/webapps目录下。同时,别忘了把mysql驱动拷贝到 cas3/WEB-INF/lib或apache-tomcat-cas/lib目录下。 重新启动Tomcat,如果测试使用数据库认证信息登录成功,说明CAS认证服务器配置完成。 6.配置客户端 创建Web应用client3,添加三个包cas-client-core-3.1.10.jar、commons-logging-1.0.4.jar、 spring-2.5.6.jar。 6.1.修改apache-tomcat-client端口 总共有三个端口需要修改,分别是默认的Server元素的port="8005"、Connector元素 port="8080"、Connector元素port="8009",笔者分别修改为 9005、9090、9009。 6.2.Web.xml配置 Xml 代码 1. 2. 6. 7. client3.1.10 8. 9. 10. contextConfigLocation 11. 12. /WEB-INF/classes/spring-appContext.xml 13. 14. 15. 16. 17. 18. encodingFilter 19. org.springframework.web.filter.CharacterEncodingFilter 20. 21. encoding 22. UTF-8 23. 24. 25. forceEncoding 26. true 27. 28. 29. 30. 31. CAS Single Sign Out Filter 32. org.jasig.cas.client.session.SingleSignOutFilter 33. 34. 35. 36. CAS Authentication Filter 37. org.springframework.web.filter.DelegatingFilterProxy 38. 39. targetBeanName 40. authenticationFilter 41. 42. 43. 44. 45. CAS Validation Filter 46. org.springframework.web.filter.DelegatingFilterProxy 47. 48. targetBeanName 49. ticketValidationFilter 50. 51. 52. 53. 54. CAS HttpServletRequestWrapperFilter 55. org.springframework.web.filter.DelegatingFilterProxy 56. 57. targetBeanName 58. casHttpServletRequestWrapperFilter 59. 60. 61. 62. 63. 64. CAS AssertionThreadLocalFilter 65. org.jasig.cas.client.util.AssertionThreadLocalFilter 66. 67. 68. 69. 70. encodingFilter 71. /* 72. 73. 74. 75. CAS Single Sign Out Filter 76. /* 77. 78. 79. 80. CAS Authentication Filter 81. /secure/* 82. 83. 84. 85. CAS Validation Filter 86. /secure/* 87. 88. 89. 90. CAS HttpServletRequestWrapperFilter 91. /secure/* 92. 93. 94. 95. CAS AssertionThreadLocalFilter 96. /secure/* 97. 98. 99. 100. org.springframework.web.context.ContextLoaderListener 101. 102. 103. 104. org.jasig.cas.client.session.SingleSignOutHttpSessionListener 105. 106. 107. Xml 代码 1. 2. 6. 7. client3.1.10 8. 9. 10. contextConfigLocation 11. 12. /WEB-INF/classes/spring-appContext.xml 13. 14. 15. 16. 17. 18. encodingFilter 19. org.springframework.web.filter.CharacterEncodingFilter 20. 21. encoding 22. UTF-8 23. 24. 25. forceEncoding 26. true 27. 28. 29. 30. 31. CAS Single Sign Out Filter 32. org.jasig.cas.client.session.SingleSignOutFilter 33. 34. 35. 36. CAS Authentication Filter 37. org.springframework.web.filter.DelegatingFilterProxy 38. 39. targetBeanName 40. authenticationFilter 41. 42. 43. 44. 45. CAS Validation Filter 46. org.springframework.web.filter.DelegatingFilterProxy 47. 48. targetBeanName 49. ticketValidationFilter 50. 51. 52. 53. 54. CAS HttpServletRequestWrapperFilter 55. org.springframework.web.filter.DelegatingFilterProxy 56. 57. targetBeanName 58. casHttpServletRequestWrapperFilter 59. 60. 61. 62. 63. 64. CAS AssertionThreadLocalFilter 65. org.jasig.cas.client.util.AssertionThreadLocalFilter 66. 67. 68. 69. 70. encodingFilter 71. /* 72. 73. 74. 75. CAS Single Sign Out Filter 76. /* 77. 78. 79. 80. CAS Authentication Filter 81. /secure/* 82. 83. 84. 85. CAS Validation Filter 86. /secure/* 87. 88. 89. 90. CAS HttpServletRequestWrapperFilter 91. /secure/* 92. 93. 94. 95. CAS AssertionThreadLocalFilter 96. /secure/* 97. 98. 99. 100. org.springframework.web.context.ContextLoaderListener 101. 102. 103. 104. org.jasig.cas.client.session.SingleSignOutHttpSessionListener 105. 106. 107. 6.3.添加资源文件 在类路径下新建文件 cas-client.properties,内容如下: cas.server.url=https://localhost:8443/cas3 cas.client.serverName=localhost:9090 第一行指定 CAS 服务器地址 ,第二行指定客户端服务名称,格式为:主机名:端口号 6.4.添加 Spring 配置文件 在类路径下新建文件 spring-appContext.xml,内容如下: Xml 代码 1. 2. 7. 8. 9. 10. 11. 12. 13. 14. 15. 22. 23. 24. 28. 29. 30. 31. 32. 33. 34. 35. 47. 48. 50. Xml 代码 1. 2. 7. 8. 9. 10. 11. 12. 13. 14. 15. 22. 23. 24. 28. 29. 30. 31. 32. 33. 34. 35. 47. 48. 50. 6.5.导入根证书 由于 CAS Server 启用了 HTTPS,要使 Web 客户端信任其身份,必须为 jdk_client 导入根证书。 keytool -import -v -trustcacerts -storepass changeit -alias root -file root/root-cert.pem -keystore E:/DevelopTools/JDK/jdk_client/jre/lib/security/cacerts 7.综合测试 7.1.启动 apache-tomcat-cas 7.2.启动 apache-tomcat-client 7.3.访问 client3 访问 http://localhost:9090/client3/secure/securedpage.jsp 页面,将被引导到 CAS 登录页 面。输入凭证信息,认证通过后将出现以下页面: 访问 http://localhost:9090/client3/secure/debug.jsp 页面,可以看到通过三种不同的方式 获取用户名。 在 http://localhost:9090/client3/secure/securedpage.jsp 页面中,单击“退出”链接将单 点退出系统。 8.可能遇到的问题 8.1 报错 : 'unable to find valid certification path to requested target' 原因 : Web 应用服务器端没有导入根证书 解决方法 : 在 jdk_client 导入根证书。 至此,本节内容结束,有遗漏或疏忽的地方请大家指出,谢谢!  client3.zip (2.6 MB)
还剩38页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 15 金币 [ 分享pdf获得金币 ] 3 人已下载

下载pdf

pdf贡献者

song5749

贡献于2012-02-21

下载需要 15 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf