CAS实现单点登录
1.简介
SSO (Single Sign On)单点登录:在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.
新浪微博与新浪博客是相互信任的应用系统.
*当用户首次访问新浪微博时,认证中心识别到用户未登录,将请求重定向到登录页.
*当用户已登录再访问新浪博客时,认证中心识别到用户已登录,返回用户的身份,此时用户无需登录即可使用新浪博客.
CAS :Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法, CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目.
CAS包含CAS Client 和 CAS Server.
CAS Client:要使用单点登录的Web应用, 将与同组下的Web应用构成相互信任的关系,只需在web应用中添加CAS提供的Listener和Filter即可成为CAS Client ,其主要负责对客户端的请求进行重定向和校验ticket工作.
CAS Server:主要负责对用户的用户名/密码进行认证, 颁发票据等, 需要单独的进行部署.
*同组下的任意一个Web应用登录后其他应用都不需要登录即可使用.
2.CAS服务器搭建
2.1 去CAS源码包
将下载的源码包中的cas-server-webapp工程导入ide中,将工程导出为war包,直接放入tomcat下的webapp中运行.
*CAS 5.0版本以上需要jdk1.8和gradle进行构建、4.X版本使用maven进行构建(maven 3.3+)
2.2 在Tomcat中开启HTTPS协议
*由于CAS Server默认使用HTTPS协议进行访问,因此需要在Tomcat中开启HTTPS协议.
1.使用JDK提供的keytool命令生成秘钥库
2.修改tomcat配置并开启8443端口
在tomcat/conf/server.xml中添加:
<!-- 单向认证 --> <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="www.gimc.cn.keystore" keystorePass="123456" />
校验Tomcat是否支持HTTPS协议:
2.3 进入CAS认证中心
登录处理地址:
*由于首次访问,客户端浏览器进程所占用的内存中不存在TGC Cookie,所以CAS Server认为用户未进行登录,因此将请求转发到登录页面.
*默认账号:casuser/Mellon
*当登录后再次访问登录处理时,将会直接转发到已登录页面.
*CAS Server根据Cookie(TGC是否能匹配TGT)来判断用户是否已登录过,默认情况下TGC Cookie位于浏览器进程所占用的内存中,因此当关闭浏览器时Cookie失效(TGC失效),此时再访问CAS登录页面将需要重新登录,当CAS服务器重启时,TGT将会失效(位于服务器内存),此时也需要重新进行登录.
*当用户登录后,CAS Server会维护TGT与用户身份信息的关系,所有CAS Client可以从CAS Server中获取当前登录的用户的身份信息.
注销处理地址:
*在已登录的状态下访问注销地址将会提示注销成功,其经过以下步骤:
1.清除保存在客户端浏览器的TGC Cookie(设空).
2.清除保存在服务器的TGT.
3.销毁所有CAS Client的Session对象.
*当注销成功后,此时再访问登录页面时需重新登录.
2.4 修改为自定义数据源
1.修改cas-server-webapp/WEB-INF/deployerConfigContext.xml
注释配置:
<!-- <alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" /> -->
新增配置:
<!-- 对密码进行加密 --> <bean id="passwordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"> <constructor-arg value="MD5"></constructor-arg> <property name="characterEncoding" value="UTF-8"></property> </bean> <!-- 自定义数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/cas?useUnicode=true&characterEncoding=UTF-8"></property> <property name="user" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 认证控制器 --> <bean id="queryDatabaseAuthenticationHandler" name="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"> <property name="passwordEncoder" ref="passwordEncoder" /> <property name="dataSource" ref="dataSource" /> <!-- 通过用户名查询密码的SQL --> <property name="sql" value="select password from sys_user where username =?" /> </bean>
2.在cas-server-webapp/WEB-INF/lib包中添加:cas-server-support-jdbc.jar、mysql-connector-java.jar
2.5 修改为HTTP方式访问
1.修改cas-server-webapp/WEB-INF/cas.properties
tgc.secure=false warn.cookie.secure=false
2.修改cas-server-webapp/WEB-INF/classes/services/HTTPSandIMAPS-10000001.json
"serviceId" : "^(https|imaps|http)://.*"
*修改serviceId的值即可.
3.删除cas-server-webapp/WEB-INF/view/jsp/default/ui/casLoginView.jsp页面中校验是否是HTTPS协议的标签块.
<c:if test="${not pageContext.request.secure}"> <div id="msg" class="errors"> <h2><spring:message code="screen.nonsecure.title" /></h2> <p><spring:message code="screen.nonsecure.message" /></p> </div> </c:if>
3.CAS客户端搭建
3.1 引入Maven依赖
<dependency> <groupId>org.jasig.cas.client</groupId> <artifactId>cas-client-core</artifactId> <version>3.2.0</version> </dependency>
3.2 在web.xml中配置CAS提供的Listener、Filter
<!-- 单点退出Listener --> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <!-- 单点退出Filter --> <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> <!-- CAS认证Filter --> <filter> <filter-name>CASFilter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <!-- CAS登录页面,当SessionId无法匹配Session时,跳转到CAS登录页面 --> <param-name>casServerLoginUrl</param-name> <param-value>http://localhost:8080/cas-server-webapp-4.2.7/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://localhost:8080</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- CAS Ticket校验Filter --> <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>http://localhost:8080/cas-server-webapp-4.2.7</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://localhost:8080</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 使客户端支持通过AssertionHolder来获取用户的登录名 --> <filter> <filter-name>CAS Assertion Thread Local Filter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS Assertion Thread Local Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
*客户端可通过 AssertionHolder.getAssertion().getPrincipal().getName() 获取当前登录用户的用户名.
4.CAS原理分析
4.1 项目架构图
4.2 用户第一次访问项目A
*用户第一次访问项目A,请求将到达项目A的CAS Filter,由于没有携带SessionId,无法成功匹配Session,因此项目A认为该用户未登录,返回302状态码示意浏览器将请求重定向到CAS Server进行登录处理,并在请求URL后追加service参数传递原访问项目A的URL.
*CAS Client根据SessionId能否成功匹配到Session来判断当前用户是否已经登录过.
*当浏览器收到项目A返回的302重定向请求后,对重定向目标地址重新发起HTTP请求,最终到达CAS Server进行登录处理,由于浏览器不存在TGC Cookie,CAS Server认为用户未进行登录,因此将请求转发到登录页面.
*输入用户名/密码进行提交
*CAS Server对用户输入的用户名/密码进行校验,若校验成功则返回302状态码示意浏览器将请求重定向到原访问项目A的URL地址并在URL后追加ticket参数传递ST,并且最终保存TGC Cookie在客户端浏览器进程所占用的内存中.
TGC:Ticket Granted Cookie , 以Cookie的形式保存在客户端浏览器所占用的内存中(Cookie值).
TGT:Ticket Granted Ticket, 保存在CAS服务器的内存中,其可以签发ST.
ST:Service Ticket, 由TGT签发,最终通过URL传给CAS Client .
*CAS Server根据TGC匹配TGT, TGT又与用户的登录信息相关联.
*当用户登录成功后,此时客户端就存在TGC Cookie, CAS服务端就存在对应的TGT.
*当浏览器收到CAS Server返回的302重定向请求后,对重定向目标地址重新发起HTTP请求( 携带ticket参数 ),此时请求将会进入项目A的ticket验证Filter中, 项目A将对 ticket(ST) 进行有效性校验( 内部访问Cas Server进行校验 ),若校验成功则返回302状态码示意浏览器将请求重定向到原访问项目A的地址( 不带ticket参数 )
*当浏览器收到项目A返回的302重定向请求后, 重新请求最初访问项目A的URL地址.
*由于携带了SessionId并且能成功匹配Session对象,因此项目A认为用户已经进行登录,允许访问资源.
4.3 用户再次访问项目A
*由于携带了SessionId并且能成功匹配Session对象,因此项目A认为用户已经进行登录,允许访问资源.
4.4 用户第一次访问项目B
*用户第一次访问项目B,请求将到达项目B的CAS Filter,由于没有携带SessionId,因此项目B认为该用户未登录,返回302状态码示意浏览器将请求重定向到CAS Server进行登录处理,并在请求URL后追加service参数传递原访问的URL.
*当浏览器收到项目B返回的302重定向请求后,对重定向目标地址重新发起HTTP请求,最终到达CAS Server进行登录处理,由于客户端浏览器中存在TGC Cookie,并且CAS Server成功根据TGC匹配TGT,所以CAS Server认为该用户已经进行登录,最终通过TGT签发ST,返回302状态码示意浏览器将请求重定向到原访问项目B的URL,并在URL追加ticket参数传递ST.
*当浏览器收到CAS Server返回的302重定向请求后,对重定向目标地址重新发起HTTP请求( 携带ticket参数 ),此时请求将会进入项目B的ticket认证Filter中, 项目B将对ticket进行有效性校验( 内部访问Cas Server进行校验 ),若校验成功则返回302状态码示意浏览器将请求重定向到原访问项目B的地址( 不带ticket参数 )
*当浏览器收到项目B返回的302重定向请求后, 重新请求最初访问项目B的URL地址.
*由于携带了SessionId并且能成功匹配Session对象,因此项目B认为用户已经进行登录.
4.5 注销
访问CAS Server注销处理地址:
*当访问CAS注销地址后,将会清除位于客户端浏览器进程所占用的内存中的TGC Cookie (设空) 、清除位于CAS Server中对应的TGT 、销毁所有CAS Client此用户的Session对象,当再次访问项目A和项目B时,将需要重新进行登录.
4.6 关闭浏览器
*当关闭浏览器时Cookie失效(SessionId、TGC失效),此时再访问项目A和项目B时将需要重新登录.
4.7 重启CAS Server
*当CAS Server重启时,TGT将会失效(位于服务器内存),TGC无法成功匹配TGT,此时也需要重新进行登录.
*重启CAS Client后无须登录也可以使用.
上一篇: WCF WCF的宿主
下一篇: WCF 之部署(VS2010)