原理

这里用的是:cas-client-core-3.4.0.jar(2015-07-21发布的)

下载地址为:http://mvnrepository.com/artifact/org.jasig.cas.client/cas-client-core/3.4.0

下面介绍两种配置方法:一种是纯web.xml配置,一种是借助Spring来配置,相关的官方文档如下所示

https://wiki.jasig.org/display/CASC/Configuring+the+Jasig+CAS+Client+for+Java+in+the+web.xml

https://wiki.jasig.org/display/CASC/Configuring+the+JA-SIG+CAS+Client+for+Java+using+Spring

纯web.xml

web.xml中需配置四个顺序固定的Filter,而且出于认证考虑,最好配置在其他Filter之前,它们的先后顺序如下

  1. AuthenticationFilter
  2. TicketValidationFilter(或其它AbstractTicketValidationFilter实现,比如Cas20ProxyReceivingTicketValidationFilter)
  3. HttpServletRequestWrapperFilter
  4. AssertionThreadLocalFilter

下面分别介绍一下四个Filter的各自用途

  • AuthenticationFilter

用来拦截请求,判断是否需要CASServer认证,需要则跳转到CASServer登录页,否则放行请求

有两个必须参数:一个是指定CASServer登录地址的casServerLoginUrl,一个是指定认证成功后跳转地址的serverName或service

service和serverName设置一个即可,二者都设置时service的优先级更高(即会以service为准)

service指的是一个确切的URL,而serverName是用来指定客户端的主机名的,格式为{protocol}:{hostName}:{port}

指定serverName时,该Filter会把它附加上当前请求的URI及对应的查询参数来构造一个确切的URL作为认证成功后的跳转地址

比如serverName=”http://gg.cn”,当前请求的URI为”/oa”,查询参数为”aa=bb”,则认证成功后跳转地址为http://gg.cn/oa?aa=bb

简单来讲:casServerLoginUrl--去哪登录serverName--我是谁

  • TicketValidationFilter

请求通过AuthenticationFilter认证后,若请求中携带了ticket参数,则会由该类Filter对携带的ticket进行校验

验证ticket的时候,要访问CAS服务的/serviceValidate接口,使用的url就是${casServerUrlPrefix}/serviceValidate

所以它也有两个参数是必须指定的,casServerUrlPrefix(CASServer对应URL地址的前缀)和serverName或service

实际上,TicketValidationFilter只是对验证ticket的这一类Filter的统称,其并不对应CASClient中的具体类型

CASClient中有多种验证ticket的Filter,都继承自AbstractTicketValidationFilter

常见的有Cas10TicketValidationFilter/Cas20ProxyReceivingTicketValidationFilter/Saml11TicketValidationFilter

它们的验证逻辑都是一致的,都有AbstractTicketValidationFilter实现,只是使用的TicketValidator不一样而已

如果要从服务器获取用户名之外的更多信息应该采用CAS20这个2.0协议的代理

  • HttpServletRequestWrapperFilter

用于封装每个请求的HttpServletRequest为其内部定义的CasHttpServletRequestWrapper

它会将保存在Session或request中的Assertion对象重写HttpServletRequest的getUserPrincipal()、getRemoteUser()、isUserInRole()

这样在我们的应用中就可以非常方便的从HttpServletRequest中获取到用户的相关信息

  • AssertionThreadLocalFilter

为了方便用户在应用的其它地方获取Assertion对象,其会将当前的Assertion对象存放到当前的线程变量中

以后用户在程序的任何地方都可以从线程变量中获取当前的Assertion,而无需从Session或request中解析

该线程变量是由AssertionHolder持有的,可以通过Assertion assertion = AssertionHolder.getAssertion()获取当前Assertion

借助Spring

与上述web.xml配置四个Filter方式不同的是,可以使用Spring的四个DelegatingFilterProxy来代理需要配置的四个Filter

此时这四个Filter就应该配置为Spring的Bean对象,并且web.xml中的就应该对应SpringBean名称

但是SingleSignOutFilter/HttpServletRequestWrapperFilter/AssertionThreadLocalFilter等Filter不含配置参数

所以实际上只需要配置AuthenticationFilter和Cas20ProxyReceivingTicketValidationFilter两个Filter交由Spring代理就可以了

注意

  • CAS1.0提供的接口有/validate,CAS2.0提供的接口有/serviceValidate、/proxyValidate、/proxy

  • 四个Filter太多了,有时间的话考虑参考org.springframework.web.filter.CompositeFilter写一个Filter来实现

  • web.xml的好处是可以配置匿名访问的资源,配置参数参考AuthenticationFilter中的ignoreUrlPatternMatcherStrategyClass

   起码cas-client-core-3.4.0.jar中的Spring配置还不支持ignorePattern(该参数默认正则验证,此外还有contains和equals验证)

  • javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching casserver found

   这是由于创建证书的域名和应用中配置的CAS服务域名不一致(也就是说客户端导入的CRT证书与CAS服务端的域名不同)

代码

本文源码下载:(下面两个地址的文件的内容,都是一样的)

Github:https://github.com/v5java/demo-cas-client

CSDN下载:http://download.csdn.net/detail/jadyer/8934207

下面是web.xml的配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping> <filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> <!-- SSO -->
<filter>
<filter-name>casAuthenticationFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>casAuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>casTicketValidationFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>casTicketValidationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--
<context-param>
<param-name>serverName</param-name>
<param-value>http://boss.jadyer.com:8080</param-value>
</context-param>
<filter>
<filter-name>casAuthenticationFilter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>http://sso.jadyer.com:8080/cas-server-web/login</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>casAuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>casTicketValidationFilter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>http://sso.jadyer.com:8080/cas-server-web</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>casTicketValidationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-->
<filter>
<filter-name>casHttpServletRequestWrapperFilter</filter-name>
<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>casHttpServletRequestWrapperFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>casAssertionThreadLocalFilter</filter-name>
<filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>casAssertionThreadLocalFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

下面是/src/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xs 大专栏  单点登录CAS系列第06节之客户端配置单点登录i="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="ignoreResourceNotFound" value="false"/>
<property name="locations">
<list>
<value>classpath:config.properties</value>
</list>
</property>
</bean>
<mvc:resources mapping="/index.jsp" location="/index.jsp"/> <!-- cas -->
<bean name="casAuthenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter">
<property name="serverName" value="${casClientServerName}"/>
<property name="casServerLoginUrl" value="${casServerLoginUrl}"/>
</bean>
<bean name="casTicketValidationFilter" class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter">
<property name="serverName" value="${casClientServerName}"/>
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="${casServerUrlPrefix}"/>
</bean>
</property>
</bean>
</beans>

下面是/src/config.properties

#<<Central Authentication Service>>
#where to login
casServerLoginUrl=http://sso.jadyer.com:8080/cas-server-web/login
#login server root
casServerUrlPrefix=http://sso.jadyer.com:8080/cas-server-web
#who am i
#casClientServerName=http://boss.jadyer.com:8180
casClientServerName=http://risk.jadyer.com:8280

最后是/WebRoot/index.jsp

<%@ page pageEncoding="UTF-8"%>
<%@ page import="java.util.Map"%>
<%@ page import="java.net.URLDecoder"%>
<%@ page import="org.jasig.cas.client.util.AssertionHolder"%>
<%@ page import="org.jasig.cas.client.authentication.AttributePrincipal"%> <body style="background-color:#CBE0C9;">
<span style="color:red; font-size:32px; font-weight:bold;">客户端登录成功</span>
</body> <hr size="2"> <%
AttributePrincipal principal = (AttributePrincipal)request.getUserPrincipal();
Map<String, Object> attributes = principal.getAttributes();
out.print("principal.getName()=" + principal.getName() + "<br/>");
out.print("request.getRemoteUser()=" + request.getRemoteUser() + "<br/>");
out.print("登录用户:" + attributes.get("userId") + "<br/>");
out.print("登录时间:" + AssertionHolder.getAssertion().getAuthenticationDate() + "<br/>");
out.print("-----------------------------------------------------------------------<br/>");
for(Map.Entry<String, Object> entry : attributes.entrySet()){
//服务端返回中文时需要encode,客户端接收显示中文时需要decode,否则会乱码
out.print(entry.getKey() + "=" + URLDecoder.decode(entry.getValue().toString(), "UTF-8") + "<br/>");
}
out.print("-----------------------------------------------------------------------<br/>");
Map<String, Object> attributes22 = AssertionHolder.getAssertion().getAttributes();
for(Map.Entry<String, Object> entry : attributes22.entrySet()){
out.print(entry.getKey() + "=" + entry.getValue() + "<br/>");
}
out.print("-----------------------------------------------------------------------<br/>");
Map<String, Object> attributes33 = AssertionHolder.getAssertion().getPrincipal().getAttributes();
for(Map.Entry<String, Object> entry : attributes33.entrySet()){
out.print(entry.getKey() + "=" + entry.getValue() + "<br/>");
}
%>

测试

接下来就可以测试了,测试之前先修改几处配置,模拟单点环境

首先在C:WindowsSystem32driversetchosts中添加以下三个配置

127.0.0.1 sso.jadyer.com
127.0.0.1 boss.jadyer.com
127.0.0.1 risk.jadyer.com

然后拷贝三个Tomcat,分别用作sso服务器和两个sso客户端

修改两个sso客户端的Tomcatconfserver.xml的以下三个端口,保证启动监听端口不重复

<Server port="8105" shutdown="SHUTDOWN">
<Connector port="8180" protocol="HTTP/1.1"......>
<Connector port="8109" protocol="AJP/1.3" redirectPort="8443"/>
<Server port="8205" shutdown="SHUTDOWN">
<Connector port="8280" protocol="HTTP/1.1"......>
<Connector port="8209" protocol="AJP/1.3" redirectPort="8443"/>

最后修改两个sso客户端Tomcatwebappscas-clientWEB-INFclassesconfig.properties里面的casClientServerName的值

casClientServerName=http://boss.jadyer.com:8180
casClientServerName=http://risk.jadyer.com:8280

下面开始测试

先访问http://boss.jadyer.com:8180/cas-client,发现没登录会自动跳转到单点登录页

输入密码后登录成功

再访问http://risk.jadyer.com:8280/cas-client,会发现自动登录成功,不用再登录了

单点登录CAS系列第06节之客户端配置单点登录的更多相关文章

  1. cas sso单点登录系列7_ 单点登录cas常见问题系列汇总

    转:http://blog.csdn.net/matthewei6/article/details/50709252 单点登录cas常见问题(一) - 子系统是否还要做session处理?单点登录ca ...

  2. cas系列(一)--cas单点登录基本原理

    (这段时间打算做单点登录,因此研究了一些cas资料并作为一个系列记录下来,一来可能会帮助一些人,二来对我自己所学知识也是一个巩固.) 一.为什么要实现单点登录 随着信息化不断发展,企业的信息化过程是一 ...

  3. 【CAS单点登录视频教程】 第06集【完】 -- Cas认证 学习 票据认证FormsAuthentication

    目录 ----------------------------------------- [CAS单点登录视频教程] 第06集[完] -- Cas认证 学习 票据认证FormsAuthenticati ...

  4. 轻松入门CAS系列(1)-轻松看懂企业单点登录的解决方案

    常见的企业应用情况 企业内部的信息化一般都是一个过程中的 ,起初企业为了部分管理的需要,会上线几个信息化系统:后来对这块慢慢重视,信息系统会越来越多.开始,只有一两个系统时,员工还好,靠脑袋还能记得住 ...

  5. 【SSO单点系列】(2):CAS4.0 登录页的个性化定制

    上一篇 [SSO单点系列](1):CAS环境的搭建介绍了CAS最简单环境的搭建,以及一个例子用来讲解CAS的一个最基础的用法. 今天主要是介绍如何对CAS登录页进行个性化定制.    一.开始 下图是 ...

  6. 单点登录CAS使用记(一):前期准备以及为CAS-Server配置SSL协议

    知识点: SSO:单点登录(Single Sign On),是目前比较流行的企业业务整合的解决方案之一.SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统. CAS:耶 ...

  7. 单点登录CAS使用记(二):部署CAS服务器以及客户端

    CAS-Server下载地址:https://www.apereo.org/projects/cas/download-cas CAS-Client下载地址:http://developer.jasi ...

  8. 单点登录CAS使用记(三):实现自定义验证用户登录

    问题: CAS自带的用户验证逻辑太过简单,如何像正常网站一样,通过验证DB中的用户数据,来验证用户以及密码的合法性呢? 方案1:CAS默认的JDBC扩展方案: CAS自带了两种简单的通过JDBC方式验 ...

  9. 单点登录CAS使用记(四):为登录页面加上验证码

    CAS默认的登录页面样式如下,只有用户名与密码两项验证项目. 现在需要为首页登录加上验证码功能. 第一步:首页对默认登录页面的样式进行了调整,使其看上去还算美观. 在页面上加上了验证码项目. 第二步: ...

随机推荐

  1. win10下安装cygwin全过程

    简单讲:cygwin就是在windows系统上跑linux和unix的环境,跨平台移植的应用程序移植. 安装步骤: 下载cygwin: 打开官网https://cygwin.com/install.h ...

  2. 15 docker 网络 docker 容器之间的关系 docker link

    1.案例:使用 link 关联后台与数据库 创建 test1 容器 docker run -d --name test1 busybox /bin/sh -c "while true; do ...

  3. no.2淘宝架构背后——零售业务中台架构设计探讨及实践读后感

    2017年8月12日,袋鼠云首席架构师正风在“网易博学实践日:大数据与人工智能技术大会”进行<淘宝架构演进背后——零售业务中台架构设计探讨及实践>演讲分享.传统零售行业如何选择应对新经济模 ...

  4. Python——Pandas 时间序列数据处理

    介绍 Pandas 是非常著名的开源数据处理库,我们可以通过它完成对数据集进行快速读取.转换.过滤.分析等一系列操作.同样,Pandas 已经被证明为是非常强大的用于处理时间序列数据的工具.本节将介绍 ...

  5. set theory

    set theory Apart from classical logic, we assume the usual informal concept of sets. The reader (onl ...

  6. RDD(六)——分区器

    RDD的分区器 Spark目前支持Hash分区和Range分区,用户也可以自定义分区,Hash分区为当前的默认分区,Spark中分区器直接决定了RDD中分区的个数.RDD中每条数据经过Shuffle过 ...

  7. mysql琐碎操作杂记

    1.索引相关 查看表索引 show index from `user` 查看sql的执行计划 explain select * from where user 2.存储过程相关 查看存储过程 show ...

  8. mysql批量删除报1064原因

    DELETE FROM table_name  t where t......; 报1064 错误,原因MySQL 中delete 语句不能给表名起别名. 另外.如果记录不存在,delete from ...

  9. 关于Tarjan的一些问题

    $Q:$为什么tarjan求强连通分量要记录点是否在栈内,而求双连通分量不用? $A:$在有向图中存在横叉边,而无向图中不存在: $Q:$为什么用子搜索树中的$low[v]$来更新$low[k]$,而 ...

  10. MySQL安装教程及Navicat连接MySQL报错:1251-Client does not support authentication protocol requested by server

    MySQL安装可参考: MySql 8.0.18安装 此参考文章后面涉及到的密码修改,对本标题碰到的错误同样适用. 本文先讲如何安装,在讲碰到的1251问题.要直接看解决方案的朋友可以直接通过目录链接 ...