Accessing Password-Protected Sites

Many popular sites, such as The Wall Street Journal, require a username and password for access. Some sites, such as Oracle TechNet, implement this through HTTP authentication. Others, such as the Java Developer Connection, implement it through cookies and HTML forms. Java’s URL class can access sites that use HTTP authentication, though 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, partially because Java doesn’t really support cookies and partially because this requires parsing and submitting HTML forms. 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.

The Authenticator Class

Starting in Java 1.2 (but not available in Java 1.1), 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

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 it out of an encrypted file.

An authentication dialog

Figure 7-4. An authentication dialog

To make the URL class use your subclass, you 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 for it 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 prompt is provided by the HTTP server. It’s typically the name of the realm for which authentication is required. (Some large web servers such as metalab.unc.edu 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 may not be 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.requestPassword Authentication( ) throws a SecurityException.

Your 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, then 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( ) will throw 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(  )

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 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 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 get method:

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

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 he’s typing 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 zeroes. 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.11 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-11. 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.12 is a revised SourceViewer program that can ask the user for a name and password by using the DialogAuthenticator class.

Example 7-12. 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 e) {
        System.err.println(args[0] + " is not a parseable URL");
      }
      catch (IOException e) {
        System.err.println(e);
      }
      
      // 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
                  
                  
                  

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.72.78