Java Network Programming (3rd ed) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Java Network Programming (3rd ed) [Electronic resources] - نسخه متنی

Harold, Elliotte Rusty

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








7.6 Accessing Password-Protected Sites


Many popular sites, such as
The Wall Street Journal,
require a username and password for access. Some sites, such as the
W3C member pages, implement this correctly through HTTP
authentication. Others, such as the Java Developer Connection,
implement it incorrectly through cookies and HTML forms.
Java's
URL
class can access sites that use HTTP authentication, although
you'll of course need to tell it what username and
password to use. Java does not provide support
for sites that use nonstandard, cookie-based authentication, in part
because Java doesn't really support cookies in Java
1.4 and earlier, in part because this requires parsing and submitting
HTML forms, and, lastly, because cookies are completely contrary to
the architecture of the Web. (Java 1.5 does add some cookie support,
which we'll discuss in the next chapter. However, it
does not treat authentication cookies differently than any other
cookies.) You can provide this support yourself using the
URLConnection class to read and write the HTTP
headers where cookies are set and returned. However, doing so is
decidedly nontrivial and often requires custom code for each site you
want to connect to. It's really hard to do short of
implementing a complete web browser with full HTML forms and cookie
support. Accessing sites protected by standard, HTTP authentication
is much easier.


7.6.1 The Authenticator Class


The
java.net package includes an
Authenticator class you can use to provide a
username and password for sites that protect themselves using HTTP
authentication:

public abstract class Authenticator extends Object // Java 1.2

Since Authenticator is an abstract class, you must
subclass it. Different subclasses may retrieve the information in
different ways. For example, a character mode program might just ask
the user to type the username and password on
System.in. A GUI program would likely put up a
dialog box like the one shown in Figure 7-4. An automated robot might
read the username out of an encrypted file.


Figure 7-4. An authentication dialog

To make the URL class use the subclass, install it
as the default authenticator by passing it to the static
Authenticator.setDefault() method:

public static void setDefault(Authenticator a)

For example, if you've written an
Authenticator subclass named
DialogAuthenticator, you'd
install it like this:

Authenticator.setDefault(new DialogAuthenticator( ));

You only need to do this once. From this point forward, when the
URL class needs a username and password, it will
ask the DialogAuthenticator using the static
Authenticator.requestPasswordAuthentication() method:

public static PasswordAuthentication requestPasswordAuthentication(
InetAddress address, int port, String protocol, String prompt, String scheme)
throws SecurityException

The address argument is the host for which
authentication is required. The port argument is
the port on that host, and the protocol argument
is the application layer protocol by which the site is being
accessed. The HTTP server provides the prompt.
It's typically the name of the realm for which
authentication is required. (Some large web servers such as
www.ibiblio.org have multiple realms, each of
which requires different usernames and passwords.) The
scheme is the authentication scheme
being used. (Here the word scheme is not being
used as a synonym for protocol. Rather it is an
HTTP authentication scheme, typically basic.)

Untrusted applets are not allowed to ask the user for a name and
password. Trusted applets can do so, but only if they possess the
requestPasswordAuthentication
NetPermission. Otherwise,
Authenticator.requestPasswordAuthentication( )
throws a SecurityException.

The Authenticator subclass must override the
getPasswordAuthentication( ) method. Inside this
method, you collect the username and password from the user or some
other source and return it as an instance of the
java.net.PasswordAuthentication class:

protected PasswordAuthentication getPasswordAuthentication( )

If you don't want to authenticate this request,
return null, and Java will tell the server it
doesn't know how to authenticate the connection. If
you submit an incorrect username or password, Java will call
getPasswordAuthentication( ) again to give you
another chance to provide the right data. You normally have five
tries to get the username and password correct; after that,
openStream( ) throws a
ProtocolException.

Usernames and passwords are cached within the same virtual machine
session. Once you set the correct password for a realm, you
shouldn't be asked for it again unless
you've explicitly deleted the password by zeroing
out the char array that contains it.

You can get more details about the request by invoking any of these
methods inherited from the
Authenticator
superclass:

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

These methods either return the information as given in the last call
to requestPasswordAuthentication( ) or return
null if that information is not available.
(getRequestingPort( ) returns -1 if the port
isn't available.) The last method,
getRequestingHost( ), is only available in Java
1.4 and later; in earlier releases you can call
getRequestingSite( ).getHostName( ) instead.

Java 1.5 adds two more methods to this class:

protected final String getRequestingURL( )  // Java 1.5
protected Authenticator.RequestorType getRequestorType( )

The getRequestingURL( ) method returns the
complete URL for which authentication has been requestedan
important detail if a site uses different names and passwords for
different files. The getRequestorType( ) method
returns one of the two named constants
Authenticator.RequestorType.PROXY or
Authenticator.RequestorType.SERVER to indicate
whether the server or the proxy server is requesting the
authentication.


7.6.2 The PasswordAuthentication Class


PasswordAuthentication is a very simple final class that
supports two read-only properties: username and password. The
username is a String. The password is a
char array so that the password can be erased when
it's no longer needed. A String
would have to wait to be garbage collected before it could be erased,
and even then it might still exist somewhere in memory on the local
system, possibly even on disk if the block of memory that contained
it had been swapped out to virtual memory at one point. Both username
and password are set in the constructor:

public PasswordAuthentication(String userName, char[] password)

Each is accessed via a getter method:

public String getUserName( )
public char[] getPassword( )


7.6.3 The JPasswordField Class


One useful tool for asking users for their passwords in a more or
less secure fashion is the
JPasswordField component from Swing:

public class JPasswordField extends JTextField

This lightweight component behaves almost exactly like a text field.
However, anything the user types into it is echoed as an asterisk.
This way, the password is safe from anyone looking over the
user's shoulder at what's being
typed on the screen.

JPasswordField also stores the passwords as a
char array so that when you're
done with the password you can overwrite it with zeros. It provides
the getPassword( ) method to
return this:

public char[] getPassword( )

Otherwise, you mostly use the methods it inherits from the
JTextField superclass. Example 7-13 demonstrates a Swing-based
Authenticator subclass that brings up a dialog to
ask the user for his username and password. Most of this code handles
the GUI. A JPasswordField collects the password
and a simple JTextField retrieves the username. Figure 7-4 showed the rather simple dialog box this produces.


Example 7-13. A GUI authenticator


package com.macfaq.net;
import java.net.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class DialogAuthenticator extends Authenticator {
private JDialog passwordDialog;
private JLabel mainLabel
= new JLabel("Please enter username and password: ");
private JLabel userLabel = new JLabel("Username: ");
private JLabel passwordLabel = new JLabel("Password: ");
private JTextField usernameField = new JTextField(20);
private JPasswordField passwordField = new JPasswordField(20);
private JButton okButton = new JButton("OK");
private JButton cancelButton = new JButton("Cancel");
public DialogAuthenticator( ) {
this(", new JFrame( ));
}
public DialogAuthenticator(String username) {
this(username, new JFrame( ));
}
public DialogAuthenticator(JFrame parent) {
this(", parent);
}
public DialogAuthenticator(String username, JFrame parent) {
this.passwordDialog = new JDialog(parent, true);
Container pane = passwordDialog.getContentPane( );
pane.setLayout(new GridLayout(4, 1));
pane.add(mainLabel);
JPanel p2 = new JPanel( );
p2.add(userLabel);
p2.add(usernameField);
usernameField.setText(username);
pane.add(p2);
JPanel p3 = new JPanel( );
p3.add(passwordLabel);
p3.add(passwordField);
pane.add(p3);
JPanel p4 = new JPanel( );
p4.add(okButton);
p4.add(cancelButton);
pane.add(p4);
passwordDialog.pack( );
ActionListener al = new OKResponse( );
okButton.addActionListener(al);
usernameField.addActionListener(al);
passwordField.addActionListener(al);
cancelButton.addActionListener(new CancelResponse( ));
}
private void show( ) {
String prompt = this.getRequestingPrompt( );
if (prompt == null) {
String site = this.getRequestingSite( ).getHostName( );
String protocol = this.getRequestingProtocol( );
int port = this.getRequestingPort( );
if (site != null & protocol != null) {
prompt = protocol + "://" + site;
if (port > 0) prompt += ":" + port;
}
else {
prompt = ";
}
}
mainLabel.setText("Please enter username and password for "
+ prompt + ": ");
passwordDialog.pack( );
passwordDialog.show( );
}
PasswordAuthentication response = null;
class OKResponse implements ActionListener {
public void actionPerformed(ActionEvent e) {
passwordDialog.hide( );
// The password is returned as an array of
// chars for security reasons.
char[] password = passwordField.getPassword( );
String username = usernameField.getText( );
// Erase the password in case this is used again.
passwordField.setText(");
response = new PasswordAuthentication(username, password);
}
}
class CancelResponse implements ActionListener {
public void actionPerformed(ActionEvent e) {
passwordDialog.hide( );
// Erase the password in case this is used again.
passwordField.setText(");
response = null;
}
}
public PasswordAuthentication getPasswordAuthentication( ) {
this.show( );
return this.response;
}
}

Example 7-14 is a revised
SourceViewer program that asks the user for a name
and password using the DialogAuthenticator class.


Example 7-14. A program to download password-protected web pages


import java.net.*;
import java.io.*;
import com.macfaq.net.DialogAuthenticator;
public class SecureSourceViewer {
public static void main (String args[]) {
Authenticator.setDefault(new DialogAuthenticator( ));
for (int i = 0; i < args.length; i++) {
try {
//Open the URL for reading
URL u = new URL(args[i]);
InputStream in = u.openStream( );
// buffer the input to increase performance
in = new BufferedInputStream(in);
// chain the InputStream to a Reader
Reader r = new InputStreamReader(in);
int c;
while ((c = r.read( )) != -1) {
System.out.print((char) c);
}
}
catch (MalformedURLException ex) {
System.err.println(args[0] + " is not a parseable URL");
}
catch (IOException ex) {
System.err.println(ex);
}
// print a blank line to separate pages
System.out.println( );
} // end for
// Since we used the AWT, we have to explicitly exit.
System.exit(0);
} // end main
} // end SecureSourceViewer


/ 164