How can I force UriBuilder to use https? - jersey-2.0

I'm currently running Dropwizard behind Apache httpd acting as a reverse proxy, configured like so:
<VirtualHost *:443>
<Location /api>
ProxyPass "http://my.app.org:8080/api"
<Location>
...
</VirtualHost>
With other Location settings serving static assets and some authentication thrown in. Now, httpd also performs SSL offloading, so my Dropwizard only receives the plain HTTP request.
In my Dropwizard API, I like to return a Location header indicating newly created resources:
#Path("/comment")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
class CommentResource() {
#PUT
fun create(#Context uri: UriInfo, entity: EventComment): Response {
val stored: EventComment = createEntity(entity)
return Response.created(uri.baseUriBuilder.path(MessageStream::class.java)
.resolveTemplate("realmish", entity.realmId)
.path(stored.id.toString()).build()).build()
}
This creates a Response with a Location header from JerseyUriBuilder:
Location http://my.app.org/api/messages/123
Which, on my SSL-only app, naturally fails to load (I'm actually surprised this didn't turn out to render as http://my.app.org:8080/api/messages/123 - probably also the reason why ProxyPassReverse didn't help).
I know I can force the scheme to be https by using baseUriBuilder.scheme("https"), but this gets repetitive and is an easy source of errors.
Thus my question: how can I either make Jersey generate correct front-end URLs or successfully make httpd rewrite those generated by Dropwizard?

For Jersey, you can use a pre-matching ContainerRequestFilter to rewrite the URI. For example
#PreMatching
public class SchemeRewriteFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext request) throws IOException {
URI newUri = request.getUriInfo().getRequestUriBuilder().scheme("https").build();
request.setRequestUri(newUri);
}
}
Then just register it with Jersey (Dropwizard)
env.jersey().register(SchemeRewriteFilter.class);
EDIT
The above only works when you use uriInfo.getAbsolutePathBuilder(). If you want to use uriInfo.getBaseUriBuilder(), then you need to use the overloaded setRequestUri that accepts the base uri as the first arg.
URI newUri = request.getUriInfo().getRequestUriBuilder().scheme("https").build();
URI baseUri = request.getUriInfo().getBaseUriBuilder().scheme("https").build();
request.setRequestUri(baseUri, newUri);

If using Jetty, then you can avoid the hacks by registering the org.eclipse.jetty.server.ForwardedRequestCustomizer with your server. This will look at the X-Forwarded-* headers to build the base URI.
Sample using embedded Jetty:
Server jettyServer = new Server();
HttpConfiguration config = new HttpConfiguration();
config.addCustomizer(new ForwardedRequestCustomizer());
ServerConnector serverConnector = new ServerConnector(jettyServer,
new HttpConnectionFactory(config));
serverConnector.setPort(8080);
jettyServer.setConnectors(new Connector[] {serverConnector});
This seems to work whether or not you are behind a reverse proxy, so I don't know why it isn't just enabled by default.

Related

Failed to connect GWT to BigCommerce API

Criteria: I'm trying to connect to a secured web service API called BigCommerce using GWT RequestBuilder.
This is my entry point:
public class GwtTest implements EntryPoint {
String url = "http://my-url-api/api/v2/products.xml"; // not the original url i'm using
#Override
public void onModuleLoad() {
url = URL.encode(url);
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
builder.setHeader("Authorization", "Basic XXX"); // I generated this from Postman app on Chrome where things work perfectly
builder.setHeader("Access-Control-Allow-Credentials", "true");
builder.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8888/");
builder.setHeader("Access-Control-Allow-Methods", "POST, GET, UPDATE, OPTIONS");
builder.setHeader("Access-Control-Allow-Headers", "x-http-method-override");
builder.setHeader("Content-Type", "application/xml");
try {
builder.sendRequest(url, new RequestCallback() {
#Override
public void onResponseReceived(Request request, Response response) {
RootPanel.get().add(new HTML("Success: "+response.getText()));
}
#Override
public void onError(Request request, Throwable exception) {
RootPanel.get().add(new HTML("Failure (Response Error): "+exception));
}
});
} catch (RequestException e) {
RootPanel.get().add(new HTML("Failure Request Exception: "+e));
}
}
}
Errors encountered: I encounter the Same Origin Policy error at first:
Then After I disabled CORS on my browser I get the Perflight error:
Work-around: I was able to get results by disabling web security on Chrome but I don't think it's the right solution.
Trivial note: Please guide me on this one, guys, because I'm new to GWT and BigCommerce thanks.
Use a proxy servlet.
This is also the solution the GWT Openlayers wrappers uses.
https://github.com/geosdi/GWT-OpenLayers/blob/c3becee0cdd5eefdc40b18e4999c2744dc23363a/gwt-openlayers-server/src/main/java/org/gwtopenmaps/openlayers/server/GwtOpenLayersProxyServlet.java
Based on Rob Newton answer, if your application is pure front end, you can host your files on nginx and add some proxy_pass directive to your configuration, for example:
location ~* ^/bigcommerce/(.*) {
proxy_pass http://api.bigcommerce.com/$1$is_args$args;
}
So whenever you call http://hostaddress/bigcommerce/something, this will be forwared to http://api.bigcommerce.com/something. Headers are not forwared in this config, you can add more directives for that.
You could include a servlet in your webapp that acts as a proxy between the client and BigCommerce.
An alternative is to run a reverse proxy such as Apache httpd that makes requests to the BigCommerce server appear to be on the same host as your webapp.
Here is an example of an Apache httpd config file that will act as a reverse proxy for your webapp and an external service on a different host. To the browser both the webapp and the external service will appear to be running on the same host, which is what your want.
# file: /etc/httpd/conf.d/mywebapp.conf
<VirtualHost *:80>
ProxyPreserveHost On
# Forward requests to path /SomeExternalService to an external service
ProxyPass /SomeExternalService http://externalhost/
# Forward all other requests to a local webserver hosting your webapp,
# such as Tomcat listening on port 8081
ProxyPass / http://127.0.0.1:8081/
ProxyPassReverse / http://127.0.0.1:8081/
</VirtualHost>

Fix CORS header Access-Control-Allow-Origin missing on plugin development on eclipse

-I am try to make a modular application using eclipse plugin development and using jax-rs.
-I want to access an event source created by jetty server and translate each event in time.
-When i try to access the event i am get this error in firefox that run my client html 5 page :
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:9050/services/events. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
-I know that i must configure the server , but i don't have .htaccess and no web-inf dir.
-Is there any to declare this file in the vm arguments using eclipse ?
-Is there any other way to do it ?
-I don't have WEB-INF directory and i don't know if it supported in this plugin development approach.
-I don't have main function I have only bundles (activator, ect.) and I don't have main function .
-I also have manifest.mf file
Any help is accepted.Thanks in advance!
Try implementing a response filter which would add your needed headers to the response.
#Provider
public class CORSFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
// the wildcard char `*` will allow any origin
responseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
// add anything and everything you need
responseContext.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type");
responseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
// etc
}
}
Don't forget to register it.

Jersey REST GET is working but PUT not. The specified HTTP method is not allowed for the requested resource

I've been breaking my head over this for a few days now. This little sniplet is working fine (using Jersey 2.26-b03 on Tomcat).
#GET
#Path("/{code}")
public Response update(#PathParam("code") String code) {
System.out.println("!!!!!!!");
return Response.status(Response.Status.OK).build();
}
curl -i -X GET http://localhost:18270/nyx/rest/servervirtueel/SVM0000
HTTP/1.1 200 OK
Followed by a bunch of Jersey tracing I enabled. But if I only change the GET to a PUT (exactly the same method, just change the annotation):
#PUT
#Path("/{code}")
public Response update(#PathParam("code") String code) {
System.out.println("!!!!!!!");
return Response.status(Response.Status.OK).build();
}
curl -i -X PUT http://localhost:18270/nyx/rest/servervirtueel/SVM0000
HTTP/1.1 405 Method Not Allowed
Followed by HTML telling me that the "The specified HTTP method is not allowed for the requested resource". However, POST does work (changing the annotation again).
It turned out that the OWASP method whitelist valve was configured on Tomcat (Catalina) level to only allow GET and POST; this is a webapp that held only SOAP services until now. You do not see this in either web.xml's or server.xml, but it's in Catalina/localhost/webappname.xml.

Protecting Jersey web service with SSL

I have pretty simple web-service, which runs on the Embedded Glassfish server:
#Stateless
#Path("/id")
#Produces(MediaType.TEXT_HTML)
public class SimpleFacadeBean {
#GET
public String sayHello(#Context HttpServletRequest request, #Context HttpServletResponse response) {
return "Hello world!";
}
}
And corresponding class, that set root-element for all web-service calls:
#ApplicationPath("root")
public class ApplicationConfig extends Application {
}
Everything works fine, but I want to protect this GET method through SSL.
I have read such manuals as: this, and this. I am not interested in configuring Glassfish part of SSL connection (I know how to do it), I just want not to see my GET method in localhost:8080/root/application.wadl link (this will mean that my web-service is connected to 8181 secure glassfish port, not to default unsecure 8080 port).
For this purpose I wrote web.xml descriptor and put it in webapp/WEB-INF directory. The most interesting part of it looks like:
<security-constraint>
<display-name>SecurityConstraint</display-name>
<web-resource-collection>
<web-resource-name>Secure Area</web-resource-name>
<url-pattern>/root/id</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
But after executing I saw (in application.wadl) again my web-service URL connected to unsecured port (8080 instead of 8181). I have tried a lot of other ways, trying to make my code work correctly. I was not successful at all.
I package this web-services into .war. After that this .war goes to .ear. Any ideas why I am not successful in NOT seeing my "secure" web-service in application.wadl?

Cross domain REST/Jersey web services with CORS

I want to make Cross domain REST web services with CORS(Cross-Origin Resource Sharing). I am using Jersey Libraries for making services.
I need to know
What code/configuration changes i need to do from server side perspective?
How to invoke this services from HTML5/js.
Thanks
All the information for your server side configuration can be found at enable-cors.org.
There is no need to change any code clientside, but I would recommend checking the Browsers capability for CORS before using it. Testing code can be found, e.g., here.
I have chosen to solve this problem by providing the server CORS response at the Jersey container level. This might offer more convenience for some applications since it can apply for all responses from the container without modification to the resource handling code.
First one has to create a container response filter that will inject the appropriate header(s). For example for a container that indicates Access-Control-Allow-Origin:* for any response:
class CORSFilter implements ContainerResponseFilter {
#Override
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
response.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
return response;
}
}
Then the filter must be added be added to the Jersey response filter chain. This can be done through the resource config in use for the application.
...
DefaultResourceConfig rc = new ClasspathResourceConfig();
rc.getContainerResponseFilters().add(new CORSFilter());
// now create a simple lightweight server using this resource config.
HttpServer server = HttpServerFactory.create(uri,rc);
...
Steps which I used to enable CORS Filter in my Jersey based embedded Jetty application.
jetty-servlet version - 2.12
Added cors-filter dependency in pom
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.1.2</version>
</dependency>
Add the corsfilter to ServletContextHandler of your application.
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
context.addFilter(CORSFilter.class, "/*", EnumSet.of(DispatcherType.INCLUDE,DispatcherType.REQUEST));
server.setHandler(context);//set handle to your server