My requirement:
I want to make API like dropbox's /longpoll_delta,I have useed jerser 2 in java for my restful. Actually I want to notify all client when there is change found on server. So server may take 2hr or more then notify all its client that i have some update. I don't want to use comet, websocket etc for security purpose.
1> Suggest how can i made such API ?
My effor :
I have created Asynchronous Server API
Server code:
#GET
#Path("/longpoll")
#ManagedAsync
public void async(#Suspended final AsyncResponse asyncResponse)
{
new Thread(new Runnable()
{
#Override
public void run()
{
String result = veryExpensiveOperation();
asyncResponse.resume(result);
}
private String veryExpensiveOperation()
{
try
{
// put some long running code here
Thread.sleep(60000);
return "Hello World";
}
catch (Exception e)
{
e.printStackTrace();
return "error";
}
}
}).start();
}
So now for testing this i have created client
Client code:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.InvocationCallback;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientConfig;
public class JersyClient
{
public static void main(String args[]) throws InterruptedException, ExecutionException
{
ClientConfig clientConfig = new ClientConfig();
Client client = ClientBuilder.newClient(clientConfig);
WebTarget target = client.target("http://localhost:8080");
Invocation.Builder invocationBuilder = target.path("MyApp_3_3_5/api/1/longpoll").request();
invocationBuilder.header("Authorization", "Bearer yBOrnV6zlOrgoYxrMsfQ_BYZ5p37gALB");
final Future<Response> future = invocationBuilder.async().get(new InvocationCallback<Response>()
{
#Override
public void completed(Response response)
{
System.out.println("Response status code "
+ response.getStatus() + " received.");
System.out.println("In response " + response.readEntity(String.class));
}
#Override
public void failed(Throwable throwable)
{
System.out.println("Invocation failed.");
throwable.printStackTrace();
}
});
Response response = future.get();
System.out.println("your response " + response.readEntity(String.class));
// get() waits for the response to be ready
System.out.println("Go ahead");
}
}
But this not working properly.
OutPut:
Response status code 200 received.
In response
your response
Go ahead
So here client is not wait for actual response that server send after 1 minute.
Is server code have any problem ?
Why client not wait for actual response ?
Should i achieve long_pooling using this way ?
if server send response after 2hr then in such case is this working ?
Related
I've been working on a Java application that utilizes the openshift api. Specifically OpenShift deployment configuration
I have tried to set up a watcher, but my response body is never called. I am already able to get a response from the 'non watcher' APIcalls. I am using the groovy httpbuilder library to fulfill my request
def http = new HTTPBuilder(<<URL TO OPENSHIFT>>)
try {
http.get(path: '/oapi/v1/watch/namespaces/myproject/deploymentconfigs', contentType: "application/json") { resp, reader ->
println(resp)
return reader
}
} catch (HttpResponseException e) {
System.out.println(e)
}
Please Advise on a path forward to set up OpenShift watchers in my application.
An error message is never thrown. minishift logs -f are not providing any feedback either.
Also note that I have gotten this to work with the curl command, documented in the api
You can use the OKHttpClient to handle the http websocket upgrade protocol for you. Note legacy versions of minishift require the query parameter "access_token" when trying to make a websocket connection request
OkHttpClient client = new OkHttpClient();
def token = token
Request request = new Request.Builder()
.get()
.url("https://<<IP>>/oapi/v1/watch/namespaces/<<namespace>>/deploymentconfigs?watch=true&access_token=<<token>>")
.addHeader("Accept", "application/json")
.addHeader("Connection", "close")
.addHeader("Sec-WebSocket-Protocol",'base64url.bearer.authorization.k8s.io.' + Base64.getEncoder().encodeToString(token.getBytes()))
.addHeader('Origin', 'https://<<IP>>')
.build()
WebSocketListener websocketListener= new WebSocketListenerImpl()
client.newWebSocket(request, websocketListener)
WebSocketListenerImpl Class
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
public class WebSocketListenerImpl extends WebSocketListener {
public WebSocketListenerImpl() {
super();
}
#Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
print "WEBSOCKET OPEN"
}
#Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
print "WEBSOCKET RECEIVED"
}
#Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
super.onMessage(webSocket, bytes);
print "WEBSOCKET OPEN"
}
#Override
public void onClosing(WebSocket webSocket, int code, String reason) {
super.onClosing(webSocket, code, reason);
print "WEBSOCKET CLOSING"
}
#Override
public void onClosed(WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason);
print "WEBSOCKET CLOSED"
}
#Override
public void onFailure(WebSocket webSocket, Throwable t, #javax.annotation.Nullable Response response) {
super.onFailure(webSocket, t, response);
println "WEBSOCKET FAILED"
}
}
From this artcle, I have implemented calling another rest API from my REST API method in micronaut gradle application. Since my REST API expects jwt token I am sending the same token I received with in current request. I am seeing Unauthorized error even token is being passed. Can anyone help in this regard. Below is my code.
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpStatus;
import io.appter.clientmgmt.models.ClientContact;
import io.appter.clientmgmt.repositories.IClientContactRepository;
import io.micronaut.http.uri.UriTemplate;
import io.micronaut.security.annotation.Secured;
import io.micronaut.security.authentication.Authentication;
import io.micronaut.security.rules.SecurityRule;
import io.micronaut.http.annotation.*;
import io.micronaut.http.client.RxHttpClient;
import io.micronaut.http.client.annotation.Client;
import io.reactivex.Flowable;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import javax.validation.constraints.NotNull;
import java.security.Security;
import java.util.List;
#Controller("/clientcontact")
//#Secured(SecurityRule.IS_ANONYMOUS)
public class ClientContactController {
private static final Logger LOG = LoggerFactory.getLogger(ClientContactController.class);
private IClientContactRepository clientContactRepository;
private final RxHttpClient httpClient;
public ClientContactController(IClientContactRepository clientContactRepository,
#Client("http://appterauthsvc-env.g2yapp2kcp.us-east-1.elasticbeanstalk.com") RxHttpClient httpClient) {
this.clientContactRepository = clientContactRepository;
this.httpClient = httpClient;
}
#Get("/")
public HttpStatus index() {
return HttpStatus.OK;
}
#Post("/")
#Secured(SecurityRule.IS_AUTHENTICATED)
public ClientContact createClientContact(#Body ClientContact clientContact,
Authentication authentication,
#Header("Authorization") String authorization) {
try {
List<ClientContact> existingClientContacts = clientContactRepository.getClientContactByClientId(clientContact.getClientId());
LOG.info("current contacts count for the client " + clientContact.getClientId() + " is " + existingClientContacts.size());
if (existingClientContacts.isEmpty()) {
User userObj = new User();
Long clientId = new Long(clientContact.getClientId());
userObj.setClientId(clientId);
userObj.setFirstName(clientContact.getFirstName());
userObj.setLastName(clientContact.getLastName());
userObj.setEmailId(clientContact.getEmailAddress());
userObj.setPhoneNo(clientContact.getContactNumber());
userObj.setIsActive(true);
LOG.info("User Email set is: "+userObj.getEmailId());
LOG.info("authorization token is: "+authorization);
HttpRequest<?> request = HttpRequest.POST("/user", userObj).bearerAuth(authorization);
String response = httpClient.toBlocking().retrieve(request);
LOG.info("Request Object: "+ request.toString());
LOG.info("Response Object: "+ response.toString());
LOG.info("User API executed.. ");
}
return clientContactRepository.createClientContact(clientContact);
} catch (Exception ex) {
LOG.error(ex.getMessage(), ex);
return null;
}
}
}
Thanks in advance.
Likely because #Header("Authorization") String authorization is returning something like Bearer xyz... and the bearerAuth method is adding Bearer to the string so you are sending Bearer Bearer xyz...
So just do .header(HttpHeaders.AUTHORIZATION, authorization)
Also as a side note you really shouldn't be doing blocking HTTP calls in this method. It's not the end of the world since in this case you're blocking an IO thread, however this type of code should be avoided.
I'm using Jetty 9 and I'm trying to process the headers of a PUT request before all the body has arrived on the server. Here's what I've done:
Server.java:
public class SimplestServer
{
public static void main(String[] args) throws Exception
{
Server server = new Server(9080);
ServletHandler handler = new ServletHandler();
server.setHandler(handler);
handler.addServletWithMapping(HelloServlet.class, "/*");
handler.addFilterWithMapping(HelloPrintingFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
server.start();
server.dumpStdErr();
server.join();
}
public static class HelloServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(System.currentTimeMillis() + ": Hello from HelloServlet GET");
}
#Override
protected void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(System.currentTimeMillis() + ": Hello from HelloServlet PUT");
}
}
public static class HelloPrintingFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println(System.currentTimeMillis() + ": Hello from filter");
chain.doFilter(request, response);
}
#Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println(System.currentTimeMillis() + ": Init from filter");
}
#Override
public void destroy() {
System.out.println(System.currentTimeMillis() + ": Destroy from filter");
}
}
}
Client.java
public class SimplestClient
{
public static void main(String[] args) throws Exception
{
URL url = new URL("http://localhost:9080/resource");
HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();
httpCon.setDoOutput(true);
httpCon.setRequestMethod("PUT");
OutputStream out = httpCon.getOutputStream();
byte[] b = new byte[65536];
Random r = new Random();
r.nextBytes(b);
for (int i = 0; i < 1024; i++) {
out.write(b);
}
System.out.println(System.currentTimeMillis() + ": Data sent. Waiting 5 seconds...");
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
out.close();
System.out.println(System.currentTimeMillis() + ": Done!");
httpCon.getInputStream();
}
}
In a nutshell, the server program listens for connections on port 9080, when a request arrives the filter HelloPrintingFilter gets executed, then the request is processed by the HelloServlet. The client, instead, connects to the server, sends a bunch of data, then sleeps for 5 seconds and finally closes the connection with the server.
A run of both programs yields the following result:
Client:
1469613522350: Data sent. Waiting 5 seconds...
1469613527351: Done!
Server:
1469613527373: Hello from filter
1469613527373: Hello from HelloServlet PUT
Looking at the timestamps I can only get my filter code executed after all the body has arrived. Could anyone explain me how to do it? A typical use case is: a client tries to upload a 5GB file. As soon as the headers arrive, I want to check if they are OK (e.g. by checking if the Content-MD5 header, or whatever custom header I need to check, is present). If the request is OK then start processing the body. If the request is not OK then close the connection.
Thanks.
Use multiple requests. e.g. first request includes custom header and subsequent requests are used to upload a 5GB file.
You aren't doing anything in your HelloServlet.doPut(), so you are basically telling the Servlet container (aka Jetty) that you are done handling that request.
The request processing in Jetty is handled by a series of buffers from the network.
Your PUT headers and the start of your body content likely fit in a single buffer.
Jetty will parse the headers out, and then start the dispatch of the request to the Servlet chain, hitting your HelloFilter, and then your filter moves it along the chain with the chain.doFilter(request, response);
The point in time when HelloServlet.doPut() is reached, the headers have been processed, and the start of the body content hasn't, waiting for your implementation in doPut() to call HttpServletRequest.getInputStream() and start processing it, at which point Jetty is free to start reading more buffers off the network.
Note: if your servlet exits without reading the request input stream, and the response hasn't indicated a Connection: close, then Jetty will be forced to read the entire request to completion looking for the next request after it (known as a persistent connection in HTTP/1.1 spec)
The closest you'll get to reaching your stated goal to reject the Request body content is to use what you have available in the HTTP/1.1 spec (assuming this is a HTTP/1.1 request). Namely a proper response status code and a server initiated Connection: close response header.
Here's a complete example:
package jetty;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.Uptime;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class PutRejectExample
{
public static class RejectServlet extends HttpServlet
{
#Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
timedLog("doPut() - enter");
if (req.getHeader("X-Key") == null)
{
resp.setHeader("Connection", "close");
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
timedLog("doPut() - rejected");
return;
}
File output = File.createTempFile("reject-", ".dat");
try (FileOutputStream out = new FileOutputStream(output))
{
IO.copy(req.getInputStream(), out);
}
resp.setStatus(HttpServletResponse.SC_OK);
resp.setHeader("Connection", "close"); // be a good HTTP/1.1 citizen
timedLog("doPut() - exit");
}
}
private static Server server;
private static int port;
private static void timedLog(String format, Object... args)
{
System.out.printf(Uptime.getUptime() + "ms " + format + "%n", args);
}
#BeforeClass
public static void startServer() throws Exception
{
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(0);
server.addConnector(connector);
// collection for handlers
HandlerCollection handlers = new HandlerCollection();
server.setHandler(handlers);
// servlet context
ServletContextHandler context = new ServletContextHandler();
context.addServlet(RejectServlet.class, "/reject");
handlers.addHandler(context);
// default handler
handlers.addHandler(new DefaultHandler());
// start server
server.start();
// grab port
port = connector.getLocalPort();
}
#AfterClass
public static void stopServer() throws Exception
{
server.stop();
}
private void performPUT(int requestSize, String... extraRequestHeaders) throws IOException
{
StringBuilder req = new StringBuilder();
req.append("PUT /reject HTTP/1.1\r\n");
req.append("Host: localhost:").append(port).append("\r\n");
req.append("Content-Length: ").append(requestSize).append("\r\n");
for (String extraHeader : extraRequestHeaders)
{
req.append(extraHeader);
}
req.append("\r\n");
timedLog("client open connection");
try (Socket socket = new Socket())
{
socket.connect(new InetSocketAddress("localhost", port));
try (OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
InputStreamReader reader = new InputStreamReader(in))
{
timedLog("client send request (headers + body)");
try
{
// write request line + headers
byte headerBytes[] = req.toString().getBytes(StandardCharsets.UTF_8);
out.write(headerBytes);
out.flush();
// write put body content
int bufSize = 65535;
byte[] buf = new byte[bufSize];
int sizeLeft = requestSize;
while (sizeLeft > 0)
{
int writeSize = Math.min(sizeLeft, bufSize);
ThreadLocalRandom.current().nextBytes(buf);
out.write(buf, 0, writeSize);
out.flush();
sizeLeft -= writeSize;
try
{
// simulate a slower connection
TimeUnit.MILLISECONDS.sleep(10);
}
catch (InterruptedException ignore)
{
// ignore
}
}
}
catch (IOException e)
{
timedLog("client request send exception");
e.printStackTrace(System.out);
}
timedLog("client send request complete");
timedLog("client read response");
try
{
StringWriter respStream = new StringWriter();
IO.copy(reader, respStream);
timedLog("client response: %s", respStream.toString());
}
catch (IOException e)
{
timedLog("client read response exception");
e.printStackTrace(System.out);
}
}
}
timedLog("client connection complete");
}
#Test
public void testBadPost() throws IOException
{
timedLog("---- testBadPost()");
performPUT(1024 * 1024 * 10);
}
#Test
public void testGoodPost() throws IOException
{
timedLog("---- testGoodPost()");
performPUT(1024 * 1024 * 10, "X-Key: foo\r\n");
}
}
This uses raw Socket and raw streams to avoid getting confused by all of the buffering present in the HttpUrlConnection.
The output you'll see for the normal / happy case is like this ...
416ms ---- testGoodPost()
416ms client open connection
2016-07-27 06:40:22.180:INFO:oejs.AbstractConnector:main: Started ServerConnector#55f3ddb1{HTTP/1.1,[http/1.1]}{0.0.0.0:46748}
2016-07-27 06:40:22.181:INFO:oejs.Server:main: Started #414ms
421ms client send request (headers + body)
494ms doPut() - enter
2084ms doPut() - exit
2093ms client send request complete
2093ms client read response
2094ms client response: HTTP/1.1 200 OK
Date: Wed, 27 Jul 2016 13:40:22 GMT
Connection: close
Server: Jetty(9.3.11.v20160721)
2094ms client connection complete
The output for the rejected case will look like this ...
2095ms ---- testBadPost()
2095ms client open connection
2096ms client send request (headers + body)
2096ms doPut() - enter
2101ms doPut() - rejected
2107ms client request send exception
java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109)
at java.net.SocketOutputStream.write(SocketOutputStream.java:153)
at jetty.PutRejectExample.performPUT(PutRejectExample.java:137)
at jetty.PutRejectExample.testBadPost(PutRejectExample.java:180)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
2109ms client send request complete
2109ms client read response
2109ms client response: HTTP/1.1 403 Forbidden
Date: Wed, 27 Jul 2016 13:40:23 GMT
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html;charset=iso-8859-1
Content-Length: 322
Connection: close
Server: Jetty(9.3.11.v20160721)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
<title>Error 403 </title>
</head>
<body>
<h2>HTTP ERROR: 403</h2>
<p>Problem accessing /reject. Reason:
<pre> Forbidden</pre></p>
<hr />Powered by Jetty:// 9.3.11-SNAPSHOT<hr/>
</body>
</html>
2109ms client connection complete
Gotcha! The problem was not in the server side, but in the client side, which was only intended as a stub. Specifically, the problem was the buffering in the HttpUrlConnection.
Recalling my client.java, I have:
for (int i = 0; i < 1024; i++) {
out.write(b);
}
and if I change the loop to something like
for (int i = 0; i < 1024*1024; i++) {
out.write(b);
}
I immediately get an OutOfMemoryError exception, getting nothing on the server side, indicating that no single byte was transferred. And of course it is right, because BEFORE putting the headers on the wire the HttpUrlConnection needs to know the body length since it must emit the Content-Length header. Changing the client implementation to raw sockets, effectively controlling when bytes go to the wire, resolved the issue.
As a side note, the server code can be further simplified by removing the filter class. The complete server-side code is:
server.java:
public class SimplestServer
{
public static void main(String[] args) throws Exception
{
Server server = new Server(9080);
ServletHandler handler = new ServletHandler();
server.setHandler(handler);
handler.addServletWithMapping(HelloServlet.class, "/*");
server.start();
server.dumpStdErr();
server.join();
}
public static class HelloServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(System.currentTimeMillis() + ": Hello from HelloServlet GET");
}
#Override
protected void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(System.currentTimeMillis() + ": Hello from HelloServlet PUT");
// Perform some checks here
if (request.getHeader("X-Key") == null)
{
response.setHeader("Connection", "close");
response.sendError(HttpServletResponse.SC_FORBIDDEN);
System.out.println(System.currentTimeMillis() + ": Filter --> X-Key failed!");
return;
}
// Everything OK! Read the stream.
System.out.println(System.currentTimeMillis() + ": Proceded!!");
InputStream body = request.getInputStream();
long bytesReadSoFar = 0;
byte[] data = new byte[65536];
while (true) {
int bytesRead = body.read(data);
if (bytesRead < 0)
break;
bytesReadSoFar += bytesRead;
}
System.out.println(System.currentTimeMillis() + ": Finished! Read " + bytesReadSoFar + " bytes.");
response.setHeader("Connection", "close");
response.setStatus(HttpServletResponse.SC_OK);
}
}
}
In a given moment in time an authenticated session is created.
I need to create a jersey client (post method) using that authenticated session.
I've tried set the JSESSIONID in the jersey client but it doesn't recognize the session.
Client client = Client.create();
final String url = "http://localhost:8080/api/send";
WebResource wr = client.resource(url);
javax.ws.rs.core.Cookie cookie=new javax.ws.rs.core.Cookie("JSESSIONID", "521448844J5WE54D");
wr.cookie(cookie);
// Set POST parameters
FormDataMultiPart multipart = new FormDataMultiPart();
FormDataBodyPart fdp = new FormDataBodyPart("file", uploadedInputStream, MediaType.MULTIPART_FORM_DATA_TYPE);
multipart.bodyPart(fdp);
String response = wr.type(MediaType.MULTIPART_FORM_DATA_TYPE).post(String.class, multipart);
System.out.println(response);
I've tried also the code below, that in the jersey client I call first an API to authenticate the session and then try to use the same client object to call another API that require a auth session, didn't work.
Client client = Client.create();
final String url = "http://localhost:8080/api/auth";
WebResource wr = client.resource(url);
//set parametes for request
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
queryParams.add("user", "admin");
queryParams.add("pass", "123456");
wr.queryParams(queryParams);
ClientResponse response = wr.type(MediaType.MULTIPART_FORM_DATA_TYPE).post(ClientResponse.class);
System.out.println(response.getCookies().toString());
//------------------------------------------------------------
final String url2 = "http://localhost:8080/api/send";
WebResource wr2 = client.resource(url2);
// Set POST parameters
FormDataMultiPart multipart = new FormDataMultiPart();
FormDataBodyPart fdp = new FormDataBodyPart("file", uploadedInputStream, MediaType.MULTIPART_FORM_DATA_TYPE);
multipart.bodyPart(fdp);
String response2 = wr2.type(MediaType.MULTIPART_FORM_DATA_TYPE).post(String.class, multipart);
System.out.println(response2);
How can I do that ? I mean, how to use an authenticated JSESSIONID in a new jersey client connection ?
Regards.
I think the best way to do is to use JWT for user Authorization.
I am assuming that you have already authenticated the user via an API Endpoint. Once the user is authenticated, you can reply back a header element. You can read more about JWT # https://jwt.io/introduction/
Your implementation should look like the following steps.
1) Authenticate the user and upon successful authentication, add "Authorization: " token to the response.
2) In every API call, expect the user to pass the Authorization header with each request and use a Filter to authorize the user by parsing the JWT Token. You may want to #Inject the Parser and make sure that your parser is Threadsafe.
3-a) If the JWT Token is valid, you let the request pass through to your resource.
3-b) If the JWT Token is invalid, you reply back wit HTTP 401.
Here is a sample implementation.
import com.google.inject.Inject;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.proc.BadJOSEException;
import com.nimbusds.jose.proc.SecurityContext;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.JWTParser;
import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.text.ParseException;
#PreMatching
#Priority(Priorities.AUTHENTICATION)
#Provider
#Secured
public class SimpleAuthorizationFilter implements ContainerRequestFilter {
static JWTParser jwtParser = null;
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleAuthorizationFilter.class);
#Inject
private ConfigurableJWTProcessor jwtProcessor;
public SimpleAuthorizationFilter() {
LOGGER.debug("Init {}", getClass().getName());
}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Began authorization filter for {}", requestContext.getUriInfo().getPath());
}
MultivaluedMap < String, String > headers = requestContext.getHeaders();
JWT jwt = null;
if (headers.containsKey(AccessTokens.AUTHORIZATION)) {
String accessToken = headers.getFirst(AccessTokens.AUTHORIZATION);
try {
jwt = JWTParser.parse(accessToken);
} catch (ParseException parseException) {
LOGGER.error("Unable to parse JWT Token {}, reason {}", requestContext.getUriInfo().getPath(), parseException.getMessage());
throw new WebApplicationException("Unable to parse JWT Token", Response.Status.UNAUTHORIZED);
}
// Check if JWT has been init successfully.
if (jwt == null) {
LOGGER.error("JWT is null {}", requestContext.getUriInfo().getPath());
throw new WebApplicationException("Unable to init JWT", Response.Status.UNAUTHORIZED);
}
try {
if (jwt.getJWTClaimsSet().getExpirationTime().before(new java.util.Date())) {
LOGGER.debug("JWT Token expired on {}, requesting new token ", jwt.getJWTClaimsSet().getExpirationTime().toString());
} else {
// Do nothing, continue as usual.
}
} catch (ParseException e) {
LOGGER.error("Authorization failed # {} , due to {}", requestContext.getUriInfo().getPath(), e.getMessage());
throw new WebApplicationException("Unable to Authorize " + e.getMessage(), Response.Status.UNAUTHORIZED);
}
SecurityContext ctx = null; // optional context parameter, not required here
JWTClaimsSet claimsSet = null;
try {
claimsSet = jwtProcessor.process(accessToken, ctx);
} catch (ParseException e) {
LOGGER.error("Authorization failed # ParseException {} , due to {}", requestContext.getUriInfo().getPath(), e.getMessage());
throw new WebApplicationException("Unable to Authorize " + e.getMessage(), Response.Status.UNAUTHORIZED);
} catch (BadJOSEException e) {
LOGGER.error("Authorization failed # BadJOSEException {} , due to {}", requestContext.getUriInfo().getPath(), e.getMessage());
throw new WebApplicationException("Unable to Authorize " + e.getMessage(), Response.Status.UNAUTHORIZED);
} catch (JOSEException e) {
LOGGER.error("Authorization failed # JOSEException {} , due to {}", requestContext.getUriInfo().getPath(), e.getMessage());
throw new WebApplicationException("Unable to Authorize " + e.getMessage(), Response.Status.UNAUTHORIZED);
}
// This should not have happened.
if (claimsSet == null) {
LOGGER.error("JWT Claim is null failed # {} , due to {}", requestContext.getUriInfo().getPath());
throw new WebApplicationException("Unable to Authorize", Response.Status.UNAUTHORIZED);
}
} else {
LOGGER.error("Authorization header is missing {}", requestContext.getUriInfo().getPath());
throw new WebApplicationException("Authorization header is missing", Response.Status.UNAUTHORIZED);
}
}
}
I actually created an annotation #Secured and any resource method annotated with #Secured will be greeted first with this filter.
Here is my Annotation:
import javax.ws.rs.NameBinding;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#NameBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Secured { }
Then I created a DynamicFeature as:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
#Provider
public class ResourceFilterBindingFeature implements DynamicFeature {
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceFilterBindingFeature.class);
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if (resourceInfo.getResourceMethod().isAnnotationPresent(Secured.class)) {
LOGGER.info("{} is annotated to be a secure method " , resourceInfo.getResourceMethod().getName() );
context.register(CustomAuthorizationFilter.class);
}
}
}
You will need to register the above DyamicFeature in Jersey as
register(SimpleAuthorizationFilter.class)
Finally, here is my resource that I used to test
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#Path("/authorizationTest")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public class AuthorizationTest {
#GET
#Path("/secure")
#Secured
public Response secure(){
return Response.ok(MediaType.APPLICATION_JSON).build();
}
#GET
#Path("/unsecure")
public Response unsecure(){
return Response.ok(MediaType.APPLICATION_JSON).build();
}
}
Hope that helps.
I need to make a connection with Viagogo website using oAuth. Referring to their documentation I need to create a request similar to the following one
Using the example in step 1A, this means you may generate a signature base string that looks like the following:
GET&http%3A%2F%2Fapi.viagogo.net%2FPublic%2FSimpleOAuthAccessRequest&oauth_consumer_key%3Dtestkey%26oauth_nonce%3Dmyn0nc3%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1292404912%26oauth_version%3D1.0%26scope%3DAPI.Public
I am using the following code but when I comment lines 1,2 it return unauthorized error, and when I use them it shows oauthService.signRequest returns void.
TradeKingAPI.java
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.Token;
public class TradeKingAPI extends DefaultApi10a {
#Override
public String getRequestTokenEndpoint() {
return "http://api.viagogo.net/Public/SimpleOAuthAccessRequest";
}
#Override
public String getAccessTokenEndpoint() {
return "http://api.viagogo.net/Public/SimpleOAuthAccessRequest";
}
#Override
public String getAuthorizationUrl(Token requestToken) {
return "http://api.viagogo.net/Public/SimpleOAuthAccessRequest";
}
}
main.java
import org.scribe.builder.ServiceBuilder;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verb;
import org.scribe.oauth.OAuthService;
import api.TradeKingAPI;
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.OAuthConstants;
import org.scribe.oauth.OAuthService;
........
OAuthService oauthService = new ServiceBuilder()
.provider(TradeKingAPI.class)
.apiKey("My consumer key")
.apiSecret("My secret")
.scope("API.Public")
.build();
Long seconds = (System.currentTimeMillis() / 1000);
System.out.println(">>>" + seconds);
String stSeconds = seconds.toString();
OAuthRequest request = new OAuthRequest(Verb.GET, "http://api.viagogo.net/Public
/SimpleOAuthAccessRequest");
request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, "My consumer key");
request.addOAuthParameter(OAuthConstants.NONCE, "myn0nc3");
request.addOAuthParameter(OAuthConstants.SIGN_METHOD, "HMAC-SHA1");
request.addOAuthParameter(OAuthConstants.TIMESTAMP, seconds.toString());
request.addOAuthParameter(OAuthConstants.VERSION, "1.0");
request.addOAuthParameter("scope", "API.Public");
1 String signature = oauthService.signRequest(OAuthConstants.EMPTY_TOKEN, request);
2 request.addOAuthParameter(OAuthConstants.SIGNATURE,signature);
Response response = request.send();
System.err.println(">>" + response.isSuccessful());
System.err.println(">>" + response.getMessage());
System.err.println(">>" + response.getBody());
From what I understand from Viagogo public API access documentation, the token you get in the step 1, is the equivalent to a request token in a complete OAuth 1.0a "dance".
So, you should be able to use scribe-java internal classes to get this token without doing it by hand. The only difference is that in scribe, this request sends also a callback url to the OAuth server for the next step of OAuth "dance".
As I can't get a consumer account I can only make assumption here. So let's have 2 scenarios :
Scenario 1 : Viagogo server tolerate extra parameter (i.e. call back URL)
so you can go with this code
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.Token;
public class TradeKingAPI extends DefaultApi10a {
#Override
public Verb getRequestTokenVerb()
{
return Verb.GET;
}
#Override
public String getRequestTokenEndpoint() {
return "http://api.viagogo.net/Public/SimpleOAuthAccessRequest";
}
#Override
public String getAccessTokenEndpoint() {
return "none";
}
#Override
public String getAuthorizationUrl(Token requestToken) {
return "none";
}
}
Then your calling code will be :
OAuthService service = new ServiceBuilder()
.provider(TradeKingAPI.class)
.signatureType(QueryString)
.apiKey("My consumer key")
.apiSecret("My secret")
.scope("API.Public")
.build();
Token requestToken = service.getRequestToken();
//make your API calls
OAuthRequest request = new OAuthRequest(Verb.GET,
"http://api.viagogo.net/Public/Event/235");
service.signRequest(requestToken, request);
Response response = request.send();
System.out.println(response.getBody());
But as I said, if Viagogo security is a bit strict and it refuses the useless param oauth_callback, you'll need to switch to scenario 2
Scenario 2 : Build your own OAuthService
In this scenario you have to create a new OAuthService to avoid dealing with OAuthCallback parameter.
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.*;
import org.scribe.oauth.OAuth10aServiceImpl;
import java.util.Map;
public class OAuth10aServiceForViagogo extends OAuth10aServiceImpl {
private OAuthConfig config;
private DefaultApi10a api;
public OAuth10aServiceForViagogo(DefaultApi10a api, OAuthConfig config) {
super(api, config);
this.api = api;
this.config = config;
}
private void addOAuthParams(OAuthRequest request, Token token) {
request.addOAuthParameter(OAuthConstants.TIMESTAMP, api.getTimestampService().getTimestampInSeconds());
request.addOAuthParameter(OAuthConstants.NONCE, api.getTimestampService().getNonce());
request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, config.getApiKey());
request.addOAuthParameter(OAuthConstants.SIGN_METHOD, api.getSignatureService().getSignatureMethod());
request.addOAuthParameter(OAuthConstants.VERSION, getVersion());
request.addOAuthParameter(OAuthConstants.SCOPE, config.getScope());
request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, token));
}
private String getSignature(OAuthRequest request, Token token) {
String baseString = api.getBaseStringExtractor().extract(request);
String signature = api.getSignatureService().getSignature(baseString, config.getApiSecret(), token.getSecret());
return signature;
}
private void appendSignature(OAuthRequest request) {
for (Map.Entry<String, String> entry : request.getOauthParameters().entrySet()) {
request.addQuerystringParameter(entry.getKey(), entry.getValue());
}
}
#Override
public Token getRequestToken(RequestTuner tuner) {
OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint());
addOAuthParams(request, OAuthConstants.EMPTY_TOKEN);
appendSignature(request);
Response response = request.send(tuner);
String body = response.getBody();
return api.getRequestTokenExtractor().extract(body);
}
}
TrakingApi class will be slightly different to create the an OAuth10aServiceForViagogo when calling createService :
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.Token;
public class TradeKingAPI extends DefaultApi10a {
#override
public OAuthService createService(OAuthConfig config)
{
return new OAuth10aServiceForViagogo(this, config);
}
#Override
public Verb getRequestTokenVerb()
{
return Verb.GET;
}
#Override
public String getRequestTokenEndpoint() {
return "http://api.viagogo.net/Public/SimpleOAuthAccessRequest";
}
#Override
public String getAccessTokenEndpoint() {
return "none";
}
#Override
public String getAuthorizationUrl(Token requestToken) {
return "none";
}
}
Then your calling code will be the same :
OAuthService service = new ServiceBuilder()
.provider(TradeKingAPI.class)
.signatureType(QueryString)
.apiKey("My consumer key")
.apiSecret("My secret")
.scope("API.Public")
.build();
Token requestToken = service.getRequestToken();
//make your API calls
OAuthRequest request = new OAuthRequest(Verb.GET,
"http://api.viagogo.net/Public/Event/235");
service.signRequest(requestToken, request);
Response response = request.send();
System.out.println(response.getBody());
I didn't test all this code because I can't access consumer and secret key, but it should be close to what you need.
I'm assuming you're trying to get the access token (e.g you're calling SimpleOAuthAccessRequest). Scribe's OauthService has methods to handle this.
BUT ... if you're going to do it manually, here is what's wrong with your code - at least with what you've listed here. I'm assuming you've configured scribe correctly.
don't pass the consumer secret with your request, that is only for signing the request
you should use addOauthParameter vs addQueryStringParameter
you should use the Scribe constants
you need to sign the request (again, Scribe's OauthService has help method for signing request)
Here's your updated snippet of code.
UPDATE:
Have Scribe provide all the Oauth parameters for you
OAuthRequest request = new OAuthRequest(Verb.GET, ...
//since you're just passing Oauth parameters and nothing else,
//you can use signRequest will create Oauth Parameters for you
service.signRequest(OAuthConstants.EMPTY_TOKEN, request)
Response response = request.send()