Authenticator(认证者)模式

 

F. Lee Brown, Jr.等 著,    透明

 

抽象

       想象一个为各种互不相关的分布式用户[1]充当对象仓库(repository of object)的服务器系统,它很可能需要一种限制用户访问权限的方法,而且这种方法应该是基于提出请求的用户的身份的。身份鉴定与认证,这些重要的协议却常常被现在的分布式对象系统忽略了。

       Authenticator(鉴定者)模式描述了一个一般性的机制,它为服务器提供了对用户的身份鉴定与认证方法。它还有另外的特点:它可以在使用同一个程序的过程中进行协议谈判(protocol negatiation)。这个模式的工作方式是:提供一个认证谈判对象来认证用户身份,只有认证成功之后才提供被保护的对象。

1.意图

       Authenticator模式在决定分布式对象的访问权限之前对提出请求的进程进行身份认证。

2.动机

       常用的分布式对象系统都提供命名注册服务,一般情况下它允许任何访问者通过访问注册表获得任何分布式对象——只要访问者知道这个对象的名字。而在提供多种服务的分布式对象系统中,为了确定每个用户是否有访问权限,使用一个登录-认证(login-and-authenticate)协议是很必要的。

       身份认证对于为用户——来自内部(Intranet)和外部(Internet)——提供多种访问权限的分布式系统来说是尤其重要的。

       远端对象也需要重新配置,以解决客户机与服务器之间性能差异带来的问题。

这个模式背后隐藏着这样的能力:

l         用户对于不同的远程对象(remote object)可能拥有不同的权限。在服务器决定用户的访问权限之前,它必须认证访问者的身份以确定它的权限。

l         远程对象需要适合这两个相关的系统的性能。这些性能差异只能由一个谈判(negotiating)进程在创建对象之前进行协调。

l         需要一种一般化的认证方法,该方法不依赖于任何特定的认证方法。

3.适用性

以下情况使用Authenticator模式

1.需要对远端用户进行身份鉴别和认证

2.有几种不同的认证方法都可以使用

3.附加的协议谈判(密码选择、软件版本支持等等)需要首先获得一个远端对象,但低层分布式系统不支持这样的要求。

4.结构和参与者

       Authenticator模式使用了一个可以从远端访问的分布式对象,这个对象将对请求的发起者(“请求者requestor”)进行身份鉴别和认证,并且这个对象可以进行某些协议谈判。当且仅当认证和谈判成功时,认证者才创建请求者要求的分布式对象,并使请求者可以使用它。

Authenticator模式由以下元件(component)组成:

1Authenticator —— 认证者。这个抽象类定义了一个接口用于鉴别一个连接进程或谈判进程的参量。应用程序需要定义一个具体类来实现这个接口,从而提供特定的认证或谈判协议。得到的这个类应该被实例化,并注册到一个合适的名称服务(naming service),成为一个可以在网络的任何地方访问的分布式对象。Requestor在得到一个Authenticator实例的引用后,就可以调用它的authenticate方法。在认证成功以后,Authenticator对象创建一个Requestor请求的分布式对象的实例,然后Requestor就可以使用get方法访问自己请求的对象了。关于实现本模式的一个建议是:把一个类型为Object Factory的引用传递给Authenticator的构造函数以创建这个被保护的对象。

2Object Factory —— 对象工厂。这个抽象类只包含一个方法,create。这个方法的实现需要创建被保护的对象。作为谈判过程的结果,Authenticator可以让Object Factory进行另一些特定的操作。

3Requestor —— 请求者。尽管并没有严格规定远程请求者作为这个模式的一部分,但我们通常按照一种与具体Authenticator类相匹配的方式实现它,并将它按值传递给authenticate方法,Authenticator用它的值来完成认证过程。

1展示出Authenticator模式的参与者关系。


1 - Authenticator模式参与者

5.协作

Authenticator模式有四个阶段操作:

1初始化。为了能让客户远程访问Authenticator对象,这个对象必须被注册到分布式对象系统的名称服务上。这阶段的操作是系统在模式外部指定的,但是与模式的耦合很紧。

2连接。当一个远程对象——请求者——得到一个Authenticator对象的引用之后,它调用authenticate方法传递一个字符串给实际的Authenticatordauthenticate的返回值是另一个字符串,请求者用这个字符串判断认证(或谈判)是否成功、是否(怎样)创建另一个字符串来进行另一次authenticate方法的调用。请求者继续调用authenticate方法,直到认证(或谈判)完成。

3创建。当实际Authenticator对象确认一个认证过程成功之后,它创建一个保护对象,准备响应请求者的get方法。一个很理想的处理流程是让authenticate调用一个对象工厂方法来创建保护对象。不过,为了让一个(或多个)对象可以被远程访问,你可以用任何方式使用Authenticator对象和对象工厂,

4获得。最后,请求者调用get方法获得一个保护对象的引用。

    下图是这个模式的操作图解。


2 - Authenticator模式顺序图解

总结:

- 初始化。服务器创建并注册Authenticator对象,把一个对象工厂对象作为构造参数传递给Authenticator对象。

- 连接。Requestor查询服务器的注册表,得到一个Authenticator对象的引用。

- Requestor调用authenticate(string)方法。

- Authenticator返回代表“完成/失败/继续认证”的字符串。

- 如果有必要,重复上面的两个步骤,直到认证/谈判完成。

- 创建。在返回成功标志之前,authenticate方法调用对象工厂以创建/初始化保护对象。

- 获得。Requestor调用get方法得到保护对象的引用。

6.效果

Authenticator模式提供如下益处:

1为需要复杂身份认证的应用程序提供一种选择。可以在Authenticator模式中实现不同的认证方法,从而允许多种用户使用他们各自的认证方法。

2为需要复杂协议谈判的应用程序提供一种选择。在服务器提供对一个对象的访问权限之前,应用程序可能需要与服务器进行一些谈判。这些谈判可以包括密码方法谈判、密钥交换、最大公约数版本谈判(greatest common denominator version negotiation)、保护对象参数化(即:传递一个保护对象的构造参数给对象工厂)及应用程序的其他任何需求。

3准许多个客户同时访问远程对象。如果操作系统和硬件平台支持多CPU,这个模式可以允许授权多个客户同时访问远程对象。

4为身份认证提供一条一般化的、不依赖于客户或被访问对象的途径。

Authenticator模式有以下不足之处:

1本模式的能力限制了分布式对象系统的逻辑能力。一个分布式系统可能已经可以提供重复认证/协议谈判的能力。本模式会与低层系统的这些能力发生功能交迭。但是,如果我们设计的是一个新的系统,这就不成为一个问题了。

2实现与调试困难。对于Authenticator模式来说,认证/谈判的编程、服务器端的调试都可能会相当困难,尤其是在有客户访问的时候。

7.实现

       下面讨论了实现Authenticator模式时需要考虑的一些要点:

1安全性。出于安全性的考虑,实现Authenticator模式时应该把对象工厂类隐藏起来。非可信的客户也可以远程访问Authenticator对象,但对象工厂不是一个远程对象、所以客户也不能访问它。(对象工厂不是远程对象,也意味着任何程序对它的访问必须受到限制。)在谈判没有完成之前,Authenticator绝对不能提供对对象工厂的任何调用。

2谈判。设计认证/谈判过程时,必须重视多个用户的并发访问(比如说,把Authenticator设计成一个Singleton),必须重视网络掉线或超时、请求错误或顺序错乱以及其他无心或有心的错误情况。它还必须提供一种有效的方法向客户指出谈判进程的当前状态:成功?失败?或者正在处理?authenticate方法的返回值必须提供足够的信息以引导客户进行下一步工作。

3参数化对象创建。用对象工厂进行参数化对象创建可能是有必要的。在本文后面的例子中,对象工厂的create方法的参数列表为空;但是只要Authenticator的实现和对象工厂允许,就可以给它一个参数列表。Authenticator对象的参数列表必须使用客户端支持的数据类型,出于这个原因,参数列表应该在谈判过程中由客户端指定,或者由Authenticator对象提供一个附加方法来让客户端指定参数。

4多个对象的创建。一旦认证完成,我们下一步怎么做才是正确的?只提供一个可访问的对象?还是允许客户创建任意数目的对象?如果允许客户创建任意数目的对象,这些对象都必须是同一个类的实例吗?本文后面的例子为我们展示了“只提供一个可访问对象”的正确做法。authenticate方法强制只创建一个实例,所以即使多个客户调用get方法,它们得到的实际是同一个对象。另一种选择是:如果authenticate方法已经确定认证过程成功完成,get方法就可以在每次被调用的时候创建一个新的实例。

8.代码示例

       这里展示了一个简单的JAVA实现。构成本模式的两个抽象类以JAVA接口(interface)的形式出现。

// Object factory interface. The create method constructs and returns

// an object as determined by the implementing class.

//

public interface ObjectFactory

{

public Object create();

}

// Authenticator interface for a remote object. The implementation

// provides specific authentication requirements.

//

public interface Authenticator

extends Remote

{

// Potential response strings for the authenticate method.

//

public final static String AUTHENTICATED = “AOK”;

public final static String AUTHENTICATION_FAILED = “ERR”;

public final static String AUTHENTICATION_CONTINUE = “MORE”;

// Accept a key string that should be the next response from

// the client in the negotiation protocol, and return a prompt

// string or status indication.

//

public String authenticate(String key)

throws RemoteException;

// Return the protected object. This method returns null until

// the negotiation is successfully completed.

//

public Object get()

throws RemoteException;

}

       应用程序必须实现AuthenticatorObjectFactory接口。下面的JAVA代码向我们展示了一个框架示例,在实际的应用程序中可能用到这个框架。

// Object factory implementation. The create method returns a remote

// object of a predetermined type.

//

class ObjectFactoryImpl

implements ObjectFactory

{

public ObjectFactoryImpl()

{

}

public Object create()

{

MyRemoteObject obj = null;

try

{

obj = new MyRemoteObject();

}

catch (RemoteException x)

{

System.err.println(“RemoteException error: “ + x);

return null;

}

return ((Object) obj);

}

}

// Authenticator implementation. This implementation defines the

// authentication requirements and any other protocol negotiation.

// Successful completion of the negotiation immediately creates an

// instance of the protected object which can then be retrieved

// using the get method.

//

class AuthenticatorImpl

extends UnicastRemoteObject

implements Authenticator

{

private ObjectFactory factory;

private Object robject;

public AuthenticatorImpl(ObjectFactory inFactory)

throws RemoteException

{

super();

factory = inFactory;

robject = null;

}

public Object get()

throws RemoteException

{

return robject;

}

public String authenticate(String key)

throws RemoteException

{

String response = new String(“”);

/* Application-specific authentication */

if (response.equals(Authenticator.AUTHENTICATED))

{

robject = (Object) factory.create();

}

return response;

}

}

       Authenticator的实现将用类似下面的语句注册到JAVA RMI名称服务上(Java RMI naming service, [9])。

Naming.rebind(name,new AuthenticatorImpl(new ObjectFactoryImpl()));

       因为Authenticator接口的构造函数和get方法很可能用同样的方法实现而不管具体应用程序的情况,所以补充一个实现了Authenticator接口的DefaultAuthenticator类会是很有用的。然后应用程序可以由这个类衍生出自己需要的类。

       当然,Authenticator的实现必须重视连续、并发的认证请求。上面展示的程序框架暗示着所有的远程客户都使用一个单一的对象。在真实世界的大多数应用程序中,都需要使用多线程或其它什么方法来区分远程Authenticator对象。

9.已知应用

       大多数HTTP服务器允许用户自由访问任何资源,或者根据请求者的源地址(这个地址可能是伪造的)来控制访问权限。尽管如此,许多WEB资源仍然需要一个登录密码框来认证用户的身份。一个与之对等的、甚至是更有力的认证机制——就象Authenticator所提供的机制——对于非交互式的远程资源访问来说是同样有用的。

       在创建一个保护对象或者在向远程用户提供访问权限之前,可以用Authenticator模式创建一个请求者和服务器之间的对话。客户端和服务器之间的谈判内容可以包括身份鉴别(例如:用户登录)、认证(例如:简单密码、提问/应答(challenge/response)、多重提问/应答)、密码方法谈判、密钥交换、最大公约数版本谈判、保护对象参数化(即:传递参数给对象工厂作为保护对象的构造参数)、以及其它任何应用程序需要的谈判。

       CyberguardFort LauderdaleFL用上面讲到的途径设计了一些内部使用的软件,并且已经考虑将它用在一些产品中。很显然,由于本模式满足了一些非常基础的需求,实际中必定有本模式的另一些实现。

10.相关模式

       认证只是安全的一个方面。完整的安全性还需要一些授权机制或相似的安全机制来确定角色的权利[2]。一个类似于Bodyguard[1]的模式可以用于实现这个意图。

       Authenticator模式是Abstract Factory模式的一个变化,它也是基于一个工厂类的。但与Abstract Factory对象静态指定创建对象的类型不同的是,Authenticator模式用一个交互式谈判过程来确定是否创建一个对象(还可能需要确定什么对象)。

       因为分布式应用与普通应用的分离,本模式与Schmidts的模式[4-8]实际属于同一范畴。

参考文献

[1] F. Das Neves and A. Garrido, "Bodyguard", Chapter 13 in Pattern Languages of Program Design 3, Addison-Wesley 1998.

[2] E.B. Fernandez and J. C. Hawkins, "Determining role rights from use cases", Procs. 2 nd ACM Workshop on Role-Based Access Control, 1997, 121-125.

[3] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Patterns, Addison-Wesley, 1995. 中文版:《设计模式:可复用面向对象软件的基础》,李英军等译,机械工业出版社20009月。

[4] D. C. Schmidt. “Reactor: An Object Behavioral Pattern for Concurrent Event Demultiplexing and Event Handler Dispatching.” Pattern Languages of Program Design (J. O. Coplien and D. C. Schmidt, eds.), Reading, MA: Addison-Wesley, 1995.

[5] D. C. Schmidt. “Acceptor: A Design Pattern for Passively Initializing Network Services.” C++ Report, vol. 7, November/December 1995.

[6] D. C. Schmidt,. “Connector: A Design Pattern for Actively Initializing Network Services.” C++ Report, vol. 8, January 1996.

[7] D. C. Schmidt. “A Family of Design Patterns for Application-Level Gateways.” Theory and Practice of Object Systems, J. Wiley & Sons, vol. 2, no. 1, December 1996.

[8] D. C. Schmidt. “Acceptor and Connector: Design Patterns for Initializing Communication Services.” The 1 st European Pattern Languages of Programming Conference (Washington University technical report #WUCS-97-07), July 1997.

[9] “Java Remote Method Invocation.” Sun Microsystems, Inc., Redmond, Washington, 1977.

 


[1] 译者注:本文中的“client”通常是指从远端计算机连接到服务器的远程用户。当它指一个远端机器时,我把它译为“客户机”;当它指机器上的一个进程或是使用机器的人时,我把它译为“用户”。