许多流行的站点,像TheWall Street Journal,需要一个用户名和密码才能被访问。一些网站,像W3C的会员页面,通过HTTP认证正规地执行这些。其他的像Java Developer Connection,通过cookies 和 HTML非正规地执行这些认证。Java的URL类能访问使用http认证的站点,尽管你也需要告诉它你的用户名和密码。Java对那些使用非标准的、基于cookie认证的站点是提供支持的。一部分原因是在java1.4和早期版本中,java实际上是不支持cookie的,还有部分原因是:如果这样做,需要解析和发送html表格,最后是因为cookie与web架构是完全抵触的。(Java 1.5的确增加了对cookie的支持,我们将在下一章讨论它。尽管如此,但它没有对有认证功能的cookie与其他cookie区分开。)你可以自己提供这种支持,
使用URLConnection 类来读和写cookie被设置或被返回的http头文件。但是,要想这样做并不简单,经常需要你要连接站点的本地代码。这个难度不压于让web浏览器完全实现对html表格和cookie的支持。访问受标准的http认证保护的站点就简单多了。
Authenticator 类
包java.net 中有一个Authenticator 类,你能用它给受http验证保护的站点提供一个用户名和密码:
public abstract class Authenticator extends Object // Java 1.2
因为Authenticator 是一个抽象类,你必须生成它的子类。不同的子类会用不同的方法检索信息。例如,一个字符模式的程序可能会叫用户输入用户名和密码,用System.in 接受它们。一个GUI程序可能会显示一个对话框,像图7-4那样。一个自动机器会从一个加密文件中读出用户名。
图7-4。验证对话框
为了要让URL 类使用Authenticator 类的子类,就把它传递给静态方法Authenticator.setDefault() ,这样就把它安装成了默认的authenticator:
public static void setDefault(Authenticator a)
比如,如果你已经写好了一个叫DialogAuthenticator 的Authenticator 子类,你得这样安装它:
Authenticator.setDefault(new DialogAuthenticator( ));
你只需要这样做一次就行了。从这以后,当URL 类需要用户名和密码时,它会叫DialogAuthenticator 使用静态方法Authenticator.requestPasswordAuthentication() :
public static PasswordAuthentication requestPasswordAuthentication(
InetAddress address, int port, String protocol, String prompt, String scheme)
throws SecurityException
参数address 就是需要进行验证的主机。参数port 就是那个主机上的端口,参数protocol 就是访问这个站点时所用的应用层协议。http服务器会提供参数prompt 。一般就是指需要被验证的访问域。(像www.ibiblio.org 这样的大网站会有许多的访问域,每个都要求用不同的用户名和密码。)参数scheme 是用来做验证的模式。(这里的单词scheme 与protocol (协议)不是同义词。而是一种基本的http验证模式。)
不被信任的applet不允许让用户提供用户名和密码。受信任的applet可以这样做,只要它们有requestPasswordAuthenticationNetPermission
否则的话,Authenticator.requestPasswordAuthentication( ) 抛出SecurityException 异常。Authenticator 的子类必须覆盖getPasswordAuthentication( ) 方法。这个方法里,你需要从用户或其他来源处取到用户名和密码,然后作为java.net.PasswordAuthentication 类的实例返回:
java.net.PasswordAuthentication class:
protected PasswordAuthentication getPasswordAuthentication( )
如果你不想验证这个请求,就返回null,java回告诉服务器它不知道该怎样验证这个连接。如果你发送一个不正确的用户名或密码,java会再次调用getPasswordAuthentication( ) ,再给你一次机会提交正确的数据。正常情况下,你有5次机会得到正确的用户名和密码;在那以后,openStream( ) 会抛出ProtocolException 异常。
用户名和密码被放在相同的虚拟机对话中。一旦你对一个访问域使用了正确的密码进行访问,你就不会被要求再次使用密码了,除非你已经很明确地清空了保存它的char 数组。你可以通过调用这些从超类Authenticator 继承来的方法获取更多详细的信息:
protected final InetAddress getRequestingSite( )
protected final int getRequestingPort( )
protected final String getRequestingProtocol( )
protected final String getRequestingPrompt( )
protected final String getRequestingScheme( )
protected final String getRequestingHost( ) // Java 1.4
这些方法要么返回上次被requestPasswordAuthentication( ) 调用后得到的信息,要么返回null,如果那些信息已经不能使用了。(getRequestingPort( ) 返回-1,如果那个端口不能被使用了。)最后一个方法,getRequestingHost( ) ,只有在java1.4和更新的版本中才能被使用;在早期版本里,你可以调用getRequestingSite( ).getHostName( ) 代替。Java1.5又增加了两个方法到这个类中:
protected final String getRequestingURL( ) // Java 1.5
protected Authenticator.RequestorType getRequestorType( )
方法getRequestingURL( ) 会返回一个被要求使用验证的完整的URL——这是个重要的 ,如果一个站点的不同文件需要使用不同的用户名和密码。方法getRequestorType( ) 会返回下面两个常量中的一个 Authenticator.RequestorType.PROXY 或者 Authenticator.RequestorType.SERVER ,以此来区分是服务器还是代理服务器请求验证。
PasswordAuthentication 类
PasswordAuthentication 是一个非常简单的final类,它提供两个只读属性:用户名和密码。用户名是string存放的。密码被存放在char 数组中,所以当不需要时,可以擦除密码。一个string在它可以被擦除前,要等待垃圾回收器来收集,即使这时它仍有可能存在本地系统内存上的某个地方,也有可能在磁盘上,如果这时这个内存块已经被交换到作为虚拟内存的磁盘区域上。用户名和密码被设置在这个构造函数中: