We have a static method in a utility class that will download a file from a URL. An authenticator has been set up so that if a username and password is required, the credentials can be retrieved. The problem is that the credentials from the first successful connection are used for every connection afterwords, as long as the credentials are valid. This is a problem because our code is multi user, and since the credentials are not checked for every connection, it's possible that a user without proper credentials could download a file.
Here's the code we're using
private static URLAuthenticator auth;
public static File download(String url, String username, String password, File newFile)
{
auth.set(username, password);
Authenticator.setDefault(auth);
URL fURL = new URL(url);
OutputStream out = new BufferedOutputStream(new FileOutputStream(newFile));
URLConnection conn = fURL.openConnection();
InputStream in = conn.getInputStream();
try
{
copyStream(in, out);
}
finally
{
if (in != null)
in.close();
if (out != null)
out.close();
}
return newFile;
}
public class URLAuthenticator extends Authenticator
{
private String username;
private String password;
public URLAuthenticator(String username, String password)
{
set(username, password);
}
public void set(String username, String password)
{
this.username = username;
this.password = password;
}
protected PasswordAuthentication getPasswordAuthentication()
{
log.debug("Retrieving credentials '" + username + "', '" + password + "'.");
return new PasswordAuthentication(username, password.toCharArray());
}
}
I only see the log statement from getPasswordAuthentication once, the first time that a file is downloaded. After that first successful attempt, getPasswordAuthentication is not called again, even though the credentials have been reset. The result is that after the first successful connection, invalid credentials can be entered, and a successful connection can still be made. Is this possibly a result of the download method being static, and in a static class?
Edit
I forgot to mention that this is in a JSF webapp running under tomcat - maybe one of those technologies is setting some default credentials somewhere?
I've pulled the URLAuthenticator out into its own class, and made it as non-static as possible, but the problem still exists. I've read that if the default authenticator is set to null with Authenticator.setDefault(null), then on windows the NTLM authentication will be used. That shouldn't be the problem here since I'm setting the Authenticator everytime, but I thought I'd throw it out there. The NTLM authentication is definately getting used, because if the server is run as a user that has access to the downloaded file, the credentials aren't even asked for, the file just downloads. So something obviously is grabbing my credentials and passing them in before the authenticator is called.
I've figured something out at least. It appears that this behavior is a bug. A workaround is to use a Sun specific class to explicitly reset the cache, like so:
import sun.net.www.protocol.http.AuthCacheValue;
import sun.net.www.protocol.http.AuthCacheImpl;
....
AuthCacheValue.setAuthCache(new AuthCacheImpl());
Authenticator.setDefault(new URLAuthenticator(username, password));
I'm resetting the AuthCache at the top of the download function described in the question. During compile, you'll get warnings about using these classes. This doesn't completely fix the problem: if NTLM authentication works, the authenticator still won't get called, but as long as the server is running under a user that has does not have permission for the requested file, this should clear the cache out.
Facing the same problem, none of these answers worked for me. It took me some time and looking through the java runtime source to figure this out.
Sun.net.www.protocol.http.ntlm.NTLMAuthentication attempts to use transparent authentication, what is basically use of current user credentials to login to remote server. In my server to server scenario (Java EE server to Sharepoint) this wasn't acceptable.
In order to disable transparent authentication we need to let authentication provider know that connection is not trusted and it needs to authenticate with every call:
static {
NTLMAuthenticationCallback.setNTLMAuthenticationCallback(new NTLMAuthenticationCallback()
{
#Override
public boolean isTrustedSite(URL url)
{
return false;
}
});
}
Here is the working( i.e., getPasswordAuthentication() called for every request) code. You will see compilation warning which can be ignored.
static class MyCache implements sun.net.www.protocol.http.AuthCache{
public void put(String pkey, sun.net.www.protocol.http.AuthCacheValue value){
}
public sun.net.www.protocol.http.AuthCacheValue get(String pkey, String skey){
return null;
}
public void remove(String pkey, sun.net.www.protocol.http.AuthCacheValue entry){
}
}
static{
sun.net.www.protocol.http.AuthCacheValue.setAuthCache(new MyCache());
}
It looks like in your finally block you need to call
Authenticator.setDefault(null);
Related
We have NTLM authentication from Java against MS Sharepoint working in all environments but from within Weblogic Server.
In WLS we see that Authenticator#getPasswordAuthentication returns 'basic' instead of 'ntlm'. What could the reason for that behavior be? The same code works just fine if run standalone or from within Tomcat (using the same JVM).
The relevant code as follows:
NtlmAuthenticator authenticator = new NtlmAuthenticator(configParameters.getNtlmUsername(),
configParameters.getNtlmPassword(), configParameters.getNtlmDomain());
log.info("JVM running with security manager enabled: {}", System.getSecurityManager() != null);
// requires NetPermission 'setDefaultAuthenticator' if security manager enabled
Authenticator.setDefault(authenticator);
public class NtlmAuthenticator extends Authenticator {
private char[] password;
private String userAuthentication;
public NtlmAuthenticator(String username, String password, String domain) {
userAuthentication = username;
if (StringUtils.isNotBlank(domain)) {
// According to
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa380525(v=vs.85).aspx
userAuthentication = domain + "\\" + username;
}
this.password = password.toCharArray();
}
#Override
public PasswordAuthentication getPasswordAuthentication() {
log.debug("Scheme: '{}'", getRequestingScheme());
return new PasswordAuthentication(userAuthentication, password);
}
}
Short answer 1
Use a patched version of Apache httpclient: https://issues.apache.org/jira/browse/HTTPCLIENT-1881
Short answer 2
Set -DUseSunHttpHandler=true when you start WebLogic Server. Some references: https://docs.oracle.com/en/cloud/paas/javase-cloud/csjsu/you-should-now-set-sun-http-handlers-property-value-true-when-making-outbound-http-s-calls.html, Impact/Risk on enable -DuseSunHttpHandler on Weblogic10.3.0 & https://stackoverflow.com/a/27931816/131929 (both a bit older).
At some point I during (remote) debugging noticed that I didn't have java.net.http.* objects on the stack but weblogic.net.http.*. WTF I thought...and yes, WLS does replace the standard Sun networking stack by default.
I'm beginner,
I'm writing a program which logins into my g-mail accounts and checks the messages in the Inbox.
To do this I'm using imaps ("imaps.gmail.com").
But what my problem is while logging into all the accounts, checking the messages in it one by one.
Program Reporting an error/kind of:
javax.mail.AuthenticationFailedException: [ALERT] Please log in via
your web browser: some url (Failure) imaps: xxxx#gmail.com for the
second and other accounts...
From this What I understand is I'm not closing sessions, store properly. So,Program is unable to check in the respective second and rest mail accounts..
Someone Help (With clarification)..
And the architecture of my code:
public class InboxRead {
public static boolean release = false;
private Session session = null;
public void checkInbox(String username, String password) {
// logging into account using imaps..
Properties props = System.getProperties();
props.setProperty("mail.store.protocol", "imaps");
session = Session.getDefaultInstance(props, null);
store = session.getStore("imaps");
store.connect("imap.gmail.com", username, password);
System.out.println(store);
// reading inbox... closing all folders here
store.close();
session = null;
release = true;
}
public static void main(String[] args) {
for(;;) { // gives no of accounts with credentials..
do {
String username = "xxx#gmail.com";
String password = "xxxx";
InboxRead.checkInbox(username, password);
} while(release);
}
}
}
...
Its a Google gateway thrown error. Google maintain high security in the aspect of profile access or even mail access but even that can be controlled by the user using below link Google secure control
go to that link with your google account and select "turn on" and then try to execute your program.
This question is Extension of my previous question on this SO question "How to connect XMPP bosh server using java smack library?"
I am using Java as server side language. I have successfully implement xmpp BOSH connection using smach-jbosh thanks to #Deuteu for helping me to achieve this, so far I have modify jbosh's BOSHClient.java file and added two getter method for extracting RID and SID.
Now I have RID and SID on my app server (I am using Apache Tomcat). I need to pass this credential to Strophe (web client) so that it can attach to connection.
Here I have some doubt.
When to disconnect bosh Connection establish from the app server? before passing sid, rid and jid to strophe or after passing sid, rid and jid to strophe?
As per my observation during implementation for the same, I have observed that once bosh connection from the app server has been disconnected, session is expired and SID and RID is no longer useful!!!
I have implemented this logic (Establishing bosh connection and Extracting sid and rid) on a Servlet, here once response has been send from Servlet, Thread will get expired and end BOSH connection will get terminated, so I am not able perform `Attach()` on strophe as session is expired.
Can somebody help me with that problem?
I believe #fpsColton's answer is correct - I'm just added extra info for clarity. As requested on linked thread here is the code changes I made on this - note: I only added the parts where I've labelled "DH"
In BOSHConnection:
// DH: function to preserve current api
public void login(String username, String password, String resource)
throws XMPPException {
login(username, password, resource, false);
}
// DH: Most of this is existing login function, but added prebind parameter
// to allow leaving function after all required pre-bind steps done and before
// presence stanza gets sent (sent from attach in XMPP client)
public void login(String username, String password, String resource, boolean preBind)
throws XMPPException {
if (!isConnected()) {
throw new IllegalStateException("Not connected to server.");
}
if (authenticated) {
throw new IllegalStateException("Already logged in to server.");
}
// Do partial version of nameprep on the username.
username = username.toLowerCase().trim();
String response;
if (config.isSASLAuthenticationEnabled()
&& saslAuthentication.hasNonAnonymousAuthentication()) {
// Authenticate using SASL
if (password != null) {
response = saslAuthentication.authenticate(username, password, resource);
} else {
response = saslAuthentication.authenticate(username, resource, config.getCallbackHandler());
}
} else {
// Authenticate using Non-SASL
response = new NonSASLAuthentication(this).authenticate(username, password, resource);
}
// Indicate that we're now authenticated.
authenticated = true;
anonymous = false;
// DH: Prebind only requires connect and authenticate
if (preBind) {
return;
}
// Set the user.
if (response != null) {
this.user = response;
// Update the serviceName with the one returned by the server
config.setServiceName(StringUtils.parseServer(response));
} else {
this.user = username + "#" + getServiceName();
if (resource != null) {
this.user += "/" + resource;
}
}
// Create the roster if it is not a reconnection.
if (this.roster == null) {
this.roster = new Roster(this);
}
if (config.isRosterLoadedAtLogin()) {
this.roster.reload();
}
// Set presence to online.
if (config.isSendPresence()) {
sendPacket(new Presence(Presence.Type.available));
}
// Stores the autentication for future reconnection
config.setLoginInfo(username, password, resource);
// If debugging is enabled, change the the debug window title to include
// the
// name we are now logged-in as.l
if (config.isDebuggerEnabled() && debugger != null) {
debugger.userHasLogged(user);
}
}
and
// DH
#Override
public void disconnect() {
client.close();
}
then my Client-side (Web Server) wrapper class - for connecting from within JSP is:
Note: This is proving code rather than production - so there's some stuff in here you may not want.
public class SmackBoshConnector {
private String sessionID = null;
private String authID = null;
private Long requestID = 0L;
private String packetID = null;
private boolean connected = false;
public boolean connect(String userName, String password, String host, int port, final String xmppService) {
boolean success = false;
try {
Enumeration<SaslClientFactory> saslFacts = Sasl.getSaslClientFactories();
if (!saslFacts.hasMoreElements()) {
System.out.println("Sasl Provider not pre-loaded");
int added = Security.addProvider(new com.sun.security.sasl.Provider());
if (added == -1) {
System.out.println("Sasl Provider could not be loaded");
System.exit(added);
}
else {
System.out.println("Sasl Provider added");
}
}
BOSHConfiguration config = new BOSHConfiguration(false, host, port, "/http-bind/", xmppService);
BOSHConnection connection = new BOSHConnection(config);
PacketListener sndListener = new PacketListener() {
#Override
public void processPacket(Packet packet) {
SmackBoshConnector.this.packetID = packet.getPacketID();
System.out.println("Send PacketId["+packetID+"] to["+packet.toXML()+"]");
}
};
PacketListener rcvListener = new PacketListener() {
#Override
public void processPacket(Packet packet) {
SmackBoshConnector.this.packetID = packet.getPacketID();
System.out.println("Rcvd PacketId["+packetID+"] to["+packet.toXML()+"]");
}
};
PacketFilter packetFilter = new PacketFilter() {
#Override
public boolean accept(Packet packet) {
return true;
}
};
connection.addPacketSendingListener(sndListener, packetFilter);
connection.addPacketListener(rcvListener, packetFilter);
connection.connect();
// login with pre-bind only
connection.login(userName, password, "", true);
authID = connection.getConnectionID();
BOSHClient client = connection.getClient();
sessionID = client.getSid();
requestID = client.getRid();
System.out.println("Connected ["+authID+"] sid["+sessionID+"] rid["+requestID+"]");
success = true;
connected = true;
try {
Thread.yield();
Thread.sleep(500);
}
catch (InterruptedException e) {
// Ignore
}
finally {
connection.disconnect();
}
} catch (XMPPException ex) {
Logger.getLogger(SmackBoshConnector.class.getName()).log(Level.SEVERE, null, ex);
}
return success;
}
public boolean isConnected() {
return connected;
}
public String getSessionID() {
return sessionID;
}
public String getAuthID() {
return authID;
}
public String getRequestIDAsString() {
return Long.toString(requestID);
}
public String getNextRequestIDAsString() {
return Long.toString(requestID+1);
}
public static void main(String[] args) {
SmackBoshConnector bc = new SmackBoshConnector();
bc.connect("dazed", "i3ji44mj7k2qt14djct0t5o709", "192.168.2.15", 5280, "my.xmppservice.com");
}
}
I confess that I'm don't fully remember why I put the Thread.yield and Thread.sleep(1/2 sec) in here - I think - as you can see with added PacketListener - the lower level functions return after sending data and before getting a response back from the server - and if you disconnect before the server has sent it's response then it (also) causes it to clean up the session and things won't work. However it may be that, as #fpsColton says, this dicsonnect() isn't actually required.
Edit: I now remember a bit more about whay I included sleep() and yield(). I noticed that Smack library includes sleep() in several places, including XMPPConnection.shutdown() as per source. Plus in terms of yield() I had problems in my environment (Java in Oracle Database - probably untypical) when it wasn't included - as per Smack Forum Thread.
Good luck.
After you have created a BOSH session with smack and have extracted the SID+RID values, you need to pass them to Strophe's attach() and from here on out you need to let strophe deal with this connection. Once Strophe has attached, you do not want your server to be doing anything to the connection at all.
If your server side code sends any messages at all to the connection manager after strophe has attached, it's likely that it will send a invalid RID which will cause your session to terminate.
Again, once the session has been established and is usable by strophe, do not attempt to continue using it from the server side. After your server side bosh client completes authentication and you've passed the SID+RID to the page, just destroy the server side connection object, don't attempt to disconnect or anything as this will end your session.
The thing you need to remember is, unlike traditional XMPP connections over TCP, BOSH clients do NOT maintain a persistent connection to the server (this is why we use BOSH in web applications). So there is nothing to disconnect. The persistent connection is actually between the XMPP server and the BOSH connection manager, it's not something you need to deal with. So when you call disconnect from your server side BOSH client, you're telling the connection manager to end the session and close it's connection to the XMPP server, which completely defeats the purpose of creating the session in the first place.
I have a web application, written in Java, that makes a JAX-WS web service call to an exchange server using Exchange Web Services.
When I compile and run the application using Java 1.6.0_34, it works fine.
If I compile and run it with Java 1.7.0_07, I get the following error:
com.sun.xml.ws.client.ClientTransportException: request requires HTTP authentication: Unauthorized
at com.sun.xml.ws.transport.http.client.HttpClientTransport.checkResponseCode(HttpClientTransport.java:212)
at com.sun.xml.ws.transport.http.client.HttpTransportPipe.process(HttpTransportPipe.java:149)
at com.sun.xml.ws.transport.http.client.HttpTransportPipe.processRequest(HttpTransportPipe.java:86)
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:595)
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:554)
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:539)
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:436)
at com.sun.xml.ws.client.Stub.process(Stub.java:248)
at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:135)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:109)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:89)
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:118)
at $Proxy62.getUserAvailability(Unknown Source)
...
This also occurs if I compile it with 1.6.0_34 and run it with 1.7.0_07.
I looked for changes to JAX-WS between Java 6 and 7, but I only found some notes about possible compile errors.
I'm not sure what to look at next, as obviously the authentication code hasn't been changed, so why is it failing?
Here's the method that I think should be setting up the authentication:
/**
* Set up Authentication
*
*/
protected void setupAuthenticator(){
if(authenticator == null){
authenticator = new Authenticator(){
protected PasswordAuthentication getPasswordAuthentication(){
String superusername = ExchangeServerPortFactory.this.adDomain
+ "\\"
+ ExchangeServerPortFactory.this.username;
String superPassword = ExchangeServerPortFactory.this.password;
return new PasswordAuthentication(superusername
,superPassword.toCharArray());
}
};
Authenticator.setDefault(authenticator);
}
}
I tried changing "\\" to "\\\\" because I found a message on a forum somewhere where this solved a similar problem, but it didn't do me any good.
Ok, this is what I got so far, you are calling a exchange service, but you are using a basic authentication method right now, what you need is a NTML authentication, i have no clue why this is working with j6 and not with j7, but i suppose that the client is parsing your request based on a the service's wsdl which may define that it's an exchange service, and maybe j7 have a specific authenticatior for NTML authentication, but again, it's a very long shot and i'm assuming lot's of things.
(All credits to Marcel Levy)
First, create an authenticator:
import java.net.Authenticator;
import java.net.PasswordAuthentication;
public class NtlmAuthenticator extends Authenticator {
private final String username;
private final char[] password;
public NtlmAuthenticator(final String username, final String password) {
super();
this.username = new String(username);
this.password = password.toCharArray();
}
#Override
public PasswordAuthentication getPasswordAuthentication() {
return (new PasswordAuthentication (username, password));
}
}
In your application, set up the authenticator as the default:
String username = "DOMAIN\\USERNAME";
String password = "PASSWORD"
NtlmAuthenticator authenticator = new NtlmAuthenticator(username, password);
Authenticator.setDefault(authenticator);
You probably tried this, if so, please ignore this answer.
It seems to work in 1.7.0u10 or later.
I can only guess that this is because of bug 8003948 which was fixed in that release.
I'm using the following java code to authenticate an LDAP user. This will succeed if the object exists with the specified username and password. However, I don't see any properties on the user which would allow me to activate/deactivate the user. Specifically, I just want to set a property on the user which would make authentication fail without actually deleting the entire object.
Can this be done directly or do I need to write separate code which loads the user and checks that a field is marked active or inactive?
public static boolean authenticate(String url, String securityAuthentication, String securityPrincipal, String usernameKey, String username, String password) {
try {
Hashtable env = new Hashtable();
env.put(InitialContext.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(InitialContext.PROVIDER_URL, url);
env.put(InitialContext.SECURITY_AUTHENTICATION, securityAuthentication);
env.put(InitialContext.SECURITY_PRINCIPAL, usernameKey +"="+ username + securityPrincipal);
env.put(InitialContext.SECURITY_CREDENTIALS, password );
// Create the initial context
DirContext ctx = new InitialDirContext(env);
return true;
} catch (NamingException e) {
return false;
}
}
EDIT: I'm told that in we have one that's active directory and another which is 389 Directory Server.