In questo articolo Java sarà mostrato il codice sorgente della classe Proxy che crea una connessione di rete autenticata presso un proxy server per raggiungere e leggere una risorsa remota.
La classe è stata creata per poter interrogare, tramite il metodo POST del protocollo di rete https, un web service esterno alla rete aziendale, nella quale vi sono i server WAS (WebSphere Application Server) sui quali sono deployate le applicazioni J2EE che consumano il suddetto servizio web. Ovviamente, la classe Proxy funziona correttamente anche con i protocolli di rete http ed tcp. Non è stato ancora eseguito alcun test con il protocollo ftp.
Nota
La classe è stata creata sfruttando il package java.net delle librerie J2EE 1.3 .
Non saranno spiegate, se non perchè ritenute necessarie, le specifiche di sicurezza per interfacciare un proxy server fornendo le proprie credenziali al fine di creare una connessione di rete in grado di oltrepassare lo stesso proxy server.
package net.ma.core;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Properties;
import java.util.Enumeration;
import java.util.Hashtable;
import sun.misc.BASE64Encoder;
/**
* Esegue un'autenticazione presso un proxy server
* per ottenere risorse esterne.
* Utilizza come realm il token "Proxy-Authorization"
* assegnadoli il valore in base64 della stringa "username:password".
*
* @author Mirko Agrati
* @version 1.0
*/
public final class Proxy {
/** Nome del Server Proxy. */
private static String PROXY_NAME;
/** Numero della porta su cui è in ascolto il Proxy Server. */
private static String PROXY_PORT;
/** Valore realm in base64 della coppia username:password. */
private static String ENCODED_REALM_VALUE;
/**
* Inizializza i campi static.
*/
static{
PROXY_NAME = "proxy.intranet.ma.net";
PROXY_PORT = "8080";
ENCODED_REALM_VALUE =
new BASE64Encoder().encode("username:password".getBytes());
}
/**
* Configura le proprietà necessarie al runtime Java.
* Solitamente queste proprietà vengono fornite dal client(es: browser)
* durante la creazione di una connessione,
* ma girando qui lato server ...
*/
private static void setProperties(){
Properties props = System.getProperties();
props.put("proxySet", "true" );
props.put("proxyHost", PROXY_NAME);
props.put("proxyPort", PROXY_PORT );
//Setta un gestore per connessioni sicure(https, ssl, ..).
props.put("java.protocol.handler.pkgs", "java.net.URL" );
}
/**
* Crea una connessione autenticata dal proxy server {@link #PROXY_NAME} .
*
* @param urlpath L'indirizzo della risorsa da recuperare.
* @return Una connessione autenticata pronta all'uso.
*/
public static final URLConnection getAuthURLConnection(String urlpath)
throws MalformedURLException, IOException{
setProperties();
URL url = new URL(urlpath);
URLConnection connection = url.openConnection();
connection.setRequestProperty(
"Proxy-Authorization", ENCODED_REALM_VALUE );
connection.setDoInput( true );
connection.setDoOutput( true );
return connection;
}
/**
* Legge una risorsa remota e ne restituisce il contenuto in una stringa.
*
* @param url L'indirizzo della risorsa da leggere.
* @param params Parametri da passare in POST
* durante la richiesta HTTP.
* @return Il contenuto della risorsa remota.
* @throws MalformedURLException
* @throws IOException
*/
public static final String getRemoteResource
(String url, Hashtable params)
throws MalformedURLException, IOException{
URLConnection connection = getAuthURLConnection(url);
//Apro un socket di rete verso l'host che espone il servizio
DataOutputStream output =
new DataOutputStream(connection.getOutputStream());
Enumeration pnames = params.keys();
String query = "";
/*
* Creo la querystring da inviare al server remoto
* concatenando '&nome=valore' di ogni parametro.
*/
while(pnames.hasMoreElements()){
String paramname = (String) pnames.nextElement();
String paramvalue = (String)params.get(paramname);
query +=
paramname + "=" + URLEncoder.encode(paramvalue) + "&";
}
System.out.println(
"\nRisorsa richiesta: " + url +
"\nparametri inviati: " + query);
//Invio sul socket la querystring con le coppie nome/valore.
output.writeBytes( query );
//Chiudo socket in uscita
output.close();
/*
* Leggo il contenuto della risorsa remota e
* lo conservo all'interno di uno StringBuffer.
*/
StringBuffer response = new StringBuffer();
DataInputStream input =
new DataInputStream(connection.getInputStream() );
for( int c = input.read(); c != -1; c = input.read() ){
response.append( (char)c );
}
//Chiudo il socket in ingresso.
input.close();
return response._toString();
}
/**
* Eseguo il metodo statico getRemoteResource(... , ...)
* e scrivo a video il contenuto della risorsa richiesta.
* In questo caso ricevo il codice sorgente HTML della pagina
* di ricerca di Google.it dopo aver eseguito una query
* con il mio nome e cognome.
*/
public static void main( String[] args ){
try{
String urlpath = "http://www.google.it/search";
Hashtable params = new Hashtable();
params.put("q", "mirko agrati");
System.out.println(getRemoteResource(urlpath, params));
}
catch( Exception e ){
System.out.println("Si è verificata un'eccezione:");
System.out.println(e);
e.printStackTrace();
}
}
}
Ad eccezione del metodo main(String[] args), la classe Proxy espone due metodi statici grazie ai quali si può creare una connessione autenticata e leggere il contenuto di una risorsa remota.
Il cuore della classe è rappresentato dal factory method getAuthURLConnection(String urlpath) il quale contiene la logica necessaria per poter interfacciare correttamente un proxy server, fornendo le credenziali in base64; ma è altrettanto fondamentale il metodo setProperties() che setta le proprietà di sistema necessarie per il corretto funzionamento delle operazioni di autenticazione.
La classe funziona correttamente ed ha delle buone performances.
Alla prossima,
MA.