To authenticate a request, I use Authenticator.setDefault
which is VM wide...
What If I want to separate different webservices
and each one are aware of their authentication credentials.
Do I need to Authenticator.setDefault
for each request ?
This may not work if there are concurrent connection with mixed webservices...
Building on Mike's response above I have the following solution, because while I much appreciate the general idea (that's why I've copied it ;-) , I see a few problems with it:
Below solution I think solves these issues.
/**
* Authenticator which keeps credentials to be passed to the requestor based on authority of the requesting URL. The
* authority is <pre>user:password@host:port</pre>, where all parts are optional except the host.
* <p>
* If the configured credentials are not found, the Authenticator will use the credentials embedded in the URL, if
* present. Embedded credentials are in the form of <pre>user:password@host:port</pre>
*
* @author Michael Fortin 2011-09-23
*/
public final class DefaultAuthenticator extends Authenticator {
private static final Logger LOG = Logger.getLogger(DefaultAuthenticator.class.getName());
private static DefaultAuthenticator instance;
private Map<String, PasswordAuthentication> authInfo = new HashMap<String, PasswordAuthentication>();
private DefaultAuthenticator() {
}
public static synchronized DefaultAuthenticator getInstance() {
if (instance == null) {
instance = new DefaultAuthenticator();
Authenticator.setDefault(instance);
}
return instance;
}
// unit testing
static void reset() {
instance = null;
Authenticator.setDefault(null);
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
String requestorInfo = getRequestorInfo();
LOG.info(getRequestorType() + " at \"" + getRequestingPrompt() + "\" is requesting " + getRequestingScheme()
+ " password authentication for \"" + requestorInfo + "\"");
if (authInfo.containsKey(requestorInfo)) {
return authInfo.get(requestorInfo);
} else {
PasswordAuthentication pa = getEmbeddedCredentials(getRequestingURL());
if (pa == null) {
LOG.warning("No authentication information");
}
return pa;
}
}
/**
* Register the authentication information for a given URL.
*
* @param url - the URL that will request authorization
* @param auth - the {@link PasswordAuthentication} for this URL providing the credentials
*/
public void register(URL url, PasswordAuthentication auth) {
String requestorInfo = getRequestorInfo(url.getHost(), url.getPort());
authInfo.put(requestorInfo, auth);
}
/**
* Get the requestor info based on info provided.
*
* @param host - hostname of requestor
* @param port - TCP/IP port
* @return requestor info string
*/
private String getRequestorInfo(String host, int port) {
String fullHostname;
try {
InetAddress addr = InetAddress.getByName(host);
fullHostname = addr.getCanonicalHostName();
} catch (UnknownHostException e) {
fullHostname = host;
}
if (port == -1) {
return fullHostname;
} else {
return fullHostname + ":" + port;
}
}
/**
* Get the requestor info for the request currently being processed by this Authenticator.
*
* @return requestor info string for current request
*/
private String getRequestorInfo() {
String host;
InetAddress addr = getRequestingSite();
if (addr == null) {
host = getRequestingHost();
} else {
host = addr.getCanonicalHostName();
}
return getRequestorInfo(host, getRequestingPort());
}
/**
* Get the credentials from the requesting URL.
*
* @param url - URL to get the credentials from (can be null, method will return null)
* @return PasswordAuthentication with credentials from URL or null if URL contains no credentials or if URL is
* null itself
*/
PasswordAuthentication getEmbeddedCredentials(URL url) {
if (url == null) {
return null;
}
String userInfo = url.getUserInfo();
int colon = userInfo == null ? -1 : userInfo.indexOf(":");
if (colon == -1) {
return null;
} else {
String userName = userInfo.substring(0, colon);
String pass = userInfo.substring(colon + 1);
return new PasswordAuthentication(userName, pass.toCharArray());
}
}
}
While I'm at it, let me give you my unit tests (JUnit 4).
/**
* @author Paul Balm - May 10 2012
*/
public class DefaultAuthenticatorTest {
private static final Logger LOG = Logger.getLogger(DefaultAuthenticatorTest.class.getName());
@Before
public void setUp() throws Exception {
DefaultAuthenticator.reset();
DefaultAuthenticator.getInstance();
}
@After
public void tearDown() {
DefaultAuthenticator.reset();
}
@Test
public void testRequestAuthenticationFromURL() throws MalformedURLException, UnknownHostException {
Map<String, String[]> urls = generateURLs();
for (String urlStr : urls.keySet()) {
String[] userInfo = urls.get(urlStr);
LOG.info("Testing: " + urlStr);
URL url = new URL(urlStr);
request(userInfo[1], userInfo[2], url, true);
}
}
@Test
public void testRequestAuthenticationRegistered() throws UnknownHostException, MalformedURLException {
Map<String, String[]> urls = generateURLs();
for (String urlStr : urls.keySet()) {
String[] userInfo = urls.get(urlStr);
LOG.info("Testing: " + urlStr);
URL url = new URL(urlStr);
DefaultAuthenticator.reset();
DefaultAuthenticator auth = DefaultAuthenticator.getInstance();
String userName = userInfo[1];
String password = userInfo[2];
if (password != null) {
// You can't register a null password
auth.register(url, new PasswordAuthentication(userName, password.toCharArray()));
}
request(userName, password, url, false);
}
}
/**
* Generate a bunch of URLs mapped to String array. The String array has the following elements:
* - user info part of URL,
* - expected user,
* - expected password
*
* Note that the keys of the maps must be strings and not URL objects, because of the way URL.equals is
* implemented. This method does not consider the credentials.
*
* @throws MalformedURLException
*/
Map<String, String[]> generateURLs() {
String[] hosts = new String[]{ "127.0.0.1", "localhost.localdomain"};
List<String[]> userData = new ArrayList<String[]>();
// normal cases
userData.add(new String[] { "user:pass@", "user", "pass" }); // results in: http://user:pass@[host]
userData.add(new String[] { "", null, null });
// unexpected cases
userData.add(new String[] { "@", null, null });
userData.add(new String[] { ":@", "", "" });
userData.add(new String[] { "user:@", "user", "" });
userData.add(new String[] { ":pass@", "", "pass" });
Map<String, String[]> urls = new HashMap<String, String[]>();
for (String[] userInfo : userData) {
for (String host : hosts) {
String s = "http://" + userInfo[0] + host;
urls.put(s, userInfo);
}
}
LOG.info("" + urls.size() + " URLs prepared");
return urls;
}
private void request(String expectedUser, String expectedPass, URL url, boolean inURL)
throws UnknownHostException {
String host = url.getHost();
InetAddress addr = InetAddress.getAllByName(host)[0];
int port = url.getPort();
String protocol = url.getProtocol();
String prompt = ""; // prompt for the user when asking for the credentials
String scheme = "basic"; // or digest
RequestorType reqType = RequestorType.SERVER;
PasswordAuthentication credentials =
Authenticator.requestPasswordAuthentication(addr, port, protocol, prompt, scheme);
// If the credentials are in the URL, you can't find them using this method because we're not passing the URL
checkCredentials(url, inURL ? null : expectedUser, inURL ? null : expectedPass, credentials);
credentials = Authenticator.requestPasswordAuthentication(host, addr, port, protocol, prompt, scheme);
// If the credentials are in the URL, you can't find them using this method because we're not passing the URL
checkCredentials(url, inURL ? null : expectedUser, inURL ? null : expectedPass, credentials);
credentials = Authenticator.requestPasswordAuthentication(host, addr, port, protocol, prompt, scheme, url, reqType);
checkCredentials(url, expectedUser, expectedPass, credentials);
}
private void checkCredentials(URL url, String expectedUser, String expectedPass, PasswordAuthentication credentials) {
if (expectedUser == null) {
Assert.assertNull(url.toString(), credentials);
} else {
Assert.assertNotNull(url.toString(), credentials);
Assert.assertEquals(url.toString(), expectedUser, credentials.getUserName());
if (expectedPass == null) {
Assert.assertNull(url.toString(), credentials.getPassword());
} else {
Assert.assertArrayEquals(url.toString(), expectedPass.toCharArray(), credentials.getPassword());
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With