...
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
--- -----------------------------------------------------------------------
5 host/auth.langhua@AUTH.LANGHUA
5 host/auth.langhua@AUTH.LANGHUA
...
If successful login, the OpenLDAP configuration is complete.
Config CAS
1. Create /cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/BindLdapGssapiAuthenticationHandler.java
Code Block |
---|
title | /cas-server-support-ldap/src/main/java/org/jasig/cas/adaptors/ldap/BindLdapGssapiAuthenticationHandler.java |
---|
borderStyle | solid |
---|
|
/*
* 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;
}
}
|
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 |
---|
borderStyle | solid |
---|
|
/*
* 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;
}
}
|
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 |
---|
borderStyle | solid |
---|
|
/*
* 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");
}
}
}
}
}
|
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 |
---|
borderStyle | solid |
---|
|
/*
* 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;
}
}
|
build and deploy CAS 3.1
Create $CAS/WEB-INF/gssapi.conf
Code Block |
---|
title | $CAS/WEB-INF/gssapi.conf |
---|
borderStyle | solid |
---|
|
org.jasig.cas.adaptors.ldap.util.AuthenticatedLdapGssapiContextSource {
com.sun.security.auth.module.Krb5LoginModule required client=TRUE
useTicketCache=TRUE;
};
|
Edit $CAS/WEB-INF/deployerConfigContext.xml
Code Block |
---|
title | $CAS/WEB-INF/deployerConfigContext.xml |
---|
borderStyle | solid |
---|
|
<?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>
|
restart tomcat
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
Good Luck!
Shi Yusen/Beijing Langhua Ltd. http://www.langhua.cn/