SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一。
当用户第一次访问应用系统1的时候,因为还没有登录,会被引导到认证系统中进行登录;根据用户提供的登录信息,认证系统进行身份校验,如果通过校验,应该返回给用户一个认证的凭据--ticket;用户再访问别的应用的时候就会将这个ticket带上,作为自己认证的凭据,应用系统接受到请求之后会把ticket送到认证系统进行校验,检查ticket的合法性。如果通过校验,用户就可以在不用再次登录的情况下访问应用系统2和应用系统3了。
要实现SSO,需要以下主要的功能:
系统共享
统一的认证系统是SSO的前提之一。认证系统的主要功能是将用户的登录信息和用户
信息库相比较,对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志(ticket),返还给用户。另外,认证系统还应该对ticket进行校验,判断其有效性。
信息识别
要实现SSO的功能,让用户只
登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对ticket进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过,从而完成
单点登录的功能。
另外:
1、单一的用户信息数据库并不是必须的,有许多系统不能将所有的用户信息都
集中存储,应该允许用户信息放置在不同的存储中,事实上,只要统一认证系统,统一ticket的产生和校验,无论用户信息存储在什么地方,都能实现
单点登录。
2、统一的认证系统并不是说只有单个的认证服务器
认证服务器之间要通过标准的通讯协议,互相交换认证信息,就能完成更高级别的
单点登录。如:当用户在访问应用系统1时,由第一个认证服务器进行认证后,得到由此服务器产生的ticket。当他访问应用系统2的时候,认证服务器2能够识别此ticket是由第一个服务器产生的,通过认证服务器之间标准的通讯协议(例如SAML)来交换认证信息,仍然能够完成SSO的功能。
web-sso:用户在访问页面1的时候进行了登录,但是
客户端的每个请求都是单独的连接,当客户再次访问页面2的时候,如何才能告诉Web服务器,客户刚才已经登录过了呢?
浏览器和服务器之间有约定:通过使用cookie技术来维护应用的状态。Cookie是可以被Web服务器设置的字符串,并且可以保存在
浏览器中。当
浏览器访问了页面1时,web服务器设置了一个cookie,并将这个cookie和页面1一起返回给浏览器,浏览器接到cookie之后,就会保存起来,在它访问页面2的时候会把这个cookie也带上,Web服务器接到请求时也能读出cookie的值,根据cookie值的内容就可以判断和恢复一些用户的信息状态。Web-SSO完全可以利用Cookie技术来完成用户登录信息的保存,将
浏览器中的Cookie和上文中的Ticket结合起来,完成SSO的功能。
为了完成一个简单的SSO的功能,需要两个部分的合作:
1、统一的
身份认证服务。
2、修改Web应用,使得每个应用都通过这个统一的认证服务来进行身份校验。
很多的网站都有用到SSO技术,
新浪的用户登录也是用到的SSO技术.
PS:以上内容来自百度百科。
言归正传。
首先要下载cas-client和cas-server资源,这些都系在官网和网上都很多,自己去下载吧。我下的的是cas-server-3.4.2.1和cas-client-3.2.1。
单点登录依赖证书,好在jdk可以帮我们生产,免去了花钱买证书的麻烦。如果你安装了jdk,并配置了环境变量,如下配置:
一、生产证书打开cmd输入: keytool -genkey -alias cassso -keyalg RSA -keystore D:/keys/sso.keystore,回车输入信息,成功后在d盘keys文件夹内多了一个sso.
keystore文件很好做的。自己去实现吧。
二、导出证书:keytool -export -file D:/keys/sso.crt -alias cassso -keystore D:/keys/sso.
keystore,回车,多了一个sso.crt文件。
三、导入到jdk中,不然后面会报
PKIX:unable to find valid certification path to requested target之类的错误。keytool -import -keystore %JAVA_HOME%\jre\lib\security\cacerts -file D:/keys/sso.crt -alias cassso,注意所有的别名要一致,比如我的就是cassso。不然不会成功的。
配置cas服务器,也就是把cas服务器加到tomcat容器内。我用的是解压缩版的tomcat。6.0.37版的,解压,复制三份,一份作为服务器apache-tomcat-6.0.37,另外两份是客户端apache-tomcat1和apache-tomcat2。
配置服务器:
到cas-server内的\modules文件夹内复制war到
apache-tomcat-6.0.37的webapps内,改下名字吧,不然比较长,我改为cas.war了。既然生成了证书,当然就要使用啦。修改apache-tomcat-6.0.37\conf下的server.xml文件,放开84行的节点,并添加证书引用。如下:
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="d:/keys/sso.keystore" keystorePass="cassso"
clientAuth="false" sslProtocol="TLS"/>
加了keystorefile和keystorepass属性,这是证书的路径和密码。
启动apache-tomcat-6.0.37的tomcat,输入https
://127.0.0.1:8443/cas/login,注意是https协议,为什么呢,请看上面的节点对应的scheme。
如果出现
那么恭喜你,至少cas服务器配置好了。输入任意用户名和密码一致的信息,登录即可。这是为什么呢?应该默认的cas服务器是用的是<bean
class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />这个bean验证的,实现代码如下:
class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />这个bean验证的,实现代码如下:
public final class SimpleTestUsernamePasswordAuthenticationHandler extends
AbstractUsernamePasswordAuthenticationHandler {
public SimpleTestUsernamePasswordAuthenticationHandler() {
log
.warn(this.getClass().getName()
+ " is only to be used in a testing environment. NEVER enable this in a production environment.");
}
public boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials) {
final String username = credentials.getUsername();
final String password = credentials.getPassword();
if (StringUtils.hasText(username) && StringUtils.hasText(password)
&& username.equals(getPasswordEncoder().encode(password))) {
log
.debug("User [" + username
+ "] was successfully authenticated.");
return true;
}
log.debug("User [" + username + "] failed authentication");
return false;
}
}
可以看到如何usrname和password相同就返回真啦。后面还讲呢。
下面就要配置cas客户端了。
当然还要依赖tomcat了,但是如果不做特殊处理,不可能同时启动多个tomcat的,这要如何修改的,打开startup.bat文件,如下
if "%OS%" == "Windows_NT" setlocal
rem ---------------------------------------------------------------------------
rem Start script for the CATALINA Server
rem
rem $Id: startup.bat 908749 2010-02-10 23:26:42Z markt $
rem ---------------------------------------------------------------------------
rem Guess CATALINA_HOME if not defined
set "CURRENT_DIR=%cd%"
if not "%CATALINA_HOME%" == "" goto gotHome
set "CATALINA_HOME=%CURRENT_DIR%"
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
cd ..
set "CATALINA_HOME=%cd%"
cd "%CURRENT_DIR%"
:gotHome
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
echo The CATALINA_HOME environment variable is not defined correctly
echo This environment variable is needed to run this program
goto end
:okHome
set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat"
rem Check that target executable exists
if exist "%EXECUTABLE%" goto okExec
echo Cannot find "%EXECUTABLE%"
echo This file is needed to run this program
goto end
:okExec
rem Get remaining unshifted command line arguments and save them in the
set CMD_LINE_ARGS=
:setArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
:doneSetArgs
call "%EXECUTABLE%" start %CMD_LINE_ARGS%
:end
发现tomcat的启动依赖CATALINA_HOME 环境变量和catalina.bat, catalina.bat内依赖CATALINA_BASE环境变量,所以要配置另外的CATALINA_HOME1和CATALINA_HOME2已经 CATALINA_BASE1和CATALINA_BASE2,分别指向对应的tomcat根目录即可。这里不再介绍。配置完这些后,还要配置对应的server.xml文件,修改如下内容:
<Server port="8005" shutdown="SHUTDOWN">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
三处,我分别在前面加上1和2,即18005,18443,18009和28005,28443和28009.
tomcat多启动配置好了以后,就要配置cas客户端了。当然,自己可以写一个项目去测试,为了方便就用tomcat下的examples的内容了。
打开apache-tomcat1\webapps\examples\WEB-INF,添加cas-client包下的cas-client-core-3.2.1.jar和commons-logging-1.1.jar到lib内,修改web.xml内容,添加内容如下。
<filter>
<filter-name>CasSingleSignOutFilter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<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>https://127.0.0.1:8443/cas/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://127.0.0.1:18080</param-value>
</init-param>
</filter>
<filter>
<filter-name>CasValidationFilter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>https://127.0.0.1:8443/cas</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://127.0.0.1:18080</param-value>
</init-param>
<init-param>
<param-name>useSession</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>redirectAfterValidation</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
</filter>
<!-- CAS:END -->
再次指定过滤器的拦截方式和监听
<!-- CAS:FILTER -->
<filter-mapping>
<filter-name>CasSingleSignOutFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CasAuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CasValidationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- CAS:END -->
<!-- Listener CAS LOGOUT -->
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
官方网站都有配置说明的。第一cas客户端就配置好了,第二个和第一个是一样的,改下18080为28080就行了。配置好后,启动服务器和客户端。登录cas服务器,https://127.0.0.1:8443/cas/login,成功后返回如下结果:
退出cas服务器,https://127.0.0.1:8443/cas/logout。并关闭cas服务器,即关闭tomcat。登录客户端,输入http://127.0.0.1:18080/examples/,会发现url地址跳转到了https://127.0.0.1:8443/cas/login?service=http%3A%2F%2F……了,提示无法访问,开启cas服务器,再次登录客户端,发现如下图:
输入用户名和密码,进入到如下页面:
请注意url的变化。
再登录http://127.0.0.1:28080/examples/发现直接进入了页面,如下图:
关闭cas服务器,先登录28080,会发现又会跳到cas服务器登录了,而18080则不需要登录了。这就完全说明了单点登录配置成功了。
当然可以和数据库配合起来,前面说到那个输入用户名和密码一致的登录现象。
修改cas服务器下的cas项目配置,修改cas\WEB-INF\deployerConfigContext.xml内容为
<!--这里是验证输入用户名和密码相同就登录而已,注销这个bean
<bean
class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />-->
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="dataSource" ref="dataSource"></property>
<property name="sql" value="select password from tb_user where userName=?"></property>
<property name="passwordEncoder" ref="MD5"></property>
</bean>
增加两外两个bean。
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
<property name="url"><value>jdbc:mysql:///charles</value></property>
<property name="username"><value>root</value></property>
<property name="password"><value>root</value></property>
</bean>
<bean id="MD5" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder">
<constructor-arg index="0"><value>MD5</value></constructor-arg>
</bean>
复制 cas-server包下 的cas-server-support-jdbc-3.4.2.1.jar和mysql驱动jar到cas项目的lib内,重启。
请注意,这里你的charles数据库要存在,并且存在表tb_user已经对应的字段,添加一条记录到tb_user表内,userName为admin,password为202cb962ac59075b964b07152d234b70,这个是123的md5值,重新登录,输入admin和admin发现错误信息为“
您提供的凭证有误。”输入admin和123,成功登录。
当然,这里还有好多知识没有涉及到,只是简单的模拟罢了,有兴趣的,可以把这些东西整合到自己的项目里,就可以实现单点登录了。
有不足的地方,请指出,我慢慢加以改正。