CAS单点登录(SSO)

1.概念

CAS:Java (Spring Webflow/Spring Boot) 服务组件,可插拔身份验证支持(LDAP,Database,X.509,MFA),支持多种协议(CAS, SAML, OAuth, OpenID, OIDC),跨平台客户端支持(Java, .Net, PHP, Perl, Apache等),与uPortal,Liferay ,BlueSocket,Moodle, Google Apps等集

SSO:单点登录简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。域,跨域,Session跨域,Nginx Session共享:分别在session,nginx中已有

2.SSL/TLS/HTTPS之间的关系

SSL(Secure Sockets Layer 安全套接层),为Netscape所研发,用以保障在Internet上数据传输之安全,SSL一般是https的代名词,TLS(Transport Layer Security,传输层安全)是在SSL3.0的基础上发展的,继承了SSL的优点,都是用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之传输过程中不会被截取及窃听。SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。

SSL协议可分为两层:

SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。

SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。

3.HTTPS的数据的加密传输

 浏览器把协议版本号,支持的加密算法生成的随机数A发送给服务器,服务器接到客户端发送过来的加密算法后,查看自身支持的加密算法,确定双方后边使用哪种加密算法,然后把该算法和数字证书以及生成的随机数B发给客户端,客户端接到服务器发送过来的证数后,得到证书里的非对称加密的公钥D,然后又生成一个随机数C,把C和D加密后得到的一个字符串E发给服务器。服务器接到这个字符串E后,用自身的私钥解密得到C,这样服务器就集齐了 A,B,C,然后利用ABC生成对话加密密钥 , 因为客户端也得到了A,B,C 这样双方都能进行加密解密了,至于加密解密的算法就是前面双方交换加密方法时约定的加密算法。所以可以看出ABC中,只有C是极难被别人得到的,这样保证了安全性,同时这个过程前面用到了非对称加密,一旦加密密钥确定,后面其实用的都是对称加密了。

4.使用java的keytool制作证书

keytool -genkeypair -alias serverkey -keypass 111111 -storepass 111111 
    -dname "C=CN,ST=GD,L=SZ,O=vihoo,OU=dev,CN=vihoo.com" 
    -keyalg RSA -keysize 2048 -validity 3650 -keystore server.keystore

keytool用法  

-rfc" 表示以base64输出文件,否则以二进制输出。      

-startdate <startdate>          证书有效期开始日期/时间

-alias 实体别名(包括证书私钥)

-certreq            生成证书请求

-changealias        更改条目的别名

-dname 指定证书拥有者信息 例如: “CN=名字与姓氏,OU=组织单位名称,O=组织名称,L=城市或区域名称,ST=州或省份名称,C=单位的两字母国家代码”

-destalias <destalias>          目标别名

-ext <value>                    X.509 扩展

-exportcert         导出证书

-export 将别名指定的证书导出到文件 keytool -export -alias 需要导出的别名 -keystore 指定keystore -file 指定导出的证书位置及证书名称 -storepass 密码

-storetype <storetype>  密钥库类型

-genkeypair  生成密钥对

-genseckey  生成密钥

-genseckey          生成密钥

-gencert            根据证书请求生成证书

-keystore 指定密钥库的名称(产生的各类信息将不在.keystore文件中)

-keyalg 指定密钥的算法 (如 RSA DSA(如果不指定默认采用DSA))

-keysize 指定密钥长度

-keypass 指定别名条目的密码(私钥的密码)

-validity 指定创建的证书有效期多少天

-printcertreq       打印证书请求的内容

-printcrl           打印 CRL 文件的内容

-providername <providername>    提供方名称

-providerclass <providerclass>  提供方类名

-providerarg <arg>              提供方参数

-providerpath <pathlist>        提供方类路径

-protected                      通过受保护的机制的口令

-importcert         导入证书或证书链

-importkeystore     从其他密钥库导入一个或所有条目

-storepass 指定密钥库的密码(获取keystore信息所需的密码)

-list 显示密钥库中的证书信息 keytool -list -v -keystore 指定keystore -storepass 密码

-v 显示密钥库中的证书详细信息

-file 参数指定导出到文件的文件名

-delete 删除密钥库中某条目 keytool -delete -alias 指定需删除的别 -keystore 指定keystore -storepass 密码

-printcert 查看导出的证书信息 keytool -printcert -file yushan.crt

-keypasswd 修改密钥库中指定条目口令 keytool -keypasswd -alias 需修改的别名 -keypass 旧密码 -new 新密码 -storepass keystore密码 -keystore sage

-storepasswd 修改keystore口令 keytool -storepasswd -keystore e:yushan.keystore(需修改口令的keystore) -storepass 123456(原始密码) -new yushan(新密码)

-sigalg <sigalg>                签名算法名称

-import 将已签名数字证书导入密钥库 keytool -import -alias 指定导入条目的别名 -keystore 指定keystore -file 需导入的证书

5.keytool实例

生成keystore

keytool -genkeypair -alias tomcat -keyalg RSA -validity 3650 -keysize 1024 -keystore H:/ajava/caskey/test.keystore

注意

cas.test.com 必须与如下保持一致

否则会出如下错误

No name matching localhost found

导出证书,注意-alias tomcat  与上述的别名需要一致

keytool -keystore  H:/ajava/caskey/test.keystore   -export  -file  H:/ajava/caskey/test.cer -alias  tomcat  -storepass 123456

 可以查看详情

keytool -list -keystore  H:/ajava/caskey/test.keystore -storepass 123456  

 打印证书

keytool -printcert -file H:/ajava/caskey/test.cer   //可以添加-v

 

客户端导入证书(jdk导入证书,否则java不信任会出现错误)

keytool -import -keystore F:JAVAjdk1.8jrelibsecuritycacerts  -file  H:/ajava/caskey/cas.cer   -alias tomcat

注意:cacerts这个名字必须是cacerts,因为cacerts是java信任的证书库(启动时的jdk),否则会出现如下错误,且密码必须是changeit。否则会出现如下错误

生成后缀.p12的证书

keytool -genkeypair -alias mykey  -keyalg RSA  -storetype PKCS12  -validity 3650 -keysize 1024 -keystore H:/ajava/caskey/mykey.p12

 在tomcat中使用(服务端)

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
		       clientAuth="false" sslProtocol="TLS"
		       keystoreFile="H:/ajava/caskey/test.keystore" keystorePass="123456" />

这里没有采用客户端认证

备注:

keystoreFile:第一步创建key存放的位置

keystorePass:创建证书时的密码

truststoreFile:验证客户端证书的根证书

truststorePass:创建证书时的密码

假如客户端使用了验证时,需要在浏览器导入相应的证书

这里的例子没有采用客户端验证

6.使用cas实现单点登录

服务端

下载cas服务器    https://github.com/apereo/cas-overlay-template/tree/5.3

官方文档   https://github.com/apereo/cas

添加到Idea中,修改pom,添加如下依赖,重新打包

其中比较重要的配置如下

log4j2.xml

配置日志文件位置(这里log4j,log4j2,logback 都差不多类似)

       <Property name="cas.log.dir" >.</Property>   
        <!-- To see more CAS specific logging, adjust this property to info or debug or run server with -Dcas.log.leve=debug -->
        <Property name="cas.log.level" >warn</Property>

web-inf下的application.properties文件,假如不愿意更改原配置,那么可以在src/resources先新建application.properties,根据springboot的配置文件加载原则,会优先加载src/resources下的application.properties,密码啥的是有效的,但是application.yml,application.properties中配置的tomcat相关的配置,打成war包部署在独立的tomcat上之后, 会失效,原因是因为spring boot内置的tomcat,也就是说如下配置只对springboot启动有效,这里采用的是tomcat(所以如下都注释了),上面已经配置了,tomcat启动可以采用上述介绍的方法(重新打war包)修改配置,也可以解压后修改配置,解压后需要删除war,否则每次启动都会先解压。

#页面
#server.context-path=/cas
#端口
#server.port=8443
#证书的位置 .keystore文件的位置
#server.ssl.key-store=H:/ajava/caskey/test.keystore
#指定密钥库的密码 -storepass
server.ssl.key-store-password=123456
#别名条目的密码-keypass
#server.ssl.key-password=123456
#用户名与密码
#cas.authn.accept.users=casuser::Mellon

开启服务端,密码cas.authn.accept.users=casuser::Mellon

    

不使用ssl(https)那么需要在application.properties文件中添加,文件位置WEB-INFclassesapplication.properties

#关闭https验证
cas.tgc.secure=false

CAS 未认证授权服务需要在application.properties文件中添加

#开启识别json文件,默认false
cas.serviceRegistry.initFromJson=true

在application.properties文件中添加cas.serviceRegistry.initFromJson=true实际是扫描json文件,serviceId表示允许匹配的地址,文件位置WEB-INFclassesservicesHTTPSandIMAPS-10000001.json

{
  //该文件可以是多个,但是该文件名需要满足如下name+id+.json
  //加载的类,这里必须使用这个类
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  //能匹配的url,能够匹配多个,也可以使用正则,假如需要添加http,那么编程"serviceId" : "^(https|imaps|http)://.*",,也就是运行通过的域
  "serviceId" : "^(https|imaps|http)://.*",
  //服务名
  "name" : "HTTPS and IMAPS",
  //id,唯一标识符
  "id" : 10000001,
  //描述
  "description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
  //定义多个服务器的执行顺序,数字越大优先级越低
  "evaluationOrder" : 10000
}

假如serviceId不能匹配会出现没有授权的错误

客户端

https://github.com/apereo/java-cas-client   客户端下载

 客户端配置web.xml

<!--退出过滤器-->
    <filter>
        <filter-name>CAS Single Sign Out Filter</filter-name>
        <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
        <init-param>
            <param-name>casServerUrlPrefix</param-name>
            <param-value>https://cas.test.com:8443/cas</param-value>
        </init-param>
    </filter>
    <!--退出监听器-->
    <listener>
        <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
    </listener>
    <!--认证过滤器-->
    <filter>
        <filter-name>CAS Authentication Filter</filter-name>
        <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
     <!--没有通过认证重定向的页面--> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://cas.test.com:8443/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://localhost:9002</param-value> </init-param> </filter> <!--验证过滤器--> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>https://cas.test.com:8443/cas</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://localhost:9002</param-value> </init-param> <init-param> <param-name>redirectAfterValidation</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>useSession</param-name> <param-value>true</param-value> </init-param> </filter> <!-- 该过滤器负责实现HttpServletRequest请求的包裹--> <filter> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

测试

7. 配置数据库密码认证及md5加密,当然也可以使用其他方式加密。

使用JDBC与MD5

依赖

<!-- 数据库驱动 --> 
<dependency> 
    <groupId>mysql</groupId> 
    <artifactId>mysql-connector-java</artifactId> 
    <version>5.1.21</version> 
</dependency> 

<!--jdbc认证需要添加的,这个是cas的依赖包--> 
<dependency>
     <groupId>org.apereo.cas</groupId> 
    <artifactId>cas-server-support-jdbc</artifactId> 
    <version>${cas.version}</version> 
</dependency>

在application.properties文件中可添加额配置,绿色表示必须

#Query Database Authentication 数据库查询校验用户名开始 
#查询账号密码sql,必须包含密码字段
cas.authn.jdbc.query[0].sql=select * from sys_user where username=?
#指定上面的sql查询字段名(必须,密码)
cas.authn.jdbc.query[0].fieldPassword=password
#指定过期字段,1为过期,若过期不可用
cas.authn.jdbc.query[0].fieldExpired=expired
#为不可用字段,1为不可用,需要修改密码
cas.authn.jdbc.query[0].fieldDisabled=disabled
#数据库方言(hibernate的知识)
cas.authn.jdbc.query[0].dialect=
#数据库驱动
cas.authn.jdbc.query[0].driverClass=
#数据库连接
cas.authn.jdbc.query[0].url=
#数据库用户名
cas.authn.jdbc.query[0].user=
#数据库密码
cas.authn.jdbc.query[0].password=
#默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
#Query Database Authentication

若密码无加密,调整passwordEncoder.type=NONE
若密码加密策略为SHA,调整passwordEncoder.encodingAlgorithm=SHA
若算法为自定义,实现org.springframework.security.crypto.password.PasswordEncoder接口,并且把类名配置在passwordEncoder.type

对密码进行盐值处理再加密,增加了反查难度,如上面的例子,对密码只是简单的加密,不同的帐号有可能相同的值,能判断出密码是一致,但通过此方案,大大增加了难度,所以安全系数也高了许多,推荐策略

#Encode Database Authentication 开始 
#加密次数
cas.authn.jdbc.encode[0].numberOfIterations=2
#该列名的值可替代上面的值,但对密码加密时必须取该值进行处理
cas.authn.jdbc.encode[0].numberOfIterationsFieldName=
#盐值固定列
cas.authn.jdbc.encode[0].saltFieldName=username
#静态盐值
cas.authn.jdbc.encode[0].staticSalt=.
cas.authn.jdbc.encode[0].sql=select * from sys_user_encode where username=?
#对处理盐值后的算法
cas.authn.jdbc.encode[0].algorithmName=MD5
cas.authn.jdbc.encode[0].passwordFieldName=password
cas.authn.jdbc.encode[0].expiredFieldName=expired
cas.authn.jdbc.encode[0].disabledFieldName=disabled
cas.authn.jdbc.encode[0].url=
cas.authn.jdbc.encode[0].dialect=
cas.authn.jdbc.encode[0].user=sa
cas.authn.jdbc.encode[0].password=
cas.authn.jdbc.encode[0].driverClass=
#Encode Database Authentication 结束

 8. 退出

#允许发出退出控制退出后转发url
cas.logout.followServiceRedirects=true

 退出后跳转到指定页面,假如只有https://ip/cas/logout,那么会跳到注销页面

<a  href ="https://ip/cas/logout?service=https://demo.testcas.com/cas-server/login" />

 如下配置(不需要配置)

cas.logout.redirectParameter=service#上面a href的service的由来
cas.logout.confirmLogout=false #是否需要出现confirm窗口
cas.logout.removeDescendantTickets=false #是否需要移除Ticket
原文地址:https://www.cnblogs.com/gg128/p/9897918.html