CAS( Central Authentication Service)是耶鲁大学的开源项目,旨在为 Web 运用系统供应一种可靠的单点登录办理方法。
CAS 开始于 2001 年, 并在 2004 年 12 月正式成为 JA-SIG 的一个项目。
本文档作为sodb运用系统认证集成手册,旨在辅导sodb集成系统实现统一身份认证和登录集成。

包括了SSL安全证书认证、CAS客户端配置。
术语与缩略语整体认证流程流程解释:
1、app端有session时,无需再次要求cas做事端(实线部分的流程即可)。
2、app端有st,则直接校验即可(无须重定向到cas做事端获取st)。
3、只有当1、2都不知足,或者校验不通过期,才会唤起输入用户名和密码的页面。
4、cas是集成了spring-webflow的,而对应的信息又保存在cas_server真个session中。以是要分布式支配的话,不能忘了session的共享。
票据解释:
1、TGC (Ticket-granting cookie),是存放在浏览器中的,从它是名字就能判断出来。
2、包含用户信息的session保存在app做事端和cas的做事端
3、TGT(Ticket-granting Ticket),ST(Service Ticket)存放在cas做事端中。要实现上图的分布式支配,将其存入共用的数据库即可。
单点登录实现方案单点登录概述单点登录,即用户只登录一次,在其随后访问所有的授权的资源时,都不须要再次登录。本方案利用CAS产品为根本来实现。
CAS(Central Authentication Service) 是 Yale 大学发起的一个开源项目,据统计,大概每 10 个采取开源构建 Web SSO 的 Java 项目,就有 8 个利用 CAS 。下方紧张先容CAS构造体系、协议与安全性。
CAS构造体系从构造体系看, CAS 包含两部分:
1) CAS Server
CAS Server 卖力完成对用户的认证事情, CAS Server 须要独立支配。
CAS Server 会处理用户名 / 密码等凭据 (Credentials) ,可通过连接到LADP检索用户名密码信息,也可能在 XML 文件中检索用户密码,对这种办法, CAS 均供应一种灵巧但统一的接口 / 实现分离的办法, CAS 究竟是用何种认证办法,跟 CAS 协议是分离的,也便是,这个认证的实现细节可以自己定制和扩展。
2) CAS Client
CAS Client 卖力支配在客户端(把稳,我是指 Web 运用),原则上, CAS Client 的支配意味着,当有对本地 Web 运用的受保护资源的访问要求,并且须要对要求方进行身份认证, Web 运用不再接管任何的用户名密码等类似的 Credentials ,而是重定向到 CAS Server 进行认证。
CAS协议1) 根本协议
CAS 根本模式
上图是一个最根本的 CAS 协议, CAS Client 以 Filter 办法保护 Web 运用的受保护资源,过滤从客户端过来的每一个 Web 要求,同时, CAS Client 会剖析 HTTP 要求中是否包要求 Service Ticket( 上图中的 Ticket) ,如果没有,则解释该用户是没有经由认证的,于是, CAS Client 会重定向用户要求到 CAS Server ( Step 2 )。 Step 3 是用户认证过程,如果用户供应了精确的 Credentials , CAS Server 会产生一个随机的 Service Ticket ,然后,缓存该 Ticket ,并且重定向用户到 CAS Client (附带刚才产生的 Service Ticket ), Service Ticket 是不可以假造的,末了, Step 5 和 Step6 是 CAS Client 和 CAS Server 之间完成了一个对用户的身份核实,用 Ticket 查到 Username ,由于 Ticket 是 CAS Server 产生的,因此,以是 CAS Server 的判断是毋庸置疑的。
该协议完成了一个很大略的任务,便是 User(ah.zhangsan) 打开 IE ,直接访问 某一运用A,它被立即重定向到 CAS Server 进行认证, User 可能觉得到浏览器在 运用A 和 casserver 之间重定向,但 User 是看不到, CAS Client 和 CAS Server 相互间的 Service Ticket 核实 (Validation) 过程。当 CAS Server 奉告 CAS Client 用户 Service Ticket 对应确切身份, CAS Client 才会对当前 Request 的用户进行做事。
2) CAS 如何实现 SSO
CAS 可以很大略的实现跨域的 SSO ,由于,单点被掌握在CAS Server ,用户最有代价的 TGC-Cookie 只是跟 CAS Server 干系, CAS Server 就只有一个,因此,办理了 cookies 不能跨域的问题。
1.如果 User 的持有 TGC 且其还没失落效,那么就进行根本协议图的 Step4 ,达到了 SSO 的效果。
2.如果 TGC 失落效,那么用户还是要重新认证进行根本协议图的 Step3。
CAS安全性CAS 的安全性是一个非常主要的 Topic 。 CAS 从 v1 到 v3 ,都很依赖于 SSL ,它假定了这样一个事实,用户在一个非常不屈安的网络环境中利用 SSO , Hacker 的 Sniffer 会很随意马虎抓住所有的 Http Traffic ,包括通过 Http 传送的密码乃至 Ticket 票据。
2.3.1 TGC/PGT 安全性
对付一个 CAS 用户来说,最主要是要保护它的 TGC ,如果 TGC 不慎被 CAS Server 以外的实体得到, Hacker 能够找到该 TGC ,然后伪装 CAS 用户访问所有授权资源。
SSO 的安全性问题比普通运用的安全性还要严重,由于 SSO 存在一种门槛效应。以前纵然 Hacker 能够截获用户在 Web 运用 A 的密码,它也未必能知道用户在 Web 运用 B 的密码,但 SSO 让 Hacker 只须要截获 TGC( 打破了门槛 ) ,即能访问所有与该用户干系的所有运用系统。
PGT 跟 TGC 的角色是一样的,如果被 Hacker 得到,后果可想而知。
从根本模式可以看出, TGC 是 CAS Server 通过 SSL 办法发送给终端用户,因此,要截取 TGC 难度非常大,从而确保 CAS 的安全性。 以是CAS 的传输安全性依赖于SSL 。
2.3.2 Service Ticket/Proxy Ticket 安全性
Service Ticket 是通过 Http 传送的,意味着所网络中的其他人可以 Sniffer 到其他人的Ticket 。
CAS 协议从几个方面让 Service Ticket 变得更加安全。
· Service Ticket 只能利用一次
CAS 协议规定,无论 Service Ticket 验证是否成功, CAS Server 都会将做事真个缓存中打消该 Ticket,从而可以确保一个 Service Ticket 不被利用两次。
· Service Ticket 在一段韶光内失落效。
假设用户拿到 Service Ticket 之后,他要求 helloservice 的过程又被中断了, Service Ticket 就被空置了,事实上,此时, Service Ticket 仍旧有效。 CAS 规定 Service Ticket 只能存活一定的韶光,然后 CAS Server 会让它失落效。通过在 web.xml 中配置下面的参数,可以让 Service Ticket 在多少秒内失落效。该参数在业务运用的条件范围内,越小越安全。
· Service Ticket 是基于随机数天生的
Service Ticket 必须足够随机,如果 Service Ticket 生成规则被猜出(如果你利用了 ST+Helloservice+ 自增序列的办法, Hacker 就可以布局下一个 Ticket ),Hacker 就即是绕过 CAS 认证,直接访问所有做事。
CAS客户端配置单点登录客户端集成,紧张也便是配置了四个过滤器,和一个监听器。并且我们须要将用户信息和各个子系统进行同步。同时单点登录做事端配置了SSL,须要各个子系统导入证书到JDK中。
证书证书文件:casserver.cer
导入证书
办法一:
keytool -import -trustcacerts -alias casserver -file E:\Cas-SSO\ssl\casserver.cer -keystore \公众%JAVA_HOME%\jre\lib\security\cacerts\"大众
导入证书到jdk,输入确认命令的时候,是y不是yes
办法二:
keytool -import -alias casserver -file E:\Cas-SSO\ssl\casserver.cer -keystore \"大众%JAVA_HOME%\jre\lib\security\cacerts\"大众 -storepass changeit -trustcacerts
【解释】:-file指定证书的位置,也便是上一步导出证书的位置,命令中指定了JAVA_HOME,意思是将证书导入到客户端证书库,也便是jdk证书库中.由于客户端运用运行在本地,须要jdk的支持。回车之后,会让你输入密钥库口令,把稳,这里的密码必须要输入changeit(jdk密码),不能输入上面指定的密码123456,牢记,否则导入客户端证书会有问题,如果是多台机器演示,须要在每一台客户端导入该证书,步骤都是一样的。当看到提示\公众是否信赖此证书\"大众,输入y回车即可,见下图:(解释,命令中的-alias后面的别名可以自定义,如果涌现【证书未导入,别名<>已经存在】的缺点,该意思是说客户真个密钥库中已经存在该别名证书了,重新指定其他别名即可.)
证书查看#查看证书信息
keytool -list -keystore E:\Cas-SSO\ssl\cas
#查看jdk目录下的证书
keytool -list -keystore \"大众%JAVA_HOME%\jre\lib\security\cacerts\公众 | findstr/i casserver
keytool -list -keystore \"大众%JAVA_HOME%\jre\lib\security\cacerts\公众 -alias casserver
配置hosts环境准备· jdk1.8
· apache-tomcat-8.5.15
· cas-client-core-3.4.0.jar、commons-logging.jar
· 确保本地jdk环境已经搭建好
通用客户端配置客户端地址:http://client.cas.com:8080/clientcas做事地址:https://server.cas.com:8443/cas
jar包导入办法一:
在pom.xml配置中,须要导入cas的客户端,也可自行***依赖包添加进去。
<!-- cas client -->
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.4.1</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
办法二:
须要将供应的cas-client-core-3.2.1.jar和commons-logging.jar放置到程序所在的lib下,并配置jar包路径为运用的classpath中。如:WebRoot\WEB-INF\lib中。
创建资源过滤器类如果我们须要某些系统资源不须要单点登录校验,我们可以在web.xml中直接设置须要过滤路径的正则表达式,过滤掉这些资源的登录校验,也可以自定义UrlPatternMatcherStrategy接口的实现类。
创建一个过滤器类SimpleUrlPatternMatcherStrategy,这个类须要实现UrlPatternMatcherStrategy接口,添加 matches里面的方法,来判断是否过滤掉这条要求。
import org.jasig.cas.client.authentication.UrlPatternMatcherStrategy;
/
性能概要:过滤掉一些不须要授权,登录的界面
/
public class SimpleUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy {
/
性能概要: 判断是否匹配这个字符串
@param url 用户要求的连接
@return true : 我就不拦截你了
false :必须得登录了
/
@Override
public boolean matches(String url) {
//当含有loginout的字段,就可以不用登录了
return url.contains(\"大众/loginOut/success\"大众);
}
/
正则表达式的规则,这个地方可以是web通报过来的
/
@Override
public void setPattern(String pattern) {
}
}
配置web.xml接下来是客户端配置的紧张内容,紧张是为了让CAS客户端与CAS做事端建立连接,须要在web.xml配置干系过滤器 (加色部分须要修正为实际参数)。
个中SingleSignOutFilter必须是首个过滤器。
<!--<!-- CAS Begin -->
<!-- 用于单点退出,该过滤器用于实现单点登出功能,可选配置-->
<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>
<!--这里的server是CAS做事真个登录地址,login为固定值-->
<param-value>https://server.cas.com:8443/cas/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<!--这里是运用地址,把稳是域名:端口或者ip:端口-->
<param-value>http://client.cas.com:8080/client</param-value>
</init-param>
<!-- 不须要匹配的类,可不要 -->
<init-param>
<param-name>ignoreUrlPatternType</param-name>
<param-value>com.yellowcong.auth.SimpleUrlPatternMatcherStrategy</param-value>
</init-param>
<!-- 不须要认证地址的正则pattern,可不要 -->
<init-param>
<param-name>ignorePattern</param-name>
<param-value>.</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Authentication Filter</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
<!-- 该过滤器卖力对Ticket的校验事情,必须启用它 -->
<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>
<!--这里的server是CAS做事真个地址,这里不要加login-->
<param-value>https://server.sodb.com:8443/cas</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<!--这里是运用地址,把稳是域名:端口或者ip:端口-->
<param-value>http://client.cas.com:8080/client</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>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
<!--
该过滤器卖力实现HttpServletRequest要求的包裹,比如许可开拓者通过HttpServletRequest的getRemoteUser()方法得到SSO登任命户的登录名,可选配置。
-->
<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>
<!--
该过滤器使得开拓者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。比如AssertionHolder.getAssertion().getPrincipal().getName()。
-->
<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>
<!-- CAS end -->
以上的核心点有两个:
· serverName 客户端访问路径
· casServerUrl cas做事路径
但上面的配置非常的大略,除了被打消的所有路径都会跳转进行登录
再加一个需求,判断路径是否须要跳转到登录页再跳转,那么不得不先容一下AuthenticationFilter的一些大略配置:
配置单点登出
退出的时候,有两种办法,一种是走默认的退出页面,另一种是,退出后,跳转到哪一个界面,当然,这个界面必须能许可未登任命户访问。
1. 直接登出,返回CAS默认登出视图:
https://server.cas.com:8443/logout
2. 系统登出,并返回CAS指定返回页面 (参数名为service)
https://server.cas.com:8443/logout?service=xxx
掌握器类:
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/
功能解释:用户掌握器
/
@RequestMapping(\公众/user\"大众)
@Controller
public class UserController {
/
性能概要:单点登出
@param session
@return
/
@RequestMapping(\"大众/loginOut\公众)
public String loginOut(HttpSession session){
session.invalidate();
//http://yellowcong.com:8080/cas-client-maven/user/loginOut/success
//这个是直接退出,走的是默认退出办法
return \"大众redirect:https://server.cas.com:8443/logout\"大众;
}
@RequestMapping(\"大众/loginOut2\"大众)
public String loginOut2(HttpSession session){
session.invalidate();
//退出登录后,跳转到退成成功的页面,不走默认页面
return \"大众redirect:https://server.cas.com:8443/logout?service=http://client.cas.com:8080/client/user/loginOut/success\"大众;
}
/
功能概要:退出成功的界面
@return
/
@RequestMapping(\公众/loginOut/success\公众)
public String loginOutPage(){
return \"大众user/loginOut\公众;
}
}
访问成功启动客户端做事后,访问运用会被重定向到CAS统一认证界面,界面如下
输入用户名和密码后会跳转至各自运用界面。
获取用户
String userName = request.getRemoteUser(); // 获取用户名信息
// 办法1: 通过request工具获取
Principal principal = request.getUserPrincipal();
Map<String, Object> map = null;
if(principal!=null && principal instanceof AttributePrincipal){
AttributePrincipal aPrincipal = (AttributePrincipal)principal;
//获取用户信息中公开的Attributes部分
map = aPrincipal.getAttributes();
// 获取姓名,可以根据属性名称获取其他属性
}
// 办法2:通过session获取
Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
Map<String, Object> map = assertion.getAttributes();
// 办法3:通过AssertionHolder类获取
// 获取用户名
String name = AssertionHolder.getAssertion().getPrincipal().getName();
// 获取用户属性
Map<String, Object> map = AssertionHolder.getAssertion().getPrincipal().getAttributes();
自定义客户端集成上面说到了基本的单点登录客户端集成,但是如果想要在实现单点登录的同时,保留原有的登录功能(当单点登录做事有问题的时候)。这就须要我们在自己系统的登录校验机制中集成单点的;同时如果存在自己系统的登录标识,须要适配单点的登录。
实现思路:
1. 在单点中过滤掉本系统的所有关于登录干系的链接地址,不进行单点拦截
2. 在登录模块方法中,登录成功后仿照一个单点登录信息(Assertion工具)并放入session中
3. 在client系统的登录校验中加入单点的登录校验(比如:拦截器的办法实现)
4. 获取用户信息的办法加入从单点获取的办法
5. 系统登出的时候加上单点登出
查看AuthenticationFilter代码可知,过滤器中通过从session中获取Assertion工具来判断是否须要做登录验证,如果想要自己登录的时候不走单点校验了(骗过单点以为已登录),须要我们在走AuthenticationFilter前自己仿照一个Assertion工具并放入session中即可(或者我们重写AuthenticationFilter类的doFilter方法加入加入自己的登录判断)。
将自己的信息适配到单点登录中有两种实现思路:
1. 在登录成功后, 将登录信息天生一个Assertion工具放入Session中。
2. 在AuthenticationFilter 过滤器前添加一个过滤器,先做session判断是否存在Assertion工具,如果不存在则 根据登录信息天生一个Assertion工具并放入session中。
为此封装了一个工具类CASUtil。该类封装了对登录信息转换Assertion存入session、单点信息获取、判断是否是子系统登录放入session的判断等方法。
解释:
仿照单点登录信息
在登录接口中,登录成功后调用CASUtil.setUserToCasIfNotLogin(HttpServletRequest request, String principal, Map<String, Object> attributes)方法,将登录数据天生Assertion工具并存入session中。
如下:
Client登录校验集成单点
在登录校验中,如果本地登录校验不通过,然后判断是否存在CAS登录标识。如果存在则认为成功并放行要求,否则认证失落败。
如下方法判断是否存在单点登录标识:
CASUtil.hasCASUser(request);
本系统是在拦截器中实现:
集成用户信息获取
获取用户信息,可以先从CAS中获取用户信息(ID等),然后可以根据自己系统业务获取用户信息。
获取单点用户信息:
Map<String, Object> assertion = CASUtil.getCASUser(request);// 获取单点登任命户信息
如下:
集成登出功能
在登出接口中,调用CASUtil.isSelfLogin(request) 方法判断是否是client系统登录,如果单点登录,则重定向调用CAS的登出地址,并带上service参数指向项目地址。
如下:
双向认证
有时候或许单点登录的时效 和 系统自身的登录时效分歧一,导致单点 或者 系统自身验证单一方面失落效,我们可以在单点过滤器前 增加一个过滤器,进行校验判断。
参考github 项目地址:
https://github.com/apereo