Coin163

首页 > Apache CAS部署在tomcat上实现单点登录 - 博客频道 - CSDN.NET

Apache CAS部署在tomcat上实现单点登录 - 博客频道 - CSDN.NET

相关标签: apache cas tomcat

2020腾讯云8月秒杀活动,优惠非常大!(领取2860元代金券),
地址https://cloud.tencent.com/act/cps/redirect?redirect=1040

2020阿里云最低价产品入口,含代金券(新老用户有优惠),
入口地址https://www.aliyun.com/minisite/goods

相关推荐:CAS实现单点登录(SSO)经典完整教程 - 博客频道 - CSDN.NET

up vote 3 down vote favorite 2 When I use Fabric.js to generate canvas, there is annoying flickering when tapped with finger in iPad (mini) and iPhone (4). The flickering is like canvas goes fast to black and then white again. Steps to repr

单点登录(Single Sign On , 简称 SSO )是目前比较流行的服务于企业业务整合的解决方案之一, SSO 使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。CAS(Central Authentication Service)是一款不错的针对 Web 应用的单点登录框架,本文介绍了 CAS 的原理、协议、在 Tomcat 中的配置和使用,对于采用 CAS 实现轻量级单点登录解决方案的入门读者具有一定指导作用。

1.CAS介绍

CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。

CAS 具有以下特点:

(1)开源的企业级单点登录解决方案。

(2)CAS Server 为需要独立部署的 Web 应用。

(3)CAS Client 支持非常多的客户端(这里指单点登录系统中的各个 Web 应用),包括 Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等。

2.CAS原理和协议

从结构上看,CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server 需要独立部署,主要负责对用户的认证工作;CAS Client 负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。图1 是 CAS 最基本的协议过程:













                                       图1 CAS基础协议

CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求,CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket,如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户在第 3 步中输入认证信息,如果登录成功,CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket,并缓存以待将来验证,之后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新产生的 Ticket 过后,在第 5,6 步中与 CAS Server 进行身份合适,以确保 Service Ticket 的合法性。
在该协议中,所有与 CAS 的交互均采用 SSL 协议,确保,ST 和 TGC 的安全性。协议工作过程中会有 2 次重定向的过程,但是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的。
另外,CAS 协议中还提供了 Proxy (代理)模式,以适应更加高级、复杂的应用场景,具体介绍可以参考 CAS 官方网站上的相关文档。

3.准备工作

下载CAS Server:https://www.apereo.org/projects/cas/download-cas

下载CAS Client :http://developer.jasig.org/cas-clients/

下载Tomcat : 本文用的是Tomcat 8.0,自己去官网下载即可。

4. 生成https的证书文件

为了在Tomcat启用SSL,使其支持https访问,需要生产证书文件。

这里需要使用JDK安装包下的keytool工具生成证书文件keystore,以命令方式换到目录%TOMCAT_HOME%,在command命令行输入如下命令: 

keytool -genkey -alias tomcat_key -keyalg RSA -storepass changeit -keystore server.keystore -validity 36500

此时会提示用户输入姓名,组织等信息,如下所示,只输入localhost作为name,其余回车即可


这样就会在当前路径下生成一个名为server.keystore的证书文件,这个文件会在后面tomcat的server.xml配置文件中用到。

简单解释下这个命令的含义:

-genkey 生成证书指令

 -alias 证书的别名为tomcat_key 

-keyalg 生成证书的算法是RSA 

-storepass 证书的密码changeit 

-keystore 生成的证书文件是server.keystore 这里也可以指定具体的路径,如在Mac下:~/Document表示用户目录下的Document文件夹下

-validity 36500 证书的有效期,单位是天,这里指定的是100年


5.配置 Tomcat 使用 Https 协议

在Tomcat的server.xml配置文件中进行如下配置:

 <Connector SSLEnabled="true" clientAuth="false" 
			   maxThreads="150" port="8443" 
			   protocol="org.apache.coyote.http11.Http11NioProtocol" 
			   scheme="https" secure="true" sslProtocol="TLS"
			   keystoreFile="/Users/cruise/Documents/ISG/apache-tomcat-8.0.32/server.keystore" 
   			   keystorePass="changeit" />

这里的keystoreFile需要指定上一步生成的证书文件,keystorePass对应生成文件的时候设置的密码。

6.部署CAS Server到Tomcat

(1)到cas官网下载cas-server http://developer.jasig.org/cas/(我下载的是4.0.0)

(2)解压压缩文件,在解压后的文件夹内找到/modules/cas-server-webapp-4.0.0.war。将其复制到%Tomcat_Home%\webapps下并改名为cas.war

(3)启动Tomcat,并测试 https://localhost:8443/cas 看是否访问正常(默认输入用户名和密码一致就可以)。 

相关推荐:CAS实现单点登录(SSO)经典完整教程

    一、简介                 1、cas是有耶鲁大学研发的单点登录服务器                  2、本教材所用环境 Tomcat7.2 JDK6 CAS Service 版本    cas-server-3.4.8-release CAS Client版本      cas-client-3.2.1-release                                

CAS Server 4.0.0 默认登陆验证方式是 AcceptUsersAuthenticationHandler (老版本好像是SimpleTestUsernamePasswordAuthenticationHandler),默认用户名/密码为 casuser/Mellon(cas/WEB-INF/deployerConfigContext.xml 中找到 id=primaryAuthenticationHandler 的bean查看,里面的map也可以自己增加更多个)。我们通常需要从数据库中取出用户名和密码进行验证,所以我们需要修改 deployerConfigContext.xml,配置我们自己的服务认证方式。   

虽然 CAS Server 已经部署成功,但这只是一个缺省的实现,在实际使用的时候,还需要根据实际概况做扩展和定制,最主要的是扩展认证 (Authentication) 接口和 CAS Server 的界面。

7. 扩展认证接口

CAS Server 负责完成对用户的认证工作,它会处理登录时的用户凭证 (Credentials) 信息,用户名/密码对是最常见的凭证信息。CAS Server 可能需要到数据库检索一条用户帐号信息,也可能在 XML 文件中检索用户名/密码,还可能通过 LDAP Server 获取等,在这种情况下,CAS 提供了一种灵活但统一的接口和实现分离的方式,实际使用中 CAS 采用哪种方式认证是与CAS的基本协议分离开的,用户可以根据认证的接口去定制和扩展。
扩展 AuthenticationHandler
CAS 提供扩展认证的核心是 AuthenticationHandler 接口,该接口定义如清单 1 下:

清单 1. AuthenticationHandler定义

public interface AuthenticationHandler {
    /**
     * Method to determine if the credentials supplied are valid.
     * @param credentials The credentials to validate.
     * @return true if valid, return false otherwise.
     * @throws AuthenticationException An AuthenticationException can contain
     * details about why a particular authentication request failed.
     */
    boolean authenticate(Credentials credentials) throws AuthenticationException;
/**
     * Method to check if the handler knows how to handle the credentials
     * provided. It may be a simple check of the Credentials class or something
     * more complicated such as scanning the information contained in the
     * Credentials object. 
     * @param credentials The credentials to check.
     * @return true if the handler supports the Credentials, false othewrise.
     */
    boolean supports(Credentials credentials);
}

该接口定义了 2 个需要实现的方法,supports ()方法用于检查所给的包含认证信息的Credentials 是否受当前 AuthenticationHandler 支持;而 authenticate() 方法则担当验证认证信息的任务,这也是需要扩展的主要方法,根据情况与存储合法认证信息的介质进行交互,返回 boolean 类型的值,true 表示验证通过,false 表示验证失败。
CAS3中还提供了对AuthenticationHandler 接口的一些抽象实现,比如,可能需要在执行authenticate() 方法前后执行某些其他操作,那么可以让自己的认证类扩展自清单 2 中的抽象类:
清单 2. AbstractPreAndPostProcessingAuthenticationHandler定义

public abstract class AbstractPreAndPostProcessingAuthenticationHandler 
                               implements AuthenticateHandler{
    protected Log log = LogFactory.getLog(this.getClass());
    protected boolean preAuthenticate(final Credentials credentials) {
        return true;
    }
    protected boolean postAuthenticate(final Credentials credentials,
        final boolean authenticated) {
        return authenticated;
    }
    public final boolean authenticate(final Credentials credentials)
        throws AuthenticationException {
        if (!preAuthenticate(credentials)) {
            return false;
        }
        final boolean authenticated = doAuthentication(credentials);
        return postAuthenticate(credentials, authenticated);
    }
    protected abstract boolean doAuthentication(final Credentials credentials) throws AuthenticationException;
}
AbstractPreAndPostProcessingAuthenticationHandler 类新定义了 preAuthenticate() 方法和 postAuthenticate() 方法,而实际的认证工作交由 doAuthentication() 方法来执行。因此,如果需要在认证前后执行一些额外的操作,可以分别扩展 preAuthenticate()和 ppstAuthenticate() 方法,而 doAuthentication() 取代 authenticate() 成为了子类必须要实现的方法。
由于实际运用中,最常用的是用户名和密码方式的认证,CAS3 提供了针对该方式的实现,如清单 3 所示:
清单 3. AbstractUsernamePasswordAuthenticationHandler 定义

public abstract class AbstractUsernamePasswordAuthenticationHandler extends 
                       AbstractPreAndPostProcessingAuthenticationHandler{
    ...
    protected final boolean doAuthentication(final Credentials credentials)
     throws AuthenticationException {
        return authenticateUsernamePasswordInternal((UsernamePasswordCredentials) credentials);
    }
    protected abstract boolean authenticateUsernamePasswordInternal(
        final UsernamePasswordCredentials credentials) throws AuthenticationException;   
    protected final PasswordEncoder getPasswordEncoder() {
         return this.passwordEncoder;
     }
    public final void setPasswordEncoder(final PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }
    ...
}


基于用户名密码的认证方式可直接扩展自 AbstractUsernamePasswordAuthenticationHandler,验证用户名密码的具体操作通过实现 authenticateUsernamePasswordInternal() 方法达到,另外,通常情况下密码会是加密过的,setPasswordEncoder() 方法就是用于指定适当的加密器。
从以上清单中可以看到,doAuthentication() 方法的参数是 Credentials 类型,这是包含用户认证信息的一个接口,对于用户名密码类型的认证信息,可以直接使用 UsernamePasswordCredentials,如果需要扩展其他类型的认证信息,需要实现Credentials接口,并且实现相应的 CredentialsToPrincipalResolver 接口,其具体方法可以借鉴 UsernamePasswordCredentials 和 UsernamePasswordCredentialsToPrincipalResolver。

8.实现JDBC的认证

用户的认证信息通常保存在数据库中,因此本文就选用这种情况来介绍。将前面下载的 cas-server-{version}-release.zip 包解开后,在 modules 目录下可以找到包 cas-server-support-jdbc-{version}.jar,其提供了通过 JDBC 连接数据库进行验证的缺省实现,基于该包的支持,我们只需要做一些配置工作即可实现 JDBC 认证。

JDBC 认证方法支持多种数据库,DB2, Oracle, MySql, Microsoft SQL Server 等均可,这里以Mysql作为例子介绍。并且假设Mysql数据库名:test,数据库登录用户名: root,数据库登录密码:password,用户信息表为: t_user,该表包含用户名和密码的两个数据项分别为 username 和 password。用户表数据如下图所示:



1)配置DataSource

打开文件 cas/WEB-INF/deployerConfigContext.xml,添加一个新的 bean 标签,以MYSQL为例,内容如清单 4 所示:

    <!-- Data source definition --> 
    <bean id="casDataSource" class="org.apache.commons.dbcp.BasicDataSource"> 
        <property name="driverClassName"> 
            <value>com.mysql.jdbc.Driver</value> 
        </property> 
        <property name="url"> 
            <value>jdbc:mysql://localhost:3306/test</value> 
        </property> 
        <property name="username"> 
            <value>root</value> 
        </property> 
        <property name="password"> 
            <value>passw0rd</value> 
        </property>
    </bean>
其中 casDataSource 在后面配置 AuthenticationHandler 会被引用,添加数据库驱动程序、连接地址、数据库登录用户名以及登录密码。

2)配置 AuthenticationHandler

CAS 4.0.0 提供了 3 个基于 JDBC 的 AuthenticationHandler,分别为 BindModeSearchDatabaseAuthenticationHandler, QueryDatabaseAuthenticationHandler, SearchModeSearchDatabaseAuthenticationHandler。 
- BindModeSearchDatabaseAuthenticationHandler 是用所给的用户名和密码去建立数据库连接,根据连接建立是否成功来判断验证成功与否; 
- QueryDatabaseAuthenticationHandler 通过配置一个 SQL 语句查出密码,与所给密码匹配; 
- SearchModeSearchDatabaseAuthenticationHandler 通过配置存放用户验证信息的表、用户名字段和密码字段,构造查询语句来验证。

使用哪个 AuthenticationHandler,需要在 deployerConfigContext.xml 中设置,默认情况下,CAS 4.0.0 使用 AcceptUsersAuthenticationHandler(上面已经提到,需要在 deployerConfigContext.xml 中查看)。

在 deployerConfigContext.xml 文件中可以找到如下配置:

<bean id="primaryAuthenticationHandler"
        class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
   <property name="users">
       <map>
          <entry key="casuser" value="Mellon"/>
       </map>
   </property>
</bean> 

这里我们注释掉这段配置,然后换成我们希望的一个 AuthenticationHandler,比如,使用QueryDatabaseAuthenticationHandler 或 SearchModeSearchDatabaseAuthenticationHandler 可以分别选取清单 5 或清单 6 的配置。 
清单 5. 使用 QueryDatabaseAuthenticationHandler

<bean id="primaryAuthenticationHandler" 
        class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
        <property name="dataSource" ref="casDataSource" />
        <property name="sql" value="select password from t_user where lower(username) = lower(?)" />
        <!-- 指定密码加密器(可选) -->
        <!-- <property name="passwordEncoder" ref="passwordEncoder" /> -->
</bean>
清单 6. 使用 SearchModeSearchDatabaseAuthenticationHandler

<bean id="SearchModeSearchDatabaseAuthenticationHandler"
    class="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler"
    abstract="false" singleton="true" lazy-init="default" 
    autowire="default" dependency-check="default">
    <property name="tableUsers">
        <value>userTable</value>
    </property>
    <property name="fieldUser">
        <value>userName</value>
    </property>
    <property name="fieldPassword">
        <value>password</value>
    </property>
    <property name="dataSource" ref="casDataSource " />
</bean>
另外,由于存放在数据库中的密码通常是加密过的,所以 AuthenticationHandler 在匹配时需要知道使用的加密方法,在 deployerConfigContext.xml 文件中我们可以为具体的 AuthenticationHandler 类配置一个 property,指定加密器类,比如对于 QueryDatabaseAuthenticationHandler,可以修改如清单7所示:
清单 7. QueryDatabaseAuthenticationHandler配置加密器类

<!-- 密码加密器(可以指定自己实现的加密器) -->
<bean id="passwordEncoder"
    class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder" >
    <constructor-arg name="encodingAlgorithm" value="MD5"/>
    <property name="characterEncoding" value="UTF-8"/>
</bean>
本文这里由于未对数据库中的用户数据进行加密,所以在清单5中将配置的加密属性注释掉。

注意:DataSource 依赖于 commons-collections-3.2.jar、commons-dbcp-1.2.1.jar、commons-pool-1.3.jar、数据库驱动包、cas对jdbc的支持包cas-server-support-jdbc-4.0.0.jar,需要找到这几个jar包放进 %TOMCAT_HOME%/webapps/cas/WEB-INF/lib 目录。

然后便可以启动Tomcat使用数据库中的用户名和密码进行登录测试了。
注意:DataSource 依赖于 commons-collections-3.2.jar、commons-dbcp-1.2.1.jar、commons-pool-1.3.jar、数据库驱动包、cas对jdbc的支持包cas-server-support-jdbc-4.0.0.jar,需要找到这几个jar包放进 %TOMCAT_HOME%/webapps/cas/WEB-INF/lib 目录。

然后便可以启动Tomcat使用数据库中的用户名和密码进行登录测试了。

9.部署CAS客户端应用

单点登录的目的是为了让多个相关联的应用使用相同的登录过程,本文在讲解过程中构造 2个简单的应用,分别以 CASClient1 和 CASClient2 来作为示例,它们均只有一个页面,显示欢迎信息和当前登录用户名。这两个应用使用同一套登录信息,并且只有登录过的用户才能访问,通过本文的配置,实现单点登录,即只需登录一次就可以访问这两个应用。应用的工程结构很简单,引入必要的jar包,然后新建一个index.jsp页面。


index.jsp代码清单

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>CASClient1's index.jsp</title>
</head>
<body>
<% 
/*获取登录用户名*/
String username = request.getRemoteUser();

%>
current user: <%=username %>
<p><a href="https://localhost:8443/cas/logout">logout</a></p>
</body>
</html>

10.配置CAS客户端Filter

在CAS的client端的web.xml文件中配置过滤器如下:

<!-- 该过滤器用于实现单点登出功能,可选配置。 -->
<!-- 登出地址 https://casserver:8443/cas/logout -->
<filter>
	<filter-name>CAS Single Sign Out Filter</filter-name>
	<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>

<!-- 该过滤器负责用户的认证工作,必须启用它 -->
<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://localhost:8443/cas/login</param-value>
	</init-param>
	<init-param>
		<param-name>serverName</param-name>
		<param-value>http://localhost:8080</param-value>
	</init-param>
</filter>

<!-- 该过滤器负责对Ticket的校验工作,必须启用它 -->
<filter>
	<filter-name>CAS Validation Filter</filter-name>
	<filter-class>org.jasig.cas.client.validation.Cas10TicketValidationFilter</filter-class>
	<init-param>
		<param-name>casServerUrlPrefix</param-name>
		<param-value>https://localhost:8443/cas</param-value>
	</init-param>
	<init-param>
		<param-name>serverName</param-name>
		<param-value>http://localhost:8080</param-value>
	</init-param>
	<init-param>
		<param-name>redirectAfterValidation</param-name>
		<param-value>true</param-value>
	</init-param>
</filter>

<!-- 该过滤器负责实现HttpServletRequest请求的包装, 比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 -->
<filter>
	<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
	<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
</filter>

<!-- 该过滤器使得开发者可以通过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 Single Sign Out Filter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
	<filter-name>CAS Authentication Filter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
	<filter-name>CAS Validation Filter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
	<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
	<filter-name>CAS Assertion Thread Local Filter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

<listener>
	<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>

运行Client工程,首次访问任一页面就会跳转到https://localhost:8443/cas/login进行认证。 

把你的退出链接设置为:https://localhost:8443/cas/logout 即可实现单点登出,如果需要在系统退出后返回指定页面,我们可以修改源码在logout后面增加参数来实现。

11.单点登录测试同时运行CAS server、 CASClient1 和CASClient2。分别访问下面的URL:

CASClient1: localhost:8080/CASClient1/index.jsp

CASClient2: localhost:8080/CASClient2/index.jsp

当第一次访问上面的URL时,会重定向到下面的页面:


这时输入数据库中的用户名和对应的密码登录,登录成功后会看到如下页面:


此时访问第二个URL发现index页面会直接打开,而不用再次登录,因为访问CASClient1的时候已经认证过了。这就实现了所谓的单点登录。

原文

单点登录(Single Sign On , 简称 SSO )是目前比较流行的服务于企业业务整合的解决方案之一, SSO 使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。CAS(Central A

------分隔线----------------------------