Newsletter sign-up
View all newsletters

Sign up for our Enterprise Java Newsletter

Enterprise Java

Java Tip 47: URL authentication revisited

You're not yet using Java 1.2? Access password-protected URLs with Java 1.1

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
In the last tip, we introduced the following scenario: You're using your favorite browser to surf the 'Net, and you encounter a URL that requires authentication from a proxy or HTTP server. What you see on the screen is the standard box in which you must enter your username and password so as to gain access to the site. To refresh your memory, here's what that screen looks like:
From a Java program, you'll find yourself in trouble: When you try to read from an InputStream associated with the URL, a FileNotFoundException is thrown. Starting with Java 1.2, you can take advantage of the Authenticator class, which, as mentioned above, you learned about in Java Tip 46. However, with versions prior to Java 1.2, you need to know if the URL you want to access is password-protected before you try to read from it. If the URL is protected, you have to manually configure the username-password response for each URL, so you can properly access the contents. The response is in the form of what's called an authorization request property. The property gets associated with the URLConnection for the URL. Once the authorization setting is installed, when you get the URL's contents, the information will be accessible. The authorization string is of the form "Authorization: Basic username:password," where the basic authentication fields are encoded in Base64. (A description of the encoding scheme is found in RFC 1113. An RFC is a "request for comments" document that, basically, describes some kind of Internet standard. In this case, #1113 describes Message Encipherment and Authentication Procedures. See Resources below.) To do the encoding, you can use the BASE64Encoder class in the sun.misc package, or you can create your own class. To set up this access, you simply have to configure the URL's URLConnection to have the proper "Authorization" request property. The following steps describe everything: Step 1: Create URL
 URL url = new URL (urlString);
Step 2: Get username and password for specificURL Step 3: Place in String separated by colon (":")
 String userPassword = theUsername + ":" + thePassword;
Step 4: Encode the bytes of the string
 String encoding = new sun.misc.BASE64Encoder().encode (userPassword.getBytes());
Step 5: Create a URLConnection from the URL
 URLConnection uc = url.openConnection();
Step 6: Set the "Authorization" request property for the URLConnection
 uc.setRequestProperty ("Authorization", "Basic " + encoding);

And now, the solution

Accessing a password-protected URL in Java 1.1 is demonstrated in its entirety below, with its own authentication prompt dialog. The code is as similar as can be to the Java 1.2 Authenticator example provided in Java Tip 46. If you choose to avoid sun.misc classes, or if this happens to be unavailable on your platform, the source for a converter is included below.
 import java.awt.*;
 import java.awt.event.*;
 import java.io.*;
 import java.net.*;
 
public class Auth11 extends Frame implements ActionListener {
 
  private TextField tf = new TextField();
   private TextArea  ta = new TextArea();
 
  public void actionPerformed (ActionEvent e) {
     String s = tf.getText();
     if (s.length() != 0)
       ta.setText (fetchURL (s));
   }
 
  public Auth11() {
 
    super ("URL11 Password");
 
    // Setup screen
     add (tf, BorderLayout.NORTH);
     ta.setEditable(false);
     add (ta, BorderLayout.CENTER);
     tf.addActionListener (this);
     addWindowListener (new WindowAdapter() {
       public void windowClosing (WindowEvent e) {
         dispose();
         System.exit(0);
       }
     });
   }
 
  private String fetchURL (String urlString) {
     StringWriter sw = new StringWriter();
     PrintWriter  pw = new PrintWriter(sw);
 
    try {
       URL url = new URL (urlString);
 
     // Popup Window to request username/password password
       MyAuthenticator ma = new MyAuthenticator();
       String userPassword = ma.getPasswordAuthentication(this, url.getHost());
 
      // Encode String
       String encoding = new sun.misc.BASE64Encoder().encode (userPassword.getBytes());
 
      // or
       // String encoding = Base64Converter.encode (userPassword.getBytes());
 
      // Need to work with URLConnection to set request property
       URLConnection uc = url.openConnection();
       uc.setRequestProperty  ("Authorization", "Basic " + encoding);
       InputStream content = (InputStream)uc.getInputStream();
       BufferedReader in   = 
         new BufferedReader (new InputStreamReader (content));
       String line;
       while ((line = in.readLine()) != null) {
         pw.println (line);
       }
     } catch (MalformedURLException e) {
       pw.println ("Invalid URL");
     } catch (IOException e) {
       pw.println ("Error reading URL");
     }
     return sw.toString();
   }
 
 
  public static void main (String args[]) {
     Frame f = new Auth11();
     f.setSize(300, 300);
     f.setVisible (true);
   }
 
  class MyAuthenticator {
     String getPasswordAuthentication(Frame f, String prompt) {
       final Dialog jd = new Dialog (f, "Enter password", true);
       jd.setLayout (new GridLayout (0, 1));
       Label jl = new Label (prompt);
       jd.add (jl);
       TextField username = new TextField();
       username.setBackground (Color.lightGray);
       jd.add (username);
       TextField password = new TextField();
       password.setEchoChar ('*');
       password.setBackground (Color.lightGray);
       jd.add (password);
       Button jb = new Button ("OK");
       jd.add (jb);
       jb.addActionListener (new ActionListener() {
         public void actionPerformed (ActionEvent e) {
           jd.dispose();
         }
       });
       jd.pack();
       jd.setVisible(true);
       return username.getText() + ":" + password.getText();
     }
   }
 
}
      /*********************************************************************
      * BASE 64 encoding of a String or an array of bytes.
      *
      * See also RFC 1421.
      * 
      * @author
      *    Unknown
      *  @author
      *    David W. Croft
      * @version
      *    1998-06-08
      *********************************************************************/
 
     public class  Base64Converter
      //////////////////////////////////////////////////////////////////////
      //////////////////////////////////////////////////////////////////////
      {
 
     public static final char [ ]  alphabet = {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',   //  0 to  7
        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',   //  8 to 15
        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',   // 16 to 23
        'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',   // 24 to 31
        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',   // 32 to 39
        'o', 'p', 'q', 'r', 's', 't', 'u', 'v',   // 40 to 47
        'w', 'x', 'y', 'z', '0', '1', '2', '3',   // 48 to 55
        '4', '5', '6', '7', '8', '9', '+', '/' }; // 56 to 63
 
     //////////////////////////////////////////////////////////////////////
      //////////////////////////////////////////////////////////////////////
 
     public static String  encode ( String  s )
      //////////////////////////////////////////////////////////////////////
      {
        return encode ( s.getBytes ( ) );
      }
 
     public static String  encode ( byte [ ]  octetString )
      //////////////////////////////////////////////////////////////////////
      {
        int  bits24;
        int  bits6;
 
       char [ ]  out
          = new char [ ( ( octetString.length - 1 ) / 3 + 1 ) * 4 ];
 
       int outIndex = 0;
        int i        = 0;
 
       while ( ( i + 3 ) <= octetString.length )
        {
          // store the octets
          bits24  = ( octetString [ i++ ] & 0xFF ) << 16; 
          bits24 |= ( octetString [ i++ ] & 0xFF ) <<  8; 
          bits24 |= ( octetString [ i++ ] & 0xFF ) <<  0;
 
         bits6 = ( bits24 & 0x00FC0000 ) >> 18; 
          out [ outIndex++ ] = alphabet [ bits6 ];
          bits6 = ( bits24 & 0x0003F000 ) >> 12; 
          out [ outIndex++ ] = alphabet [ bits6 ];
          bits6 = ( bits24 & 0x00000FC0 ) >> 6; 
          out [ outIndex++ ] = alphabet [ bits6 ];
          bits6 = ( bits24 & 0x0000003F );
          out [ outIndex++ ] = alphabet [ bits6 ]; 
        }
 
       if ( octetString.length - i == 2 )
        {
          // store the octets 
          bits24  = ( octetString [ i     ] & 0xFF ) << 16; 
          bits24 |= ( octetString [ i + 1 ] & 0xFF ) <<  8;
 
         bits6 = ( bits24 & 0x00FC0000 ) >> 18;
          out [ outIndex++ ] = alphabet [ bits6 ]; 
          bits6 = ( bits24 & 0x0003F000 ) >> 12; 
          out [ outIndex++ ] = alphabet [ bits6 ]; 
          bits6 = ( bits24 & 0x00000FC0 ) >> 6; 
          out [ outIndex++ ] = alphabet [ bits6 ];
 
         // padding
          out [ outIndex++ ] = '='; 
        }
        else if ( octetString.length - i == 1 )
        {
          // store the octets 
          bits24 = ( octetString [ i ] & 0xFF ) << 16;
 
         bits6 = ( bits24 & 0x00FC0000 ) >> 18;
          out [ outIndex++ ] = alphabet [ bits6 ];
          bits6 = ( bits24 & 0x0003F000 ) >> 12; 
          out [ outIndex++ ] = alphabet [ bits6 ];
 
         // padding
          out [ outIndex++ ] = '='; 
          out [ outIndex++ ] = '='; 
        }
 
       return new String ( out );
      }
 
     //////////////////////////////////////////////////////////////////////
      //////////////////////////////////////////////////////////////////////
      }
Remember that using this program will require you to find a URL with a username and password that you know. If you happen to have acquired a free account from Discover Brokerage Direct for the Authenticator tip, you can use it again here. Otherwise, you can get a free password at the registration desk. Then, as with the last tip, you can try the program with a URL of http://www.lombard.com/cgi-bin/Quotes/quote.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comments (4)
Login
Forgot your account info?

About using sun.misc BASE64Encoder....By Anonymous on January 25, 2010, 7:54 pmPlease do not do that. See http://java.sun.com/products/jdk/faq/faq-sun-packages.html. Apache Commons has an alternative here: http://commons.apache.org/codec/userguide.html.

Reply | Read entire comment

ThanksBy Anonymous on August 31, 2009, 10:27 amVery good job and explanation

Reply | Read entire comment

using jvm popupBy Anonymous on April 22, 2009, 11:12 amI am loading an applet in web page which is accessed via a proxy. When I am loading it the JVM is throwing a popup for username/password. Is there any way to access...

Reply | Read entire comment

htaccess authenticationBy Anonymous on March 26, 2009, 10:53 pmVery very useful code. Thanks for providing to us. Nanaji

Reply | Read entire comment

View all comments

Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources
  • RFC 1113 (describes encoding format) http://andrew2.andrew.cmu.edu/rfc/rfc1113.html