Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
--- ---------------------------------------------------------------------
5 host/auth.langhua@AUTH.LANGHUA
5 host/auth.langhua@AUTH.LANGHUA

...

Code Block
title/cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/BindLdapGssapiAuthenticationHandler.java
borderStylesolid
/*
 * Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
 * distributed with this file and available online at
 * http://www.ja-sig.org/products/cas/overview/license/
 */
package org.jasig.cas.adaptors.ldap;

import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.jasig.cas.util.annotation.IsIn;

/**
 * Handler to do LDAP GSSAPI bind.
 *
  * @author Scott Battaglia
 * @version $Revision: 42053 $ $Date: 2007-06-10 09:17:55 -0400 (Sun, 10 Jun 2007) $
 * @since 3.0.3
 * @author Shi Yusen, shiys@langhua.cn
 */
public class BindLdapGssapiAuthenticationHandler extends
    AbstractLdapGssapiAuthenticationHandler {

    /** The default maximum number of results to return. */
    private static final int DEFAULT_MAX_NUMBER_OF_RESULTS = 1000;

    /** The default timeout. */
    private static final int DEFAULT_TIMEOUT = 1000;

    /** The search base to find the user under. */
    private String searchBase;

    /** The scope. */
    @IsIn({SearchControls.OBJECT_SCOPE, SearchControls.ONELEVEL_SCOPE,
        SearchControls.SUBTREE_SCOPE})
    private int scope = SearchControls.SUBTREE_SCOPE;

    /** The maximum number of results to return. */
    private int maxNumberResults = DEFAULT_MAX_NUMBER_OF_RESULTS;

    /** The amount of time to wait. */
    private int timeout = DEFAULT_TIMEOUT;

    /** Boolean of whether multiple accounts are allowed. */
    private boolean allowMultipleAccounts;

    protected final boolean authenticateUsernamePasswordInternal(
        final UsernamePasswordCredentials credentials)
        throws AuthenticationException {

        DirContext context;

                context = this.getContextSource().getDirContext(credentials.getUsername(), credentials.getPassword());
      

         if (context == null) {
        	return false;
        }

                return true;
    }

    protected String composeCompleteDnToCheck(final String dn,
        final UsernamePasswordCredentials credentials) {
        return dn + "," + this.searchBase;
    }

    /**
     * Method to return whether multiple accounts are allowed.
     * @return true if multiple accounts are allowed, false otherwise.
     */
    protected boolean isAllowMultipleAccounts() {
        return this.allowMultipleAccounts;
    }

    /**
     * Method to return the max number of results allowed.
     * @return the maximum number of results.
     */
    protected int getMaxNumberResults() {
        return this.maxNumberResults;
    }

    /**
     * Method to return the scope.
     * @return the scope
     */
    protected int getScope() {
        return this.scope;
    }

    /**
     * Method to return the search base.
     * @return the search base.
     */
    protected String getSearchBase() {
        return this.searchBase;
    }

    /**
     * Method to return the timeout. 
     * @return the timeout.
     */
    protected int getTimeout() {
        return this.timeout;
    }

    public final void setScope(final int scope) {
        this.scope = scope;
    }

    /**
     * @param allowMultipleAccounts The allowMultipleAccounts to set.
     */
    public void setAllowMultipleAccounts(final boolean allowMultipleAccounts) {
        this.allowMultipleAccounts = allowMultipleAccounts;
    }

    /**
     * @param maxNumberResults The maxNumberResults to set.
     */
    public final void setMaxNumberResults(final int maxNumberResults) {
        this.maxNumberResults = maxNumberResults;
    }

    /**
     * @param searchBase The searchBase to set.
     */
    public final void setSearchBase(final String searchBase) {
        this.searchBase = searchBase;
    }

    /**
     * @param timeout The timeout to set.
     */
    public final void setTimeout(final int timeout) {
        this.timeout = timeout;
    }

}

2. Create /cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/AbstractLdapGssapiAuthenticationHandler.java

Code Block
title/cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/AbstractLdapGssapiAuthenticationHandler.java
borderStylesolid
/*
 * Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
 * distributed with this file and available online at
 * http://www.ja-sig.org/products/cas/overview/license/
 */
package org.jasig.cas.adaptors.ldap;

import org.jasig.cas.adaptors.ldap.util.AuthenticatedLdapGssapiContextSource;
import org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.jasig.cas.util.annotation.NotNull;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.ldap.LdapTemplate;
import org.springframework.util.Assert;

/**
 * Abstract class to handle common LDAP functionality.
 * 
 * @author Scott Battaglia
 * @version $Revision: 42053 $ $Date: 2007-06-10 09:17:55 -0400 (Sun, 10 Jun 2007) $
 * @since 3.0.3
 * 
 * @author Shi Yusen, shiys@langhua.cn
 */
public abstract class AbstractLdapGssapiAuthenticationHandler extends
    AbstractUsernamePasswordAuthenticationHandler implements InitializingBean {

    /** LdapTemplate to execute ldap queries. */
    @NotNull
    private LdapTemplate ldapTemplate;
  

     /** Instance of ContextSource */
    @NotNull
    private AuthenticatedLdapGssapiContextSource contextSource;

    /** The filter path to the uid of the user. */
    @NotNull
    private String filter;

        /** Whether the LdapTemplate should ignore partial results. */
    private boolean ignorePartialResultException = false;

    /**
     * Method to set the datasource and generate a JdbcTemplate.
     * 
     * @param dataSource the datasource to use.
     */
    public final void setContextSource(final AuthenticatedLdapGssapiContextSource contextSource) {
        this.contextSource = contextSource;
        this.ldapTemplate = new LdapTemplate(contextSource);
    }


       public final void setIgnorePartialResultException(final boolean ignorePartialResultException) {
        this.ignorePartialResultException = ignorePartialResultException;
    }

    /**
     * Method to return the LdapTemplate
     * 
     * @return a fully created LdapTemplate.
     */
    protected final LdapTemplate getLdapTemplate() {
        return this.ldapTemplate;
    }

    protected final AuthenticatedLdapGssapiContextSource getContextSource() {
        return this.contextSource;
    }

    protected final String getFilter() {
        return this.filter;
    }

    public final void afterPropertiesSet() throws Exception {
        Assert.isTrue(this.filter.contains("%u"), "filter must contain %u");
        this.ldapTemplate.setIgnorePartialResultException(this.ignorePartialResultException);
    }

    /**
     * @param filter The filter to set.
     */
    public final void setFilter(final String filter) {
        this.filter = filter;
    }
}

3. Create /cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/util/AuthenticatedLdapGssapiContextSource.java

Code Block
title/cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/util/AuthenticatedLdapGssapiContextSource.java
borderStylesolid
/*
 * Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
 * distributed with this file and available online at
 * http://www.ja-sig.org/products/cas/overview/license/
 */
package org.jasig.cas.adaptors.ldap.util;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.ldap.support.LdapContextSource;

import sun.security.krb5.Credentials;

public class AuthenticatedLdapGssapiContextSource extends LdapContextSource {
	private DirContext context;
	
	public Hashtable<String, String> environment;
  

     public DirContext getDirContext(final String principal, final String credentials) {

   	     	if (context == null) {
            environment = (Hashtable) getAnonymousEnv().clone();

            Enumeration keys = environment.keys();
            while (keys.hasMoreElements())
            {
                String key = (String)keys.nextElement();
                System.setProperty(key, environment.get(key));
            }
          

             environment.put(Context.SECURITY_PRINCIPAL, principal);
            environment.put(Context.SECURITY_CREDENTIALS, credentials);

                        LoginContext lc = null;
            try {
                lc = new LoginContext(AuthenticatedLdapGssapiContextSource.class.getName(),
                        new UsernamePasswordCallbackHandler(principal, credentials));
                lc.login();
            } catch (LoginException ex) {
                ex.printStackTrace();
                throw new DataAccessResourceFailureException("GSSAPI login problem: " + ex);
            }

            context = (DirContext) Subject.doAs(lc.getSubject(), new LDAPAction(environment));

            if (context == null) {
                throw new DataAccessResourceFailureException("a problem with GSSAPI occurred - couldn't create a GSSAPI directory context");
            } else {
            	Iterator i = lc.getSubject().getPrivateCredentials().iterator();
            	while (i.hasNext()) {
            		Credentials creds = (Credentials) i.next();
            		if (creds.isForwardable()) {
                    	return context;
            		}
            	}
                throw new DataAccessResourceFailureException("a problem with GSSAPI occurred - please make sure your Credentials is forwardable. Try to use \"kinit -f yourpricinpal\".");
            }
    	} else {
    		return context;
    	}
    }

    /**
     * A simple JAAS CallbackHandler which accepts a Name String and Password
     * String in the constructor. Only NameCallbacks and PasswordCallbacks are
     * accepted in the callback array. This code based loosely on example given
     * in Sun's javadoc for CallbackHandler interface.
     * 
     * Copied from org.jasig.cas.authentication.handler.support.JaasAuthenticationHandler
     */
    protected class UsernamePasswordCallbackHandler implements CallbackHandler {

        /** The username of the principal we are trying to authenticate. */
        private final String userName;

        /** The password of the principal we are trying to authenticate. */
        private final String password;

        /**
         * Constuctor accepts name and password to be used for authentication.
         * 
         * @param userName name to be used for authentication
         * @param password Password to be used for authentication
         */
        protected UsernamePasswordCallbackHandler(final String userName,
            final String password) {
            this.userName = userName;
            this.password = password;

        }

        public void handle(final Callback[] callbacks)
            throws UnsupportedCallbackException {
            for (int i = 0; i < callbacks.length; i++) {
                final Callback callback = callbacks[i];

                if (callback.getClass().equals(NameCallback.class)) {
                    ((NameCallback) callback).setName(this.userName);
                } else if (callback.getClass().equals(PasswordCallback.class)) {
                    ((PasswordCallback) callback).setPassword(this.password
                        .toCharArray());
                } else {
                    throw new UnsupportedCallbackException(callback,
                        "Unrecognized Callback");
                }
            }
        }
    }
}

4. Create /cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/util/LDAPAction.java

Code Block
title/cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/util/LDAPAction.java
borderStylesolid
/*
 * Copyright 2008 The JA-SIG Collaborative. All rights reserved. See license
 * distributed with this file and available online at
 * http://www.ja-sig.org/products/cas/overview/license/
 */
package org.jasig.cas.adaptors.ldap.util;

import java.security.PrivilegedAction;
import java.util.Hashtable;

import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

/**
 * @author Shi Yusen
 *
  * Ref: http://forum.java.sun.com/thread.jspa?threadID=395518&messageID=2444444
 */
public class LDAPAction implements PrivilegedAction {
	
	private Hashtable env;
	
	public LDAPAction(Hashtable env){
		this.env = env;
	}

	/* (non-Javadoc)
	 * @see java.security.PrivilegedAction#run()
	 */
	public Object run() {
		
		DirContext result = null;
		
		try{
			result = new InitialDirContext(env);
		}catch(NamingException ex){
			ex.printStackTrace();
		}
		
		return result;
	}

}

5. build and deploy CAS 3.1

6. Create $CAS/WEB-INF/gssapi.conf

Code Block
title$CAS/WEB-INF/gssapi.conf
borderStylesolid
org.jasig.cas.adaptors.ldap.util.AuthenticatedLdapGssapiContextSource {
  com.sun.security.auth.module.Krb5LoginModule required client=TRUE
  														useTicketCache=TRUE;
};

7. Edit $CAS/WEB-INF/deployerConfigContext.xml

Code Block
title$CAS/WEB-INF/deployerConfigContext.xml
borderStylesolid
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC  "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
	| deployerConfigContext.xml centralizes into one file some of the declarative configuration that
	| all CAS deployers will need to modify.
	|
	| This file declares some of the Spring-managed JavaBeans that make up a CAS deployment.  
	| The beans declared in this file are instantiated at context initialization time by the Spring

	| ContextLoaderListener declared in web.xml.  It finds this file because this
	| file is among those declared in the context parameter "contextConfigLocation".
	|
	| By far the most common change you will need to make in this file is to change the last bean
	| declaration to replace the default SimpleTestUsernamePasswordAuthenticationHandler with
	| one implementing your approach for authenticating usernames and passwords.
	+-->
<beans>
	<!--
		| This bean declares our AuthenticationManager.  The CentralAuthenticationService service bean
		| declared in applicationContext.xml picks up this AuthenticationManager by reference to its id,

		| "authenticationManager".  Most deployers will be able to use the default AuthenticationManager
		| implementation and so do not need to change the class of this bean.  We include the whole
		| AuthenticationManager here in the userConfigContext.xml so that you can see the things you will
		| need to change in context.
		+-->
	<bean id="authenticationManager"
		class="org.jasig.cas.authentication.AuthenticationManagerImpl">
		<!--
			| This is the List of CredentialToPrincipalResolvers that identify what Principal is trying to authenticate.
			| The AuthenticationManagerImpl considers them in order, finding a CredentialToPrincipalResolver which

			| supports the presented credentials.
			|
			| AuthenticationManagerImpl uses these resolvers for two purposes.  First, it uses them to identify the Principal
			| attempting to authenticate to CAS /login .  In the default configuration, it is the DefaultCredentialsToPrincipalResolver
			| that fills this role.  If you are using some other kind of credentials than UsernamePasswordCredentials, you will need to replace
			| DefaultCredentialsToPrincipalResolver with a CredentialsToPrincipalResolver that supports the credentials you are
			| using.
			|
			| Second, AuthenticationManagerImpl uses these resolvers to identify a service requesting a proxy granting ticket.

			| In the default configuration, it is the HttpBasedServiceCredentialsToPrincipalResolver that serves this purpose.

			| You will need to change this list if you are identifying services by something more or other than their callback URL.
			+-->
		<property name="credentialsToPrincipalResolvers">
			<list>
				<!--
					| UsernamePasswordCredentialsToPrincipalResolver supports the UsernamePasswordCredentials that we use for /login 
					| by default and produces SimplePrincipal instances conveying the username from the credentials.
					| 
					| If you've changed your LoginFormAction to use credentials other than UsernamePasswordCredentials then you will also
					| need to change this bean declaration (or add additional declarations) to declare a CredentialsToPrincipalResolver that supports the
					| Credentials you are using.
					+-->
				<bean
					class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" />
				<!--
					| HttpBasedServiceCredentialsToPrincipalResolver supports HttpBasedCredentials.  It supports the CAS 2.0 approach of
					| authenticating services by SSL callback, extracting the callback URL from the Credentials and representing it as a
					| SimpleService identified by that callback URL.
					|
					| If you are representing services by something more or other than an HTTPS URL whereat they are able to
					| receive a proxy callback, you will need to change this bean declaration (or add additional declarations).
					+-->
				<bean
					class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />
			</list>
		</property>

		<!--
			| Whereas CredentialsToPrincipalResolvers identify who it is some Credentials might authenticate,

			| AuthenticationHandlers actually authenticate credentials.  Here we declare the AuthenticationHandlers that
			| authenticate the Principals that the CredentialsToPrincipalResolvers identified.  CAS will try these handlers in turn
			| until it finds one that both supports the Credentials presented and succeeds in authenticating.
			+-->
		<property name="authenticationHandlers">
			<list>
				<!--
					| This is the authentication handler that authenticates services by means of callback via SSL, thereby validating
					| a server side SSL certificate.
					+-->
				<bean
					class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" />

				<!--
					| This is the authentication handler declaration that every CAS deployer will need to change before deploying CAS

					| into production.  The default SimpleTestUsernamePasswordAuthenticationHandler authenticates UsernamePasswordCredentials
					| where the username equals the password.  You will need to replace this with an AuthenticationHandler that implements your
					| local authentication strategy.  You might accomplish this by coding a new such handler and declaring
					| edu.someschool.its.cas.MySpecialHandler here, or you might use one of the handlers provided in the adaptors modules.
					+-->
				<bean
					class="org.jasig.cas.adaptors.ldap.BindLdapGssapiAuthenticationHandler">
					<property name="filter" value="uid=%u" />
					<property name="searchBase" value="o=langhua,c=cn" />
					<property
						name="contextSource"
						ref="contextSource" />
				</bean>
			</list>
		</property>
	</bean>
	
	<bean id="contextSource" class="org.jasig.cas.adaptors.ldap.util.AuthenticatedLdapGssapiContextSource">
		<property name="anonymousReadOnly" value="true" />
		<property name="password" value="{password_goes_here}" />
		<property name="pooled" value="true" />
		<property name="urls">
			<list>
				<value>ldap://auth.langhua:389/</value>
			</list>
		</property>
		<property name="userName" value="{username_goes_here}" />
		<property name="baseEnvironmentProperties">
			<map>
				<entry>
				        <key><value>java.naming.factory.initial</value></key>
        				<value>com.sun.jndi.ldap.LdapCtxFactory</value>
        			</entry>
				<entry>
				        <key><value>java.naming.referral</value></key>
        				<value>ignore</value>
        			</entry>
				<entry>
				        <key><value>java.naming.ldap.attributes.binary</value></key>
        				<value>photo jpegphoto jpegPhoto</value>
        			</entry>
				<entry>
				        <key><value>java.naming.ldap.version</value></key>
        				<value>3</value>
        			</entry>
				<entry>
				        <key><value>java.naming.ldap.deleteRDN</value></key>
        				<value>false</value>
        			</entry>
				<entry>
				        <key><value>java.naming.ldap.derefAliases</value></key>
        				<value>searching</value>
        			</entry>
				<entry>
				        <key><value>javax.security.sasl.qop</value></key>
        				<value>auth-conf</value>
        			</entry>
				<entry>
				        <key><value>java.naming.provider.url</value></key>
        				<value>ldap://auth.langhua</value>
        			</entry>
				<entry>
				        <key><value>java.naming.security.authentication</value></key>
        				<value>GSSAPI</value>
        			</entry>
				<entry>
				        <key><value>java.security.auth.login.config</value></key>
        				<value>/usr/share/tomcat5/webapps/cas/WEB-INF/gssapi.conf</value>
        			</entry>
				<entry>
				        <key><value>javax.security.sasl.server.authentication</value></key>
        				<value>true</value>
        			</entry>
				<entry>
				        <key><value>javax.security.auth.useSubjectCredsOnly</value></key>
        				<value>false</value>
        			</entry>
        		</map>
		</property>
	</bean>				
</beans>

8. restart tomcat

9. kadmin.local -q "cpw -pw 111111 test"

Make CAS use password to authorize test.

...

In client, use IE6 or firefox to visit https://auth.langhua:8443/cas/
username: test or test@AUTH.LANGHUA
password: 111111

Here is a picture to show the login process. Image Added
 
Good Luck!

Shi Yusen/Beijing Langhua Ltd.
http://www.langhua.cn/