I am trying to support authenticated proxies in my Java application. My understanding that the java.net.Proxy class does not support authentication, and you need to handle authentication yourself.
I have created a subclass of the java.net.Proxy class, that takes two additional parameters, username and password.
Implementing HTTP proxy authentication was quite easy, and the method getHttpProxyAuthenticationHeader simply returns the base64 encoded auth info, to pass to HttpUrlConnection or similar.
I'm having trouble with Sock proxies though. I cannot find any documentation on sending authentication to a Socks server. I'm unsure if I need to implement the SOCKS authentication protocol in my class using a method such as authenticateSocksProxy(OutputStream stream), and then call
authedProxy.authenticateSocksProxy(outputstream);
before using the socket like
outputstream.writeBytes(myData.getBytes());
Another option would be to return a byte[] of the authentication data and then write the data manually, instead of the class writing the authentication data.
I do not think the java.net.Authenticator, or System.setProperty methods will be of any use, since my implementation needs to work on a per-connection basis and be thread-safe.
Any help is much appreciated.
Was taken from JSocks project sources:
https://code.google.com/p/jsocks-mirror/source/browse/trunk/src/java/net/sourceforge/jsocks/socks/UserPasswordAuthentication.java
I think it's clean enough to understand full process:
/**
SOCKS5 User Password authentication scheme.
*/
public class UserPasswordAuthentication implements Authentication{
/**SOCKS ID for User/Password authentication method*/
public final static int METHOD_ID = 2;
String userName, password;
byte[] request;
/**
Create an instance of UserPasswordAuthentication.
#param userName User Name to send to SOCKS server.
#param password Password to send to SOCKS server.
*/
public UserPasswordAuthentication(String userName,String password){
this.userName = userName;
this.password = password;
formRequest();
}
/** Get the user name.
#return User name.
*/
public String getUser(){
return userName;
}
/** Get password
#return Password
*/
public String getPassword(){
return password;
}
/**
Does User/Password authentication as defined in rfc1929.
#return An array containnig in, out streams, or null if authentication
fails.
*/
public Object[] doSocksAuthentication(int methodId,
java.net.Socket proxySocket)
throws java.io.IOException{
if(methodId != METHOD_ID) return null;
java.io.InputStream in = proxySocket.getInputStream();
java.io.OutputStream out = proxySocket.getOutputStream();
out.write(request);
int version = in.read();
if(version < 0) return null; //Server closed connection
int status = in.read();
if(status != 0) return null; //Server closed connection, or auth failed.
return new Object[] {in,out};
}
//Private methods
//////////////////
/** Convert UserName password in to binary form, ready to be send to server*/
private void formRequest(){
byte[] user_bytes = userName.getBytes();
byte[] password_bytes = password.getBytes();
request = new byte[3+user_bytes.length+password_bytes.length];
request[0] = (byte) 1;
request[1] = (byte) user_bytes.length;
System.arraycopy(user_bytes,0,request,2,user_bytes.length);
request[2+user_bytes.length] = (byte) password_bytes.length;
System.arraycopy(password_bytes,0,
request,3+user_bytes.length,password_bytes.length);
}
}
Related
There is following way to configure the authentication header in Jersey API .
//Universal builder having different credentials for different schemes
HttpAuthenticationFeature feature = HttpAuthenticationFeature.universalBuilder()
.credentialsForBasic("username1", "password1")
.credentials("username2", "password2").build();
final Client client = ClientBuilder.newClient();
client.register(feature);
But not able to figure out how to pass extra parameter to authentication header for e.g. IntegatorKey, SendBehalfOf. those are specific REST service call.
In My Case to call REST service need to pass following parameter as part of authentication header.
Username
Password
IntegatorKey
SendBehalfOf
How should I achieve this using the Jersey API ?
You didn't provide enough information in your question. It's hard guessing what you are trying to achieve. You really should consider updating your question with more details.
Having a look at the superficial information you provided, I guess you are trying to access the DocuSign REST API. If so, you could create a ClientRequestFilter, as following:
public class DocuSignAuthenticator implements ClientRequestFilter {
private String user;
private String password;
private String integatorKey;
private String sendBehalfOf;
public DocuSignAuthenticator(String username, String password,
String integatorKey, String sendBehalfOf) {
this.username = username;
this.password = password;
this.integatorKey = integatorKey;
this.sendBehalfOf = sendBehalfOf;
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(
"X-DocuSign-Authentication", getAuthenticationHeader());
}
private String getAuthenticationHeader() {
StringBuilder builder = new StringBuilder();
builder.append("<DocuSignCredentials>");
builder.append("<SendOnBehalfOf>");
builder.append(sendBehalfOf);
builder.append("</SendOnBehalfOf>");
builder.append("<Username>");
builder.append(username);
builder.append("</Username>");
builder.append("<Password>");
builder.append(password);
builder.append("</Password>");
builder.append("<IntegratorKey>");
builder.append(integatorKey);
builder.append("</IntegratorKey>");
builder.append("</DocuSignCredentials>");
return builder.toString();
}
}
And register it when creating a Client instance:
Client client = ClientBuilder.newClient().register(
new DocuSignAuthenticator(username, password, integatorKey, sendBehalfOf));
This is a follow-up question to the one I asked yesterday.
The log-in page is supposed to redirect to main page after correct username and password input, with server side returning an empty string (""). If either is incorrect, server side code returns "Username or Password are incorrect".
The page functionality worked well but when I was testing using my client side code using a correct pair of username and password, it returns "Username or Password are incorrect", with response returning 200OK.
Below is my client side code:
public static void main(String[] args){
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient(config);
WebTarget target = client.target("http://localhost:8080
/qa-automation-console").path("authenticate");
Form form = new Form();
form.param("username", "username");
form.param("password", "password");
Response response = target.request().post(Entity.form(form));
//The response was 200OK.
System.out.println(response.readEntity(String.class));
}
Instead of other problems including HTML and Web.xml dependency, now I suspect the client code I wrote was not correct and when the request is sent it does not contain the correct username and password. The server side code is below:
#POST
#Produces("text/plain")
#Path("authenticate")
public String authenticate(#Context HttpServletRequest req, #QueryParam("username")
String username, #QueryParam("password") String password)
throws Exception {
Environments environments = new DefaultConfigurationBuilder().build();
final ALMProfile profile = new ALMProfile();
profile.setUrl(environments.getAutomation().getAlmProfile().getUrl());
profile.setUsername(username);
if ( !Strings.isNullOrEmpty(password) ) {
String encryptedPassword = EncryptionUtils.encrypt(password);
profile.setPassword(encryptedPassword);
}
try (ALMConnection connection = new ALMConnection(profile);) {
if (connection.getOtaConnector().connected()) {
req.getSession(true).setAttribute("username", username);
req.getSession(true).setAttribute("password", profile.getPassword());
return "";
}
} catch (Exception e) {
e.printStackTrace();
return "Username or Password are incorrect";
}
return "Username or Password are incorrect";
}
Can someone point out if the client code submits the correct request?
First of all, you probably need to check what's in your stack trace. And the value of username and password. I suspect them to be null.
Secondly, I think the problem is coming from the #QueryParam annotation.
You must use instead #FormParam
You use QueryParam when your url contain params as:
www.test.com/test?username=test
When you're sending data in a Form, you must use the annotation #FormParam
I currently have a working web app, but I need to provide means for friend website to consume my data.
There is currently JSON response in place which retrieves some data from my website to caller. It's without authentication currently and I'd like to implement some kind of per request authentication.
My web app has users which are logged in and there is a authentication in place for that. But
I have 3 requests in total for which callers can get data off of my website, what would be the simplest way to add some kind of authentication just for those 3 requests?
I'm using play framework + java
Imo the best options for this would be in the order of simplicity:
Basic authentication (since it's possible to choose either to auth once and then do session-base user recognition or authorize on every request)
2-way SSL
Combination of both
What toolkit do you use for authentication part?
I personally stuck with play-authenticate. So I might be able to answer you question in regard to this toolkit, please apply it to your particular toolkit as needed.
I will provide Basic authentication example as the easiest one. The benefit is: you could start with it and add on top it later (e.g. add Client certificate authentication via Apache later on).
So, my controller code snippet
#Restrict(value = #Group({"ROLE_WEB_SERVICE1"}), handler = BasicAuthHandler.class)
public static Result ws1() {
return TODO;
}
And the authentification handler itself
public class BasicAuthHandler extends AbstractDeadboltHandler {
public static final String HEADER_PREFIX = "Basic ";
private static final String AUTHORIZATION = "authorization";
private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
#Override
public Result beforeAuthCheck(final Http.Context context) {
return basicAuthenticate(context);
}
private Result basicAuthenticate(Http.Context context) {
if (PlayAuthenticate.isLoggedIn(context.session())) {
// user is logged in
return null;
}
final String authHeader = context.request().getHeader(AUTHORIZATION);
if (authHeader == null || !authHeader.toLowerCase().startsWith(HEADER_PREFIX.toLowerCase())) {
return onAuthFailure(context, "Basic authentication header is missing");
}
final String auth = authHeader.substring(HEADER_PREFIX.length());
final byte[] decodedAuth;
final String[] credentials;
try {
decodedAuth = Base64.base64ToByteArray(auth);
credentials = new String(decodedAuth, "UTF-8").split(":");
} catch (final IOException e) {
Logger.error("basicAuthenticate", e);
return Results.internalServerError();
}
if (credentials.length != 2) {
return onAuthFailure(context, "Could not authenticate with absent password");
}
final String username = credentials[0];
final String password = credentials[1];
final AuthUser authUser = new AuthUser(password, username);
final Enum result = AuthProvider.getProvider().loginUser(authUser);
if ("USER_LOGGED_IN".equals(result.name())) {
PlayAuthenticate.storeUser(context.session(), authUser);
return null;
}
return onAuthFailure(context, "Authenticate failure");
}
#Override
public Subject getSubject(final Http.Context context) {
// your implementation
}
#Override
public Result onAuthFailure(final Http.Context context,
final String content) {
// your error hangling logic
return super.onAuthFailure(context, content);
}
}
Hopefully it fills in some blanks
I'm trying to write a simple smoke test for a web application.
The application normally uses form based authentication, but accepts basic auth as well, but since the default is form based authentication, it never sends an authentication required, but instead just sends the login form.
In the test I try to send the basic auth header using
WebClient webClient = new WebClient();
DefaultCredentialsProvider creds = new DefaultCredentialsProvider();
// Set some example credentials
creds.addCredentials("usr", "pwd");
// And now add the provider to the webClient instance
webClient.setCredentialsProvider(creds);
webClient.getPage("<some url>")
I also tried stuffing the credentials in a WebRequest object and passing that to the webClient.getPage method.
But on the server I don't get an authentication header. I suspect the WebClient only sends the authentication header if it get explicitly asked for it by the server, which never happens.
So the question is how can I make the WebClient send the Authentication header on every request, including the first one?
This might help:
WebClient.addRequestHeader(String name, String value)
More specific one can create an authentication header like this
private static void setCredentials(WebClient webClient)
{
String username = "user";
String password = "password";
String base64encodedUsernameAndPassword = base64Encode(username + ":" + password);
webClient.addRequestHeader("Authorization", "Basic " + base64encodedUsernameAndPassword);
}
private static String base64Encode(String stringToEncode)
{
return DatatypeConverter.printBase64Binary(stringToEncode.getBytes());
}
I am familiar with using Jersey to create RESTful webservice servers and clients, but due to class loading issues, I am trying to convert a Jersey client into CXF. I believe I want to use an HTTP-centric client but we don't use Spring. We need to use basic HTTP authentication. The user guide has this example:
WebClient client = WebClient.create("http:books", "username", "password", "classpath:/config/https.xml");
The first parameter isn't a URI string. Is it a format used by Spring? Can this method only be used to create WebClients using Spring?
The other way of doing authentication shown is to add a header string:
String authorizationHeader = "Basic " + org.apache.cxf.common.util.Base64Utility.encode("user:password".getBytes());
webClient.header("Authorization", authorizationHeader);
I am guessing that "user:password" should be substituted with the real values, but would appreciate confirmation.
This answer came from the CXF users mailing list.
The first example referenced above had a typo in it. It has been updated to:
WebClient client = WebClient.create("http://books", "username", "password", "classpath:/config/https.xml");
The fourth argument can be null if a Spring config file is (and therefore Spring) is not being used.
So, this worked for me:
private WebClient webClient;
public RESTfulClient(String url, String username, String password)
throws IllegalArgumentException
{
this.username = username;
this.password = password;
this.serviceURL = url;
if (username == null || password == null || serviceURL == null)
{
String msg = "username, password and serviceURL MUST be defined.";
log.error(msg);
throw new IllegalArgumentException(msg);
}
webClient = WebClient.create(this.serviceURL,
this.username,
this.password,
null); // Spring config file - we don't use this
}