构建和实现单点登录解决方案-Active Directory Server 身份验证
- UID
- 1066743
|
构建和实现单点登录解决方案-Active Directory Server 身份验证
Active Directory Server 身份验证在我的教育门户项目中,可以很轻松地修改应用程序,因为我能够得到源代码。我在每个应用程序中使用了 servlet 过滤器。在这种情况下,当用户首次访问门户并请求一个受限制的应用程序或数据源时,CAS 将执行身份验证。身份验证成功之后,CAS servlet 过滤器可以检查 ticket,并将用户名作为参数传递给请求的应用程序。我修改了现有的应用程序登录屏幕,让它们在成功的身份验证之后从 CAS 接收用户名参数。然后,应用程序可以使用这个参数对用户进行授权,并对用户的活动进行审计记录。
但是,CAS 本身没有附带任何真正有用的身份验证器。默认的身份验证器仅仅检查用户名和密码是否相同。在教育门户项目中,我使用 Microsoft 的 Active Directory Server 存储用户的个人信息。所以我需要扩展 CAS 服务器,让它根据 Active Directory Server 检查用户名和密码。
Yale University 的 CAS 设计人员开发了一个可插入的身份验证器架构。通过扩展 PasswordHandler 接口,开发人员可以创建一个类来实现密码检查逻辑。
清单 2. CAS 的可插入身份验证器架构1
2
3
4
5
6
7
8
9
10
11
12
| /** Interface for password-based authentication handlers. */
public interface PasswordHandler extends AuthHandler {
/**
* Authenticates the given username/password pair, returning true
* on success and false on failure.
*/
boolean authenticate(javax.servlet.ServletRequest request,
String username,
String password);
}
|
我要做的只是了解如何使用 Active Directory Server 检查用户名和密码,创建一个类来扩展这个接口,并添加执行身份验证的代码。
ADS 是一种与 LDAP3 兼容的目录服务器。它支持许多不同的身份验证方法,主要包括匿名(anonymous)、简单(simple) 和 SASL(Simple Authentication and Security Layer)。匿名身份验证不适合我的项目;简单身份验证用明文发送密码,这也不满足需要。所以我使用 SASL。
SASL 支持可插入的身份验证。这意味着可以对 LDAP 客户机和服务器进行配置,让它们相互协商并使用多种身份验证机制之一。下面是当前定义的一部分 SASL 机制:
- Anonymous
- CRAM-MD5
- Digest-MD5
- External
- Kerberos V4
- Kerberos V5
- SecurID
- Secure Remote Password
- S/Key
- X.509
在这些机制中,流行的 LDAP 服务器(比如 Sun、OpenLDAP 和 Microsoft 提供的服务器)都支持 External、Digest-MD5 和 Kerberos V5。
CAS 本身与 Kerberos 相似,它们有许多相同的概念,比如 ticket 和 ticket-granting ticket(在 CAS 中实际上称为 ticket-granting cookie),它们的协议也是相似的。所以,选择 Kerberos 机制似乎是很自然的。另外,添加对 Kerberos 身份验证的支持让 CAS 在未来的开发中能够作为基于 Web 的 Kerberos 代理代表用户,这使 CAS 能够替用户管理 Kerberos ticket 的所有方面。这意味着远程用户也可以使用相同的 Kerberos 身份验证机制访问本地和网络资源。
Kerberos 协议已经内置在 ADS 和 Windows 2000/XP 中。Java Authentication and Authorization Service(JAAS)提供了 Kerberos Login Module 的一个实现(参考资料 中列出的一个教程详细描述了如何建立一个示例应用程序)。精确地说,它是 GSS-API SASL 机制,但是它只为 LDAP3 服务器提供 Kerberos v5 身份验证。
Kerberos 身份验证是一个很简单的过程(如果仔细地按照以下说明进行操作的话):
- 在 JAAS 配置文件中为应用程序类配置 Login Module。
1
2
3
4
| edu.yale.its.tp.cas.auth.provider.KerberosAuthHandler
{
com.sun.security.auth.module.Krb5LoginModule required client=TRUE;
};
|
- 创建一个 LoginContext,传递执行身份验证的类的名称和 CallBackHandler 对象。
1
2
| LoginContext lc = new LoginContext(CASApp.class.getName(),
new CASCallbackHandler());
|
- 调用 login() 方法来执行身份验证。如果执行过程没有异常,身份验证就成功了。如果抛出了异常,那么这个异常会指出失败的原因。
要想深入了解 Kerberos 身份验证,建议您利用本文末尾的参考资料。(我还提供了自己的实现和配置文件,KerberosAuthHandler 和 CASCallBackHandler。)
需要创建一个新的 PasswordHandler 实现,KerberosAuthHandler,它按照上面的方法根据使用 Kerberos v5 的 Active Directory Server 进行身份验证。
清单 3. 创建新的 PasswordHandler 实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| public class KerberosAuthHandler implements PasswordHandler {
public boolean authenticate(javax.servlet.ServletRequest request,
String username,
String password)
{
LoginContext lc = null;
try
{
/* Set up the Callback handler, and initialise */
/* the userid and password fields */
CASCallbackHandler ch = new CASCallbackHandler();
ch.setUserId(username);
ch.setPassword(password);
/* Initialise the login context - LoginModule configured */
/* in cas_jaas.conf and */
/* set to use Krb5LoginModule. */
lc = new LoginContext(KerberosAuthHandler.class.getName(), ch);
/* Perform the authentication */
lc.login();
}
catch (LoginException le)
{
System.err.println("Authentication attempt failed" + le);
return false;
}
return true;
}
}
|
在创建 LoginContext 时,传递这个类名和 CASCallbackHandler 对象。JAAS 配置文件指定这个类使用的登录模块。
在调用 login() 方法时,登录模块知道为了进行身份验证它需要从用户/应用程序获得哪些信息。在使用 Kerberos 时,它需要用户名和密码,所以它构造一个数组,其中包含两个回调对象(NameCallback 和 PasswordCallback),然后调用 CallbackHandler 对象,这个对象决定如何执行这些回调并获得用户名和密码。
我采用最简单最有利的方式,使用 CallbackHandler 上的 setter 方法显式地设置用户 ID 和密码。然后,CallbackHandler 将它们传递给 NameCallback 和 PasswordCallback 对象。
清单 4. 用 setName (CASUserId) 和 setPassword (CASPassword) 设置 ID 和密码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| public class CASCallbackHandler implements CallbackHandler
{
private String CASUserId;
private char [] CASPassword;
public void handle(Callback[] callbacks)
throws java.io.IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks instanceof NameCallback) {
NameCallback cb = (NameCallback)callbacks;
cb.setName(CASUserId);
} else if (callbacks instanceof PasswordCallback) {
PasswordCallback cb = (PasswordCallback)callbacks;
cb.setPassword(CASPassword);
} else {
throw new UnsupportedCallbackException(callbacks);
}
}
}
public void setUserId(String userid)
{
CASUserId = userid;
}
public void setPassword(String password)
{
CASPassword = new char[password.length()];
password.getChars(0, CASPassword.length, CASPassword, 0);
}
|
下面要做的是让 CAS 使用新的身份验证处理器。方法是在 webapps/cas 中 CAS 服务器的 web.xml 文件中进行以下设置:
清单 5. 让 CAS 使用新的身份验证处理器1
2
3
4
5
6
7
| <!-- Authentication handler -->
<context-param>
<param-name>edu.yale.its.tp.cas.authHandler</param-name>
<param-value>
edu.yale.its.tp.cas.auth.provider.KerberosAuthHandler
</param-value>
</context-param>
|
ZIP 文件( 中的 KerberosAuthSrc.zip)中包含一个 web.xml 示例。
必须重新启动 Tomcat,但是这一次还需要设置一些 Java 运行时属性。对 ZIP 文件(KerberosAuthSrc.zip)进行解压,并将文件 cas_jaas.conf、krb5.conf 和 setkerberosjvmoptions.bat 复制到 TOMCAT_HOME 目录中。运行 setkerberosjvmoptions.bat,然后启动 Tomcat。
现在,可以再用 HelloWorld 应用程序做实验了。这一次,可以使用 Active Directory Server 中定义的有效的 Kerberos 用户名和密码对。 |
|
|
|
|
|