|  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 
 |