`
lukejin
  • 浏览: 365458 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

CAS客户端证书认证登录

    博客分类:
  • Java
阅读更多

前端时间需要实现公司内网证书自动登录CAS.

由于对CAS的底层还不是特别了解所以学习了下,看了下源码.

 

这里我由上而下的讲解实现的过程.

 

1.Web Flow

我们都知道CAS目前使用了Spring Web Flow,

在CAS中Spring Web Flow的配置文件为login-webflow.xml

里面主要配置了登录的流程.这个如果用图来表示的话那应该是一个状态图,

一些节点会有一些判断然后会有不同的分支.

这里增加了startX509Authenticate这个节点,当需要登录的时候首先进入这个节点来验证,如果这里验证不成功的话才会进入普通的登录界面.负责直接登录成功,或者登录失败.

 

修改后的配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
    <flow xmlns="http://www.springframework.org/schema/webflow"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="
              http://www.springframework.org/schema/webflow
              http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd">

	<start-state idref="initialFlowSetup"/>

	<action-state id="initialFlowSetup">
		<action bean="initialFlowSetupAction" />
		<transition on="success" to="ticketGrantingTicketExistsCheck" />
	</action-state>
	
	<decision-state id="ticketGrantingTicketExistsCheck">
		<if test="${flowScope.ticketGrantingTicketId != null}" then="hasServiceCheck" else="gatewayRequestCheck" />
	</decision-state>
    
	<decision-state id="gatewayRequestCheck">
		<if test="${externalContext.requestParameterMap['gateway'] != '' &amp;&amp; externalContext.requestParameterMap['gateway'] != null &amp;&amp; flowScope.service != null}" then="redirect" else="startX509Authenticate" />
	</decision-state>
	
	<decision-state id="hasServiceCheck">
		<if test="${flowScope.service != null}" then="renewRequestCheck" else="viewGenericLoginSuccess" />
	</decision-state>
	
	<decision-state id="renewRequestCheck">
		<if test="${externalContext.requestParameterMap['renew'] != '' &amp;&amp; externalContext.requestParameterMap['renew'] != null}" then="startX509Authenticate" else="generateServiceTicket" />
	</decision-state>
	
	<decision-state id="warn">
		<if test="${flowScope.warnCookieValue}" then="showWarningView" else="redirect" />
	</decision-state>
	

	<action-state id="startX509Authenticate">
		<action bean="x509Check" />
		<transition on="success" to="sendTicketGrantingTicket" />
		<transition on="error" to="viewLoginForm" />
	</action-state>

	<view-state id="viewLoginForm" view="casLoginView">
			<render-actions>
			<action bean="authenticationViaFormAction" method="setupForm"/>
			<action bean="authenticationViaFormAction" method="referenceData"/>
		</render-actions>
		<transition on="submit" to="bindAndValidate" />
	</view-state>
	
	<action-state id="bindAndValidate">
		<action bean="authenticationViaFormAction" />
		<transition on="success" to="submit" />
		<transition on="error" to="viewLoginForm" />
	</action-state>
	
	<action-state id="submit">
		<action bean="authenticationViaFormAction" method="submit" />
		<transition on="warn" to="warn" />
		<transition on="success" to="sendTicketGrantingTicket" />
		<transition on="error" to="viewLoginForm" />
	</action-state>
	
	<action-state id="sendTicketGrantingTicket">
		<action bean="sendTicketGrantingTicketAction" />
		<transition on="success" to="serviceCheck" />
	</action-state>

	<decision-state id="serviceCheck">
		<if test="${flowScope.service != null}" then="generateServiceTicket" else="viewGenericLoginSuccess" />
	</decision-state>
	
	<action-state id="generateServiceTicket">
		<action bean="generateServiceTicketAction" />
		<transition on="success" to ="warn" />
		<transition on="error" to="viewLoginForm" />
		<transition on="gateway" to="redirect" />
	</action-state>

	<end-state id="viewGenericLoginSuccess" view="casLoginGenericSuccessView" />
	<end-state id="showWarningView" view="casLoginConfirmView" />
	<end-state id="redirect" view="bean:alibabaDynamicRedirectViewSelector" />
	<end-state id="viewServiceErrorView" view="viewServiceErrorView" />
        <end-state id="viewServiceSsoErrorView" view="viewServiceSsoErrorView" />

	<global-transitions>
		<transition to="viewServiceErrorView" on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException" />
        <transition to="viewServiceSsoErrorView" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException" />
		<transition to="viewServiceErrorView" on-exception="org.jasig.cas.services.UnauthorizedServiceException" />
	</global-transitions>
</flow>
 

 

2.从X509Check着手

从上面的配置文件我们可看出startX509Authenticate这个节点对应的Bean是x509Check

那么我们需要增加这样的一个Bean

我们在cas-servlet.xml这个配置文件中增加这个类的配置,因为CAS自带了证书认证的Action

<bean id="x509Check" class="org.jasig.cas.adaptors.x509.web.flow.X509CertificateCredentialsNonInteractiveAction"
		p:centralAuthenticationService-ref="centralAuthenticationService"/>

 这里不得不插进来说一下,在执行的时候是怎么个机制了.

 

当有一个请求过来之后,web flow安排对应节点的Action 来处理,

Action便通过它的centralAuthenticationService 来进行createTicketGrantingTicket,传递的参数是credentials.

centralAuthenticationService ,即org.jasig.cas.CentralAuthenticationServiceImpl这个类.

在颁发TGT之前先通过自己的authenticationManager来验证当前传递过来的credentials是否合法.

来到authenticationManager 的家中之后,使用什么来验证credentials呢,对了各种authenticationHandlers该上场了,这些authenticationHandlers 们也都是在配置authenticationManager的时候xml配置进去的.这里会选择一个能够处理当前credential的authenticationHandler来进行验证工作(authenticationHandler.supports(credentials)).也就是这些处理器都需要实现supports这个方法.

如果验证成功了,我们需要将一些标示信息带到cas的client端阿,那边需要这个信息.

所以验证之后,credentialsToPrincipalResolvers 们上场了,他们能够很好的带出需要传回给cas client的信息.

 

经过上面的分析,我们需要将我们自己的Handler和credentialsToPrincipalResolvers添加到authenticationManager的配置中

	<bean id="authenticationManager"
	class="org.jasig.cas.authentication.AuthenticationManagerImpl">
		<property name="credentialsToPrincipalResolvers">
			<list>
				<bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" />
				<bean class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />
				<bean class="org.jasig.cas.adaptors.x509.authentication.principal.X509CertificateCredentialsToDNEmailPrincipalResolver"/>
			</list>
		</property>
		<property name="authenticationHandlers">
			<list>
				<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
					p:httpClient-ref="httpClient" />
				<ref bean="dynamicAuthenticationHandler" />
				<ref bean="x509CredentialsAuthenticationHandler"/>
			</list>
		</property>
	</bean>

  x509CredentialsAuthenticationHandler这个CAS自带了,但是我们需要返回到CAS Client的为证书的邮箱,而CAS没有提供这样的Resolver,所以我自己写了一个从证书中取得email的X509CertificateCredentialsToDNEmailPrincipalResolver

 

当然这里引用到的类也需要在Spring中进行配置

	<bean id="x509CredentialsAuthenticationHandler" class="org.jasig.cas.adaptors.x509.authentication.handler.support.X509CredentialsAuthenticationHandler" >
	<property name="trustedIssuerDnPattern" value="CN=intranet.+"/>
	</bean>
 

这样便可以了.

 

3.Tomcat配置

 

接下来我们便需要将CA的证书加入到servlet容器的TrustStore中.并开启客户端认证.

	<Connector port="8447" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" disableUploadTimeout="true"
               acceptCount="100" scheme="https" secure="true"
               clientAuth="want" sslProtocol="TLS" 
               keystoreFile="conf/ssl/keystore.jks" keystorePass="changeit"
			   truststoreFile="conf/ssl/keystore.jks" truststorePass="changeit"
    />

 clientAuth="want"配置成want表示不强求,如果有则使用,配置成true的话那就必须通过证书,否则直接返回404

我们这里为了如果没有证书还可以进去普通登录页面,所以采取了want这个参数.

 

启动CAS ,一切OK了.

 

---------------------------------------------------------------------------------------------

PS:附上从证书中获取Email的代码

package org.jasig.cas.adaptors.x509.authentication.principal;

import java.security.cert.X509Certificate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.x509.X509NameTokenizer;

public class X509CertificateCredentialsToDNEmailPrincipalResolver extends AbstractX509CertificateCredentialsToPrincipalResolver {

    public static final String EMAIL = "rfc822name";
    public static final String EMAIL1 = "email";
    public static final String EMAIL2 = "EmailAddress";
    public static final String EMAIL3 = "E";
    private static final String[] EMAILIDS = { EMAIL, EMAIL1, EMAIL2, EMAIL3 };
    protected final Log log = LogFactory.getLog(this.getClass());

	@Override
	protected String resolvePrincipalInternal(X509Certificate certificate) {
		String email =  getEmailFromDN(certificate.getSubjectDN().getName());
		dynamicAuthenticationHandler.reload();
		if(email!=null){
			return email;
		}
		return null;
	}
	


	/**
     * Convenience method for getting an email address from a DN.
     * @param dn the DN
     * @return the found email address, or <code>null</code> if none is found
     */
    public  String getEmailFromDN(String dn) {
        log.info(">getEmailFromDN(" + dn + ")");
        String email = null;
        for (int i = 0; (i < EMAILIDS.length) && (email == null); i++) {
            email = getPartFromDN(dn, EMAILIDS[i]);
        }

        log.error("<getEmailFromDN(" + dn + "): " + email);

        return email;
    }
    
    /**
     * Gets a specified part of a DN. Specifically the first occurrence it the DN contains several
     * instances of a part (i.e. cn=x, cn=y returns x).
     *
     * @param dn String containing DN, The DN string has the format "C=SE, O=xx, OU=yy, CN=zz".
     * @param dnpart String specifying which part of the DN to get, should be "CN" or "OU" etc.
     *
     * @return String containing dnpart or null if dnpart is not present
     */
    public  String getPartFromDN(String dn, String dnpart) {
        log.debug(">getPartFromDN: dn:'" + dn + "', dnpart=" + dnpart);

        String part = null;

        if ((dn != null) && (dnpart != null)) {
            String o;
            dnpart += "="; // we search for 'CN=' etc.

            X509NameTokenizer xt = new X509NameTokenizer(dn);

            while (xt.hasMoreTokens()) {
                o = xt.nextToken();

                //log.debug("checking: "+o.substring(0,dnpart.length()));
                if ((o.length() > dnpart.length()) &&
                    o.substring(0, dnpart.length()).equalsIgnoreCase(dnpart)) {
                    part = o.substring(dnpart.length());

                    break;
                }
            }
        }

        log.debug("<getpartFromDN: resulting DN part=" + part);

        return part;
    } //getPartFromDN

}
 

 

分享到:
评论
4 楼 jackonline 2013-09-24  
貌似不能用了
3 楼 雁行 2012-11-13  
搭建环境后,感觉客户端证书client.cer没有被用到,原来是服务端没有开启,受教了
2 楼 musenl 2012-08-27  
dynamicAuthenticationHandler.reload()是哪个类的啊
1 楼 infante_yin 2010-08-19  
你好!可以配置多种证书吗?

相关推荐

    cas客户端登陆配置文档

    本配置文档将深入探讨如何配置CAS客户端以实现自定义登录和与服务端统一登录的功能。 首先,我们需要理解CAS的基本工作流程:用户尝试访问受保护的应用,被重定向到CAS服务器进行身份验证,成功后返回一个票据...

    CAS的ASP.NET客户端配置及验证

    在ASP.NET环境中配置CAS客户端可以帮助开发者构建一个允许用户通过单一登录凭证访问多个应用系统的平台。本篇文章将深入探讨如何在ASP.NET应用程序中配置CAS客户端,并进行验证流程。 首先,理解SSO的基本概念是至...

    落雨博客基于CAS框架的单点登录技术讲解(ppt+code实例+doc)配套资料

    [置顶] SSO单点登录系列2:cas客户端和cas服务端交互原理动画图解,cas协议终极分析 http://blog.csdn.net/ae6623/article/details/8848107 目 录 1 引言 4 1.1 摘要 4 1.2 范围 4 1.3 读者对象 4 1.4 关键词 4 2 ...

    cas_client.zip CAS认证中心客户端starter

    1. **CAS客户端组件**:CAS客户端是一个库,负责与CAS服务器通信,处理用户的登录请求、服务验证以及票证的生成与解析。在"cas_client.zip"中,`cac_client`可能包含了客户端的Java代码或配置文件,以便在你的应用中...

    CAS服务器端与客户端jar包(服务器端)

    2. **客户端库**:CAS客户端库是集成到各个应用系统中的组件,负责与CAS服务器通信,实现用户登录和登出功能。它通常包括一组Java过滤器,这些过滤器会拦截用户的请求,检查是否已通过CAS服务器验证。如果用户未登录...

    CAS客户端开发配置及其所需求的最基本的jar文件

    CAS(Central Authentication Service)是一种基于Web的单一...正确配置和使用CAS客户端库能有效解决这些问题,提供安全、便捷的认证体验。确保每个环节都符合安全标准,避免类似“即将定向不安全的连接”的警告出现。

    cas4.2.7 实现其他系统和cas互相认证互信

    3. **集成CAS客户端库**:在外部系统中,需要集成CAS客户端库,如Java CAS Client、Spring Security CAS等,它们负责处理与CAS服务器的交互,包括重定向用户到CAS登录页面、接收和验证服务票证。 4. **配置客户端...

    cas 普通方式和SpringBoot方式客户端 普通方式服务端

    在本文中,我们将探讨如何在普通方式和Spring Boot方式下配置和使用CAS客户端和服务端。 首先,让我们了解一下`CMD生成证书命令.txt`。在CAS部署中,安全通信通常依赖于SSL/TLS证书,用于加密传输数据。这个文件...

    cas客户端配置

    在本文中,我们将深入探讨如何配置CAS客户端,确保系统的安全性和正确性,特别是涉及到证书管理的部分。 首先,理解CAS客户端配置的基础概念至关重要。CAS客户端是应用程序的一部分,负责与CAS服务器进行交互,验证...

    cas单点登录服务器端和客户端的demo

    - **依赖添加**:在客户端项目中引入CAS客户端库。 - **服务定义**:配置应用的URL和服务ID,告知CAS服务器此应用需要认证。 - **CAS服务器地址设置**:配置CAS服务器的URL,用于跳转到登录页面和验证服务。 - **...

    cas3.5.2单点登录文档详细配置

    4. 可能需要修改CAS客户端的Java源代码,特别是对于证书验证的问题,可能需要重新编译并替换原有包。 5. 配置应用服务器,如Tomcat,以支持SSO认证过滤器。 6. 测试SSO功能,确保用户能够在不同应用间顺畅切换,无需...

    CAS系统使用证书和命令

    此外,对于分布式部署的CAS服务器,需要确保所有节点都使用相同的证书和密钥,以保持认证的一致性。 总结来说,CAS系统中的证书管理涉及证书的生成、导入、导出以及日常维护,这些操作都需要按照安全规范进行,以...

    cas单点登录 客户端

    1. **sf-framework-share-cas-1.7.8.jar**:这可能是自定义的CAS客户端扩展,包含特定于项目的需求或适配器。 2. **sf-framework-share-sso-2.1.jar**:可能是一个通用的SSO框架组件,提供更广泛的支持和集成。 3. *...

    cas单点登录服务端+客户端demo

    CAS(Central Authentication Service)是Java开发的一个开源的单点登录(Single Sign-On,简称SSO)框架,主要用于解决用户在多个应用系统间切换时需要多次认证的问题。它提供了一个集中式的登录入口,用户只需在一...

    cas-proxy认证

    CAS 支持多种认证机制,并允许客户端应用通过代理的方式进行认证,即所谓的“CAS-Proxy 认证”。本文档将详细介绍 CAS-Proxy 认证的基本原理、配置方法以及其实现过程中的关键点。 #### 二、CAS-Proxy 认证基础 ...

    cas单点登录客户端--c++版

    在C++中开发CAS客户端涉及到以下几个关键知识点: 1. **CAS协议理解**:首先,开发者需要了解CAS的基本协议,包括TCP/TLS通信、HTTP请求和响应以及XML解析。CAS协议通常涉及票证验证(Ticket Validation)流程,如...

    CAS认证登录简单介绍

    【CAS认证登录简单介绍】 CAS(Central Authentication Service)是一种广泛使用的单点登录(SSO)框架,由耶鲁大学开发并开源,旨在提供一个安全、简单且可扩展的身份验证解决方案。CAS的目标是允许用户在访问多个...

    cas client

    3. 登录与票证验证:当用户尝试访问受保护的资源时,CAS客户端会检查是否已经存在有效的服务票据(Service Ticket)。如果没有,它会将用户重定向到CAS服务器的登录页面。用户成功登录后,CAS服务器会返回一个票据,...

    liferay+cas实现单点登录步骤

    - 配置Liferay作为CAS客户端,确保它能够识别CAS服务器的票据(ticket)并完成用户认证。这通常涉及到添加或修改Liferay的portlet或主题配置,以支持CAS认证。 7. **测试与调整** - 完成上述配置后,测试Liferay...

    用cas实现mantis单点登录和登出

    2. **配置 CAS 客户端**:Mantis 需要集成 CAS 客户端库来实现单点登录。本文档采用的是 PHP 版本的 CAS 客户端。 ##### 第二步:Mantis 程序修改 1. **添加 CAS 客户端文件夹**: - 在 Mantis 根目录下新建一个...

Global site tag (gtag.js) - Google Analytics