基于CAS实现单点登录
CAS
注:环境搭建相关资料:https://download.csdn.net/download/qq_31871785/11078868
一、概述
CAS是*认证服务Central Authentication Service
的简称。最初由耶鲁大学的Shawn Bayern 开发,后由Jasig社区维护,经过十多年发展,目前已成为影响最大、广泛使用的、基于Java实现的、开源SSO解决方案。
2012年,Jasig和另一个有影响的组织Sakai Foundation合并,组成Apereo。Apereo是一个由高等学术教育机构发起组织的联盟,旨在为学术教育机构提供高质量软件,当然很多软件也被大量应用于商业环境,譬如CAS。目前CAS由Apereo社区维护。
CAS的官方网址是: https://www.apereo.org/projects/cas
Github地址:https://github.com/apereo/cas
二、术语解释
CAS的核心就是其Ticket
,及其在Ticket之上的一系列处理操作。CAS的主要票据有TGT、ST、PGT、PGTIOU、PT,其中TGT、ST是CAS1.0(基础模式)协议中就有的票据,PGT、PGTIOU、PT是CAS2.0(代理模式)协议中有的票据。
-
TGT(Ticket Grangting Ticket)
TGT是CAS为用户签发的登录票据,拥有了TGT,用户就可以证明自己在CAS成功登录过。TGT封装了Cookie值以及此Cookie值对应的用户信息。用户在CAS认证成功后,CAS生成cookie(叫TGC),写入浏览器,同时生成一个TGT对象,放入自己的缓存,TGT对象的ID就是cookie的值。当HTTP再次请求到来时,如果传过来的有CAS生成的cookie,则CAS以此cookie值为key查询缓存中有无TGT ,如果有的话,则说明用户之前登录过,如果没有,则用户需要重新登录。
-
TGC (Ticket-granting cookie)
存放用户身份认证凭证的cookie,在浏览器和CAS Server间通讯时使用,并且只能基于安全通道传输(Https),是CAS Server用来明确用户身份的凭证。
-
ST(ServiceTicket)
ST是CAS为用户签发的访问某一service的票据。用户访问service时,service发现用户没有ST,则要求用户去CAS获取ST。用户向CAS发出获取ST的请求,如果用户的请求中包含cookie,则CAS会以此cookie值为key查询缓存中有无TGT,如果存在TGT,则用此TGT签发一个ST,返回给用户。用户凭借ST去访问service,service拿ST去CAS验证,验证通过后,允许用户访问资源。
2. 实现原理
-
CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求, CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket ( ST )和 Ticket Granting Ticket(TGT) ,如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 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环境搭建
创建证书
CAS 默认认证方式使用的是HTTPS协议,所以需要配置服务端的tomcat,使之支持SSL安全协议访问。
HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。
1)HTTPS协议需要到ca申请证书,一般免费证书很少,需要交费。
2)HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
证书是单点登录认证系统中很重要的一把钥匙,客户端于服务器的交互安全靠的就是证书;
本教程由于是演示所以就自己用JDK自带的keytool工具生成证书;如果以后真正在产品环境中使用肯定要去证书提供商去购买,证书认证一般都是由VeriSign认证(中文官方网站:http://www.verisign.com/cn/)
-
用JDK自带的keytool工具生成证书:
keytool -genkey -alias tomcat -keyalg RSA -keystore d:/keys/tomcat.keystore
-
导出证书
keytool -export -file d:/keys/tomcat.crt -alias tomcat -keystore d:/keys/tomcat.keystore
-
为客户端的JVM导入证书
keytool -import -keystore D:/softWare/Java/jdk1.8.0_91/jre/lib/security/cacerts -file D:/keys/tomcat.crt -alias tomcat
有可能会碰到报错请参考:
keytool 错误: java.io.IOException: Keystore was tampered with, or password was incorrect
这里需要输入的密码不是证书的密码执行keytool -import -keystore - file 这个命令提示需要输入密码。输入changeit
,信任证书,OK -
应用证书到Web服务器-Tomcat
打开tomcat目录的conf/server.xml文件,开启87和92行的注释代码,并设置keystoreFile、keystorePass修改结果如下
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="D:/keys/tomcat.keystore" keystorePass="123456" />
-
配置HOSTS映射
为CAS单点登录系统是基于JAVA安全证书的 https 访问, 要使用CAS单点登录必须要配置域名, CAS是不能通过ip访问的),编辑文件
C:\Windows\System32\drivers\etc\hosts
在文件末端添加下面三条信息:127.0.0.1 www.ssoserver.com 127.0.0.1 www.ssoclient1.com 127.0.0.1 www.ssoclient2.com
-
启动Tomcat并验证HTTPS配置
安装CAS-Server
-
将
cas.war
放置到tomcat/webapps/目录下,并启动tomcat在浏览器地址栏输入: https://www.ssoserver.com:8443/cas,出现CAS服务端的登录验证首页
使用账号密码(
casuser/Mellon
)登录CAS-Server -
导入lib
tomcat-7.0.57\webapps\cas\lib
导入以下2个jar包(自定义数据源相关)无需导入c3p0
安装CAS-Client
-
新建应用
cas_client1
,并导入CAS Client依赖<dependencies> <dependency> <groupId>org.jasig.cas.client</groupId> <artifactId>cas-client-core</artifactId> <version>3.3.3</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> </dependencies>
-
修改web.xml
<web-app> <display-name>Archetype Created Web Application</display-name> <!-- 单点登出配置--> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 单点登录配置--> <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://www.ssoserver.com:8443/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://www.ssoclient1.com:9090</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 票据配置 --> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>https://www.ssoserver.com:8443/cas</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://www.ssoclient1.com:9090</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 封装标准的HttpRequest,使得request.getRemoteUser()和request.getUserPrincipal()这两个方法可用 --> <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 HttpServletRequest Wrapper Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--定义的Servlet--> <servlet> <servlet-name>user</servlet-name> <servlet-class>com.baizhi.UserServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>user</servlet-name> <url-pattern>/user</url-pattern> </servlet-mapping> </web-app>
-
准备自定义Servlet
package com.baizhi; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.security.Principal; public class UserServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Principal principal = req.getUserPrincipal(); req.getSession().setAttribute("user",principal.getName()); req.getRequestDispatcher("/index.jsp").forward(req,resp); } }
-
准备受限资源
index.jsp
<html> <body> <h2>Hello World!</h2> Hello: ${user} </body> </html>
-
修改CAS-Server配置文件
修改
cas-server/cas/webapps/classes/services/HTTPSandIMAPS-10000001.json
,以支持CAS-Client Http请求形式访问CAS-Server -
启动CAS-Client测试
在浏览器地址栏输入: http://www.ssoclient1.com:9090/c1/user 回车,跳转到CAS-Server认证中心
输入用户名密码,跳转到SSO-Client
三、自定义数据源
环境搭建
-
创建用户表
Note:password进行了
MD5
加密 -
修改配置文件deployerConfigContext.xml
<!-- <bean id="primaryAuthenticationHandler" class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler"> <property name="users"> <map> <entry key="casuser" value="Mellon"/> </map> </property> </bean> --> <!-- 配置数据源--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" p:driverClass="com.mysql.jdbc.Driver" p:jdbcUrl="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull" p:user="root" p:password="root" /> <!-- 密码加密方式--> <bean id="passwordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder" autowire="byName"> <constructor-arg value="MD5"/> <property name="characterEncoding"> <value>UTF-8</value> </property> </bean> <!--查询匹配的字段--> <bean id="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler" p:dataSource-ref="dataSource" p:sql="select password from t_user where name = ?" p:passwordEncoder-ref="passwordEncoder" />
-
重启CAS-Server测试,使用自定义数据源访问
四、自定义登录页
环境搭建
-
CAS页面采用主题模板方式,定制一套自己的主题模板
# 拷贝default目录,并命名为custom cas_server/webapps/cas/WEB-INF/view/jsp/default
-
修改使用的主题模板
# 修改配置文件 cas_server/webapps/cas/WEB-INF/cas.properties
-
指定自定义页面的静态资源文件
# classes/cas-theme-default.properties 创建cas-theme-custom.properties文件,其内容是定义了css文件和js文件的位置。
-
在/css目录下创建custom.css文件,内容如下:
.form-bg{ background: #00b4ef; } .form-horizontal{ background: #fff; padding-bottom: 40px; border-radius: 15px; text-align: center; } .form-horizontal .heading{ display: block; font-size: 35px; font-weight: 700; padding: 35px 0; border-bottom: 1px solid #f0f0f0; margin-bottom: 30px; } .form-horizontal .form-group{ padding: 0 40px; margin: 0 0 25px 0; position: relative; } .form-horizontal .form-control{ background: #f0f0f0; border: none; border-radius: 20px; box-shadow: none; padding: 0 20px 0 45px; height: 40px; transition: all 0.3s ease 0s; } .form-horizontal .form-control:focus{ background: #e0e0e0; box-shadow: none; outline: 0 none; } .form-horizontal .form-group i{ position: absolute; top: 12px; left: 60px; font-size: 17px; color: #c8c8c8; transition : all 0.5s ease 0s; } .form-horizontal .form-control:focus + i{ color: #00b4ef; } .form-horizontal .fa-question-circle{ display: inline-block; position: absolute; top: 12px; right: 60px; font-size: 20px; color: #808080; transition: all 0.5s ease 0s; } .form-horizontal .fa-question-circle:hover{ color: #000; } .form-horizontal .main-checkbox{ float: left; width: 20px; height: 20px; background: #11a3fc; border-radius: 50%; position: relative; margin: 5px 0 0 5px; border: 1px solid #11a3fc; } .form-horizontal .main-checkbox label{ width: 20px; height: 20px; position: absolute; top: 0; left: 0; cursor: pointer; } .form-horizontal .main-checkbox label:after{ content: ""; width: 10px; height: 5px; position: absolute; top: 5px; left: 4px; border: 3px solid #fff; border-top: none; border-right: none; background: transparent; opacity: 0; -webkit-transform: rotate(-45deg); transform: rotate(-45deg); } .form-horizontal .main-checkbox input[type=checkbox]{ visibility: hidden; } .form-horizontal .main-checkbox input[type=checkbox]:checked + label:after{ opacity: 1; } .form-horizontal .text{ float: left; margin-left: 7px; line-height: 20px; padding-top: 5px; text-transform: capitalize; } .form-horizontal .btn{ float: right; font-size: 14px; color: #fff; background: #00b4ef; border-radius: 30px; padding: 10px 25px; border: none; text-transform: capitalize; transition: all 0.5s ease 0s; } @media only screen and (max-width: 479px){ .form-horizontal .form-group{ padding: 0 25px; } .form-horizontal .form-group i{ left: 45px; } .form-horizontal .btn{ padding: 10px 20px; } }
-
使用自定义页面覆盖默认页面
cas_server/webapps/cas/WEB-INF/view/jsp/custom