Authentication and authorization for RESTfull API (java jersery) - java

implementing service something similar with tinyurl or bit.ly, I'm would like to expose service as API, I'm using java and jersey as RESTfull service implementation.
I'm looking for simplest way for authentification of users who use API, OAuth is first thing coming in mind, but the problem is I don't need this 3 iteration calls with request token query, than access token query with callback url passing. I just need to give user ability to invoke api with no additional security calls to my server.

Thanks to patrickmcgraw comment I used 2-legged oauth authentificaton.
Here is some java code.
For client side (using Jersey api):
OAuthParameters params = new OAuthParameters().signatureMethod("HMAC-SHA1").
consumerKey("consumerKey").version("1.1");
OAuthSecrets secrets = new OAuthSecrets().consumerSecret("secretKey");
OAuthClientFilter filter = new OAuthClientFilter(client().getProviders(), params, secrets);
WebResource webResource = resource();
webResource.addFilter(filter);
String responseMsg = webResource.path("oauth").get(String.class);
On provider side:
#Path("oauth")
public class OAuthService {
#GET
#Produces("text/html")
public String secretService(#Context HttpContext httpContext) {
OAuthServerRequest request = new OAuthServerRequest(httpContext.getRequest());
OAuthParameters params = new OAuthParameters();
params.readRequest(request);
OAuthSecrets secrets = new OAuthSecrets().consumerSecret("secretKey");
try {
if(!OAuthSignature.verify(request, params, secrets))
return "false";
} catch (OAuthSignatureException ose) {
return "false";
}
return "OK";
}
}
Here is code for PHP client:
<?php
require_once 'oauth.php';
$key = 'consumerKey';
$secret = 'secretKey';
$consumer = new OAuthConsumer($key, $secret);
$api_endpoint = 'http://localhost:9998/oauth';
$sig_method = new OAuthSignatureMethod_HMAC_SHA1;
$parameters = null;
$req = OAuthRequest::from_consumer_and_token($consumer, null, "GET", $api_endpoint, $parameters);
$sig_method = new OAuthSignatureMethod_HMAC_SHA1();
$req->sign_request($sig_method, $consumer, null);//note: double entry of token
//get data using signed url
$ch = curl_init($req->to_url());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$res = curl_exec($ch);
echo $res;
curl_close($ch);

if youre using http at the transport layer you can always use basic http authentication

Related

entity must be null for http method GET

I'm trying to consume an external api exposed a payment provider.
I user Jersey and javax.ws.rs for request, because I can easily send authent with Digest.
But when it comes to make the request, a GET with payload, Jersey returns
> IllegalStateException. Entity must be null for http method GET
CashTransactionResponse responseData = null;
//We connect to intouch server
String requestUrl = rootUrlTouchPay + agency.getAgencyCode() + "/" + IntouchMethodApis.TRANSACTION + "?loginAgent=" + agency.getLogin() + "&passwordAgent=" + agency.getPassword();
ClientConfig clientConfig = new ClientConfig();
//Open Digest authentication
HttpAuthenticationFeature feature = HttpAuthenticationFeature.digest(BASIC_LOGIN, BASIC_PWD);
clientConfig.register(feature);
clientConfig.register(JacksonFeature.class);
//Create new rest client
Client client = ClientBuilder.newClient(clientConfig);
clientConfig.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
//Set the url
WebTarget webTarget = client.target(requestUrl);
Invocation.Builder invocationBuilder = webTarget.request(javax.ws.rs.core.MediaType.APPLICATION_JSON);
logger.info("Initialisation of cashout service successful for cash");
// create request
Gson gson = new Gson();
String transactionString = gson.toJson(cashRequest);
Response response = null;
// start the response
if (cashRequest.getServiceCode().contains(TelecomEnum.WAVE.name().toUpperCase())) {
response = invocationBuilder.method("GET", Entity.entity(transactionString, javax.ws.rs.core.MediaType.APPLICATION_JSON));
} else {
response = invocationBuilder.put(Entity.entity(transactionString, javax.ws.rs.core.MediaType.APPLICATION_JSON));
}
Please how could i do to send my GET request with body ?
Thanks
I believe the problem is in
Client client = ClientBuilder.newClient(clientConfig);
clientConfig.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
The client copied the values from clientConfig and any further settings on clientConfig do not have any impact on the client.
Either switch the lines or set the ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION property on the client.
Instead of doing an invocationBuilder.method("GET", ...), use invocationBuilder.post(entity), as described here. This will allow you to POST your transaction String to the endpoint.

Using rest api of azure devops

I want to fetch data from the rest api of Azure Devops using Java.But not sure how to establish the connection.May be personal acces token will help,but how to use the token in Code for establishing the connection between code and azure devops? An example from anyone will be very helpful.
A code example will be very helpfull
If I am understanding you correctly, you are trying to call azure APIs, and those API need authorization token?
For example this azure API to send data into Azure queue : https://learn.microsoft.com/en-us/rest/api/servicebus/send-message-to-queue
It needs some payload and Authorization in request header !!
If my Understanding is correct, than from java you need to use any rest client or HTTP client to call the REST API and you need to pass the Authorization token in the request header
For calling a Rest API in java with passing header below is an example:
MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
map.add("Authorization", "Bearer <Azure AD JWT token>"); // set your token here
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON); //someother http headers you want to set
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
RestTemplate restTemplate = new RestTemplate();
String azure_url = "https://azure_url"; // your azure devops REST URL
ResponseEntity<String> response = restTemplate.postForEntity(azure_url, request , String.class);
A small example with httpclient:
static String ServiceUrl = "https://dev.azure.com/<your_org>/";
static String TeamProjectName = "your_team_project_name";
static String UrlEndGetWorkItemById = "/_apis/wit/workitems/";
static Integer WorkItemId = 1208;
static String PAT = "your_pat";
String AuthStr = ":" + PAT;
Base64 base64 = new Base64();
String encodedPAT = new String(base64.encode(AuthStr.getBytes()));
URL url = new URL(ServiceUrl + TeamProjectName + UrlEndGetWorkItemById + WorkItemId.toString());
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("Authorization", "Basic " + encodedPAT);
con.setRequestMethod("GET");
int status = con.getResponseCode();
Link to the file: ResApiMain.java
You can the use java client library for azure devops rest api. This will take the overload of encoding your personal access token and indeed supports OAuth authentication.
It is been actively developed and used in production.
Source code - https://github.com/hkarthik7/azure-devops-java-sdk
Documentation - https://azure-devops-java-sdk-docs.readthedocs.io/en/latest/?badge=latest
A quick example:
public class Main {
public static void main(String[] args) {
String organisation = "myOrganisationName";
String personalAccessToken = "accessToken";
String projectName = "myProject";
// Connect Azure DevOps API with organisation name and personal access token.
var webApi = new AzDClientApi(organisation, project, personalAccessToken);
// call the respective API with created webApi client connection object;
var core = webApi.getCoreApi();
var wit = webApi.getWorkItemTrackingApi();
try {
// get the list of projects
core.getProjects();
// get a workitem
wit.getWorkItem(15);
// Get a work item and optionally expand the field
wit.getWorkItem(15, WorkItemExpand.ALL);
} catch (AzDException e1) {
e1.printStackTrace();
}
}
}
The library has support to most of the APIs and you can view the documentation and examples folder in the github repo to know how to get the most out of it.

Consuming soap service with NTLM Authentication

I am trying to consume a SOAP service with NTLM authentication by creating a NTLM engine (following instructions on http://hc.apache.org/httpcomponents-client-4.3.x/ntlm.html ) implemented AuthSchemeFactory and finally registered the AuthSchemeFactory to my HTTP Client. When I hit the service using my HTTP Client I get a reponse that "Status code - 415 , Message - The server cannot service the request because the media type is unsupported."
Can anybody tell how can I fix this issue of unsupported media to consume a NTLM-protected SOAP web service on Java platform. Is using JCIFS a correct option to conmsume NTLM protected service or are there any better approach(s). Thanks in advance.
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getAuthSchemes().register(AuthSchemes.NTLM,
new JCIFSNTLMSchemeFactory());
CredentialsProvider credsProvider = new BasicCredentialsProvider();
NTCredentials ntcred = new NTCredentials("USERNAME", "PASSWORD",
"HOST", "DOMAIN");
credsProvider.setCredentials(new AuthScope("HOST", 443,
AuthScope.ANY_REALM, "NTLM"), ntcred);
httpclient.setCredentialsProvider(credsProvider);
httpclient.getParams().setParameter(
CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
Writer writer = new StringWriter();
writer.write("MY SOAP REQUEST BODY");
HttpPost httppost = new HttpPost(
"https://<HOST_NAME>/XiPay30WS.asmx");
httppost.setEntity(new StringEntity(writer.toString()));
httppost.setHeader("Content-Type",
"application/x-www-form-urlencoded");
HttpResponse httpresponse = httpclient.execute(
new HttpHost("HOST", 443, "https"),
httppost, new BasicHttpContext());
String statusCode = httpresponse.getStatusCode();
If you use Spring WS support:
Check this Solution
http://dolszewski.com/spring/sharepoint-web-services-spring-and-ntlm-authentication/
#Bean("navisionMessageSender")
public HttpComponentsMessageSender httpComponentsMessageSender() {
HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
String user = env.getProperty("navision.endpoint.user");
String password = env.getProperty("navision.endpoint.password");
String domain = env.getProperty("navision.endpoint.domain");
NTCredentials credentials = new NTCredentials(user, String.valueOf(password), null, domain);
httpComponentsMessageSender.setCredentials(credentials);
return httpComponentsMessageSender;
}
Sample python implementation with NTLM Auth with FLASK.
If you want to use with java , run the standalone flask code below and call the url (e.g POST request /dora/httpWithNTLM ) from java code by http request
from flask import Flask, render_template, flash, request, url_for, redirect, session , Response
import requests,sys,json
from requests_ntlm import HttpNtlmAuth
app = Flask(__name__)
#app.route("/dora/httpWithNTLM",methods=['POST'])
def invokeHTTPReqWithNTLM():
url =""
reqData = json.loads(request.data)
reqxml=request.data
headers = {}
headers["SOAPAction"] = "";
headers["Content-Type"] = "text/xml"
headers["Accept"] = "text/xml"
print("req headers "+str(request.headers))
r = requests.Request("POST",url,auth=HttpNtlmAuth('domain\\username','password'), data=reqxml, headers=headers)
prepared = r.prepare()
s = requests.Session()
resp = s.send(prepared)
print (resp.status_code)
return Response(resp.text.replace("<","<").replace(">",">"),resp.status_code)
if __name__ == '__main__':
app.run(host="0.0.0.0",port=5001)

CXF SOAP Client with NTLM to SharePoint

I am writing a SOAP client using CXF Framework (version: 2.7.8) for SharePoint 2007. I have followed the online documentation for adding NTLM support here. I have the client working and tracing the HTTP session shows that NTLM credentials are being sent, however, I am still receiving a 401 Unauthorized response.
Code:
Lists listService = new Lists();
ListsSoap port = listService.getListsSoap();
BindingProvider bp = (BindingProvider) port;
bp.getRequestContext().put("use.async.http.conduit", Boolean.TRUE);
Credentials creds = new NTCredentials(USER, PASS, "", DOMAIN);
bp.getRequestContext().put(Credentials.class.getName(), creds);
Client client = ClientProxy.getClient(proxy);
HTTPConduit http = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(36000);
httpClientPolicy.setAllowChunking(false);
httpClientPolicy.setAutoRedirect(true);
http.setClient(httpClientPolicy);
// Build request and execute
Interestingly, I wrote a similar client using HTTP PUT for WebDAV to upload documents using Apache HTTPClient library, and was able to successfully authenticate using NTLM. Also, I was able to use SOAPUI to invoke the same Lists web service I am trying to build the Java client for and it successfully authenticated using NTLM as well.
I'm assuming the implementation of NTLM is different between CXF and HTTPClient. Any thoughts on what is wrong with my CXF implementation? Or how I can get it to mirror the HTTPClient implementation?
Please try this way!
HTTPConduit http = (HTTPConduit)client.getConduit();
AsyncHTTPConduit conduit = (AsyncHTTPConduit)http;
DefaultHttpAsyncClient defaultHttpAsyncClient;
defaultHttpAsyncClient = conduit.getHttpAsyncClient();
defaultHttpAsyncClient.getCredentialsProvider().setCredentials( AuthScope.ANY,
new NTCredentials( USER,PWD, "", DOM ) );
conduit.getClient().setAllowChunking( false );
conduit.getClient().setAutoRedirect( true );
#lamarvannoy, I also got this error. But I found another way. You don't need to cast HTTPConduit to AsyncHTTPConduit. Let's try this stuff:
public class Test {
static final String kuser = "yourDomain\\username";
static final String kpass = "yourPassword";
static class MyAuthenticator extends Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
System.err.println("Feeding username and password for " + getRequestingScheme());
return (new PasswordAuthentication(kuser, kpass.toCharArray()));
}
}
public static void main(String[] args) throws Exception {
Authenticator.setDefault(new MyAuthenticator());
Lists listService = new Lists();
ListsSoap port = listService.getListsSoap();
Client client = ClientProxy.getClient(port);
HTTPConduit http = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(36000);
httpClientPolicy.setAllowChunking(false);
http.setClient(httpClientPolicy);
String listName = "S030_main";
String rowLimit = "150";
ArrayList<String> listColumnNames = new ArrayList<String>();
listColumnNames.add("Title");
Test.displaySharePointList(port, listName, listColumnNames, rowLimit);
}
}
You may find the implementation of displaySharePointList() method in this post: http://davidsit.wordpress.com/2010/02/10/reading-a-sharepoint-list-with-java-tutorial/
I hope this will safe your and others time.
This works for me:
Client client = ClientProxy.getClient(port);
AsyncHTTPConduit conduit = (AsyncHTTPConduit)client.getConduit();
AuthorizationPolicy authorization = conduit.getAuthorization();
authorization.setUserName("domain\\username");
authorization.setPassword("password");
Actually this works for both NTLM and Basic
This is what I had to do to get mine to work:
// Include a version of WSDL in class path, make URL point to that
URL url = MyClient.class.getResource("previouslydownloaded.wsdl");
MyCxFService ws = new MyCxFService(url);
MyCxfClient client = ws.getMyCxfServicePort();
BindingProvider prov = ((BindingProvider) client);
Binding binding = prov.getBinding();
// Set Username and Password
if ((this.user != null) && (!this.user.isEmpty())) {
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, this.user);
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, this.passwd);
}
// Get address from config file to get rid error caused by using wsdl file:
// Caused by: java.lang.NullPointerException
// at org.apache.cxf.transport.http.URLConnectionHTTPConduit.createConnection(URLConnectionHTTPConduit.java:104)
prov.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, this.portAddress);
Hope that might help someone.

template parsing URL in java

I have a application that receive a message from SMPP server and forward the received message to users web services. all users have a URL in database like this:
http://www.example.com/get.php?from=${originator}&to=${destination}&data=${content}
http://www.example.com/submit?origin=${originator}&dst=${destination}&cnt=${content}
All of them have ${originator},${destination},${content} in their pattern. they may have other parameters in their URL or not. I use Jodd StringTemplateParser to replace above arguments after getting user URL from database:
private StringTemplateParser stp = new StringTemplateParser();
private final Map<String, String> args = new HashMap<>();
args.put("originator", msg.getOriginator());
args.put("destination", msg.getDestination());
args.put("content", msg.getContent());
String result = stp.parse(deliveryUrl, new MacroResolver() {
#Override
public String resolve(String macroName) {
return args.get(macroName);
}
});
Then I use apache http client to call user URL:
URL url = new URL(result);
int port = url.getPort();
uri = new URI(url.getProtocol(), null, url.getHost(), port == -1 ? 80 : port, url.getPath(), url.getQuery(), null);
DefaultHttpClient client = new DefaultHttpClient();
HttpGet request = null;
try {
request = new HttpGet(uri);
client.execute(request)
} catch (Exception ex) {
if (request != null) {
request.releaseConnection();
}
}
I tried to do some encoding because user may send any text containing characters like %$&. The problem is when content contains something like hello&x=2&c=3243 user only gets hello. URI class treats that as regular GET query parameter. Can anyone help?
You need to encode the parameter values when you are building your new URI.
You can encode the parameters using URLEncoder.encode(String).
So, in your MacroResolver, just return URLEncoder.encode(map.get(macroName)).
I think in your case you need to use HTTP default content char-set something like this for your code
request = new HttpGet(url+"?"+URLEncodedUtils.format(namevaluepair,
HTTP.DEFAULT_CONTENT_CHARSET));

Categories