CAS实现的单点登录系统
单点登录(single sign on ),简称SSO。
纯属学习用,对来自网络的部分如果侵害了您的权力,请联系我。QQ:262800095
SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统,而不需要重新登录。
用一个现实中的例子做比较。颐和园是北京著名的旅游景点,也是我常去的地方。在颐和园内部有许多独立的景点,例如“苏州街”、“佛香阁”和“德和园”,都可以在各个景点门口单独买票。很多游客需要游玩所有德景点,这种买票方式很不方便,需要在每个景点门口排队买票,钱包拿进拿出的,容易丢失,很不安全。于是绝大多数游客选择在大门口买一张通票(也叫套票),就可以玩遍所有的景点而不需要重新再买票。他们只需要在每个景点门口出示一下刚才买的套票就能够被允许进入每个独立的景点。
单点登录的机制也一样,如下图所示,当用户第一次访问应用系统1的时候,因为还没有登录,会被引导到认证系统中进行登录(1);根据用户提供的登录信息,认证系统进行身份效验,如果通过效验,应该返回给用户一个认证的凭据--ticket(2);用户再访问别的应用的时候(3,5)就会将这个ticket带上,作为自己认证的凭据,应用系统接受到请求之后会把ticket送到认证系统进行效验,检查ticket的合法性(4,6)。如果通过效验,用户就可以在不用再次登录的情况下访问应用系统2和应用系统3了。
要实现SSO,需要实现以下主要的功能:
所有的应用系统共享一个身份认证系统。
统一的身份认证系统是SSO的前提,认证系统的主要功能是将用户的登录信息和用户信息库相比较,对用户进行登录认证。认证成功后,认证系统应该生成统一的认证标志(ticket),返还给用户。另外,认证系统还应该对ticket进行效验,判断其有效性。
所有应用系统能够识别和提取ticket信息
要实现SSO的功能,让用户只登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对ticket进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过,从而完成单点登录的功能。
以上内容都来自网络。
如何使用CAS实现单点登录
一、简介
CAS(Central Authentication Server中央验证系统)是耶鲁大学研发的单点登录系统。系统为了安装考虑默认是需要证书验证的。
本人使用的环境为:
apache-tomcat-6.0.30(原来用的是tomcat7,但中途遇到了8443端口无法验证的问题,怀疑是版本的原因,因此换成了tomcat6。PS:最后找出了原因是域名的问题,后面将会提到)。
JDK6
CAS Server版本:cas-server-3.4.2-release(下载地址:http://downloads.jasig.org/cas/)解压后,会发现一个modules文件夹,里面包含了配置CAS server所需要的jar包和war文件。
CAS Client版本:cas-client-3.2.1(下载地址:http://downloads.jasig.org/cas-clients/
)解压后,会发现一个moudules文件夹,里面包含了配置CAS Client所需要的jar包。
PS:选择低版本的客户端可能会出现少jar包的情况。
二、安装证书:
1、用JDK自带的keytool生成证书
keytool -genkey -alias smalllove -keyalg RSA -keystore D:/keys/smallkey
这一步尤其要注意的是对名字与姓氏的填写:不要写localhost或者其他的什么东西,最好是写域名,比如我输入localhost的话,后面8443端口就访问不到系统了,就这个问题我弄了一天时间才发现。所以,最好是输入一个域名,然后修改C:\Windows\System32\drivers\etc\hosts文件,在里面添加映射。
2、导出证书:
keytool -export -file d:/keys/small.crt -alias smalllove -keystore d:/keys/smallkey
3、将证书导入到JDK中
进入C:\Program Files\Java\jdk1.6.0_24\jre\lib\security目录
keytool -import -keystore cacerts -file D:/keys/small.crt -alias smalllove
此处如果某个文件夹包含了空格的话,绝对路径是不行的。刚开始我根据网上的资料,用绝对路劲来定位,总是出现错误,后来仔细看菜发现时我的Program Files目录里有空格。所以必须进入到这个目录下,否则会出现错误。
三、HTTPS访问CAS
将CAS Server解压出来的war文件部署到eclipse里面。
将tomcat的server.xml的配置修改一下,
<Connectorport="8443"protocol="HTTP/1.1"SSLEnabled="true"
maxThreads="150"scheme="https"secure="true"
clientAuth="false"sslProtocol="TLS"
keystoreFile="D:/keys/smallkey"
keystorePass="123456"/>
注意一定要改对地方,如果是用eclipse配置的tomcat就在对应的服务器的文件里改。比如我war文件的部署在tomcat v6.0 server at localhost服务器上,就应该改的tomcat v6.0 server at localhost-config里的server.xml。
好了,此时在浏览器里输入https://sso.wsria.com:8443/cas就会显示出如下页面
可以点击继续浏览或者将证书安装到信任区里。
然后输入相同的账户密码就OK。如果进入系统后显示登录成功页面,并且在地址栏里给出了seddionid=…说明CAS Service已经安装成功。否则的话自己找问题吧^_^。
四、客户端的配置。
完成了服务端,现在我们来解决客户端。
新建一个两个web工程myapp1和myapp2,然后修改其web.xml如下:
<?xmlversion="1.0"encoding="UTF-8"?>
<web-appxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"id="WebApp_ID"version="2.5">
<display-name>myapp2</display-name>
<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://sso.wsria.com:8443/cas/</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8082</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Authentication Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
我特地将myapp2放在了另外一个tomcat服务器里面,并改了端口。
引入jar包:cas-client-core-3.2.1.jar、commons-logging-1.1.jar。将这两个包复制到lib目录下即可,如果用build path 里的add external jar来加入这两个jar包就会提示class not found的错误。
此时,登录myapp1就会跳转到CAS Server的验证界面,输入相同的用户名密码,进入myapp1,然后再进入myapp2,此时就不需要验证了。
五、通过数据库和CAS系统一起实现单点登录
1、服务器的配置
我安装的是mysql5.5,这个就不用说了……安装完成之后,建立一个名为usr_info的数据库。然后建表并插入数据:
create table usr(name char(20) not null,password char(20) not null);
insert into usr values("test1" ,"123456");
insert into usr values("test2" ,"223456");
将cas-server-3.4.2里的modules里cas-server-support-jdbc-3.4.2.jar和jdbc连接mysql的mysql-connector-java-5.1.18-bin.jar复制到cas项目的WEB-INF的lib目录下。
修改WEB-INF目录下的deployerConfigContext.xml文件,将
<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />
屏蔽掉,然后加入以下配置文件:
<beanclass="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler"
abstract="false"lazy-init="default"autowire="default">
<propertyname="tableUsers">
<value>usr</value>
</property>
<propertyname="fieldUser">
<value>name</value>
</property>
<propertyname="fieldPassword">
<value>password</value>
</property>
<propertyname="dataSource"ref="dataSource"/>
</bean>
上面这段配置文件主要是添加对用户名和密码的校验工作,常用的有三种校验方式
l SimpleTestUsernamePasswordAuthenticationHandler
这个就是默认的简单的用户名密码校验,只要用户名和密码相同就能登录。
l QueryDatabaseAuthenticationHandler
这个是用select语句来验证,具体配置文件可以参考网上写的。我刚开始时用这种方法,但每次尽管输入的用户名和密码是正确的,还会提示failed to authenticate the user which provided the following credentials:test1,所以我后来改用了指定表和字段来连接。这是一个很容易让人很纠结的问题。
l SearchModeSearchDatabaseAuthenticationHandler
这个是通过指定表盒字段来连接数据库,也就是我的配置文件里选用的连接方法。
最后,在文件的末尾加上以下配置文件:
<beanid="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
<property name="url"><value>jdbc:mysql:///usr_info</value></property>
<property name="username"><value>root</value></property>
<property name="password"><value>123</value></property>
</bean>
<beanid="MD5PasswordEncoder"class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder">
<constructor-argindex="0">
<value>MD5</value>
</constructor-arg>
</bean>
上面的这段配置文件实际上就是配置数据源和加密算法,很容易理解。
2、客户端的配置
在myapp1和myapp2的web.xml配置文件里加入如下的filter
<!--该过滤器负责对Ticket的校验工作,必须启用它 -->
<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://sso.wsria.com:8443/cas</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8081</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这样就完成了客户端的配置。运行服务器和两个客户端,发现输入admin和admin已经不能登录系统。只有当输入用户名:test1,密码:123456时才能登录。
PS:CAS提供的所谓的内存cookie。也就是,如果将所有的浏览器关闭,那么CAS的cookie会自动消失,需要重新登录。OK 功能完成!
六、客户端接收参数,以及取得当前登录的用户名
如果我在myapp1系统里有如下超链接:
<a href="http://localhost:8082/myapp2/index.jsp?value=hello">进入myapp2,并携带参数value=hello </a>
当某个超链接携带参数经过CAS验证后,会不会使得参数丢失呢?答案是不会。并且,其接受参数的方法与没有集成CAS系统的方法一模一样。
那么,在客户端又应该如何获得当前登录用户的用户名呢?
<%@ page import="org.jasig.cas.client.util.AbstractCasFilter"%>
<%@ page import="org.jasig.cas.client.validation.Assertion"%>
<%
Assertion assertion1=(Assertion)session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
String username = assertion1.getPrincipal().getName();
%>
Usernam就是当前登录用户的用户名。
到这里就告一段落了先。
以后将会进一步研究如何在CAS里实现权限的管理,比如test1用户只对A系统和B系统有访问权限,而对C系统没有访问权限,那么test1在登录过A系统之后,进入B系统就不需要验证了,进入C系统的话还是需要验证。可现在是不管进入哪个系统都不需要验证。那又应该去如何设置呢?