Cross domain REST/Jersey web services with CORS - rest

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

Related

RESTful call testing in Eclipse

In my case, I am running a eclipse project providing the Restful api, and I will call that api like in the following example. I am curious if I should create another project in the eclipse to run the following code to test the api.
Jersey Example
Form form = new Form();
form.add("x", "foo");
form.add("y", "bar");
Client client = ClientBuilder.newClient();
WebTarget resource = client.target("http://localhost:8080/someresource");
Builder request = resource.request();
request.accept(MediaType.APPLICATION_JSON);
Response response = request.get();
if (response.getStatusInfo().getFamily() == Family.SUCCESSFUL) {
System.out.println("Success! " + response.getStatus());
System.out.println(response.getEntity());
} else {
System.out.println("ERROR! " + response.getStatus());
System.out.println(response.getEntity());
}
you can run your code as a java application using main method.
Or since you are saying you have you services running you can use POSTMAN REST Client available as a plugin for chrome and chromium browsers. i don't know about the support for the same in other browsers.
Using postman you'll be able to see exactly how your restful service is working. you'll be able to send request headers and other parameters as a part of the rest request. Postman is the way to go for end to end web service testing.

How can I force UriBuilder to use https?

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.

Working Java REST Client Example to access CAS REST API

I followed this tutorial to enable REST service on my local CAS server.
However there is no Java example
"Java REST Client Example
We need a real, working, example, the previous one is useless. Many people are emailing me that it is not working, and I confirm it does not work."
I was able to find this but that unfortunately did not work for me.
Any pointers/links? Much appreciated.
Got it!
Here is the complete solution on how to enable CAS REST API and be able to connect to it via JAVA REST client to benefit others
Get CAS source code.
Review this article
Add following to pom.xml like suggested by the article in #2
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-integration-restlet</artifactId>
<version>${cas.version}</version>
<type>jar</type>
</dependency>
Make sure to add following to pom.xml to avoid Spring jar collisions. In my case, cas-server-integration-restlet was dependent on spring-web, which used by default older version of Spring. So, I explicitly defined
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
Compile your cas code. Should get cas.war in your target folder.
Upload it to your server, change permissions to tomcat and wait for it to get deployed
In CATALINA/conf find server.xml and uncomment 8443 port configuration so that our sever will allow SSL connections. Also, specify your certs in here.
Now navigate to exploded cas.war file and drill down to WEB-INF folder to find deployerConfigContext.xml file. Specify what CAS would use to authenticate. In my case, I used LDAP.
Add following to web.xml per article above
<servlet>
<servlet-name>restlet</servlet-name>
<servlet-class>com.noelios.restlet.ext.spring.RestletFrameworkServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restlet</servlet-name>
<url-pattern>/v1/*</url-pattern>
</servlet-mapping>
Restart tomcat for changes to take effect.
Test that you can log in via standard CAS UI: https://server:8443/cas/login
Test that REST API was exposed via: https://server:8443/cas/v1/tickets
Now let's connect to it. I used this sample code. Make sure to give correct links and username/password
When I tried running the code as is, it complained about "Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target". Basically asking you to install certs. If you have the access to the server, just copy it over. If not, I found this code that will take care of the installation for you if you dont have access or just too lazy :)
Now, if you run the JAVA CAS Client with valid credentials you should see something like
201
https://server_name:8443/cas/v1/tickets/TGT-4-rhVWLapYuOYi4InSEcmfNcABzaLMCPJgGIzlKqU1vb50zxb6pp-server_name
Tgt is : TGT-4-rhVWLapYuOYi4InSEcmfNcABzaLMCPJgGIzlKqU1vb50zxb6pp-server_name.ndev.coic.mil
Service url is : service=https%3A%2F%2Fmyserver.com%2FtestApplication
https://server_name:8443/cas/v1/tickets/TGT-4-rhVWLapYuOYi4InSEcmfNcABzaLMCPJgGIzlKqU1vb50zxb6pp-server_name
Response code is: 200
200
ST-4-BZNVm9h6k3DAvSQe5I3C-server_name
You can see 200 code and the ticket. If you were to review logs of your cas on the server, you should see messages about successful athentication and ticket generation.
Change username/password to some dummy data and try to run the code. You will get 400 error message, which means that permission to access was denied.
Success!
For CAS 4.0 it's a little simpler (tested on apache-tomcat-7.0.55)
in your pom.xml add following dependency
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-integration-restlet</artifactId>
<version>4.0.0</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
</exclusions>
</dependency>
Direct dependency to springframework is not necesarry because exclusions prevent from duplicated packages
In your web.xml you need to add servlet mapping for restlet (mind package has changed from com.noelios.restlet... to org.restlet...
<servlet>
<servlet-name>restlet</servlet-name>
<servlet-class>org.restlet.ext.spring.RestletFrameworkServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restlet</servlet-name>
<url-pattern>/v1/*</url-pattern>
</servlet-mapping>
As a result of above steps in yuor WEB-INF/lib directory following new files should be added
ls target/cas/WEB-INF/lib/ | grep restlet
cas-server-integration-restlet-4.0.0.jar
org.restlet-2.1.0.jar
org.restlet.ext.servlet-2.1.0.jar
org.restlet.ext.slf4j-2.1.0.jar
org.restlet.ext.spring-2.1.0.jar
If you wish to skip cert validation add this to your Java Client
//////////////////////////////////////////////////////////////////////////////////////
// this block of code turns off the certificate validation so the client can talk to an SSL
// server that uses a self-signed certificate
//
// !!!! WARNING make sure NOT to do this against a production site
//
// this block of code owes thanks to http://www.exampledepot.com/egs/javax.net.ssl/trustall.html
//
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType){}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType){}
}
};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
//
//
// end of block of code that turns off certificate validation
// ////////////////////////////////////////////////////////////////////////////////////
Usually devs are confused on how to get rest client working when accessing secured CAS web service. Most of the question out there were asking how to get restlet CAS secures a webservice and how to call those web service, because no real example were working.
Well actually there is. Groovy example is on the JASIG Cas restlet example https://wiki.jasig.org/display/casum/restful+api is clearly show how to get authenticated to call a service (its using Groovy, but converting to Java should be straight forward) . But in my opinion, it do not clearly explain that client need to authenticate to the designated web service first before accessing CAS secured web service.
For example, assume there is a JSON service that secured with CAS and build with Java and Spring. And you are using code that describe on the groovy section on https://wiki.jasig.org/display/casum/restful+api
String casUrl="https://yourcas.com/v1/tickets"
String springTicketValidation="http://yourservice.com/j_spring_cas_security_check"
String serviceToCall="http://yourservice.com/serviceToCall"
To get your service client be able to call the service, you need to follow these simple rules:
Get your ticket granting ticket from CAS
Get your Service Ticket from cas for the designated service call (service to call)
Authenticate to your service ticket validator (at this point url specified on springTicketValidation)
finally call your service
or in code perspective
String ticketGrantingTicket = getTicketGrantingTicket(casUrl, username, password)
String serviceTicket = client.getServiceTicket(casUrl, ticketGrantingTicket, serviceToCall)
// validate your ticket first to your application
getServiceCall(springTicketValidation, serviceTicket)
getServiceCall(serviceToCall, serviceTicket)
And for your note, all these operation should be done in following condition:
Your call (both restlet call and service call) should be done in the same HttpClient object. It seems that CAS put "something" in the session object that validated when you call your service. Fails this, and you will always get logon page on the HTTP result.
Your cas client should be able to recognized your CAS SSL certificate, otherwise it will throw you PKIX path building failed
This example is based on the cas secured web service that using Spring Security to secured service with CAS. I'm not sure whether other cas secured should need ticket validation on the application side or not
Hope this help

Camel ReST Proxy route in ServiceMix fails for Base64 uploads

I have deployed on SMX the following route that proxies all ReST request to the real ReST service provider (Tomcat). All ReST calls to SMX routed successfully however a saveDocument service that uploads PDF files fail.
public void configure() throws Exception {
from("jetty:http://{{smx.host}}:{{smx.rest-proxy-port}}/{{smx.context}}matchOnUriPrefix=true")
.log("ReST call received (Java DSL)")
.to("jetty:http://{{real-server-address}}:{{real-ws-port}}/{{context}}?bridgeEndpoint=true&throwExceptionOnFailure=false")
.log("Rest call proxied (Java DSL)");
}
The following exception is logged in servicemix.log.
19:53:57,065 | WARN | HttpClient-137 | HttpExchange | 111 - org.eclipse.jetty.util - 7.5.4.v20111024 | EXCEPTION JettyContentExchange#188af650=POST//real-server-address...:8080/contextpath.../saveDocument#SENDING(3ms)->
EXCEPTED(0ms)sent=3ms java.lang.IndexOutOfBoundsException
Do I have to perform some additional processing on the base64 before redirect the call to the real ReST service?
UPDATE on my previous post.
This seems to work when I use txt file but fails for pdf or doc.
UPDATE 2: It also fails when txt size exceeds 7KB.
Is it possible to set camel jetty to accept big size files?
This can be solved if Multipart WS is used.
I have implemented a Multipart CXF ReST file upload service for testing the route in case of a multipart WS.
The following route works OK for multipart:
from("jetty:http://.../?matchOnUriPrefix=true&enableMultipartFilter=false")
.noStreamCaching()
.log("Service Proxied")
.to("jetty:http://...:../?bridgeEndpoint=true&throwExceptionOnFailure=false");
Still cannot find what is going wrong with the first WS.
The issue is tracked here also.

how can I stop the application part of the url being passed to my servlet in tomcat6

I am running tomcat6 and have hooked it up in eclipse as a server so I can quickly debug code changes - however I am running into an issue with the routes that are passed to my servlet.
I am running spring mvc3 with my routes annotated on the class eg:
#Controller
#RequestMapping(value="/rest")
public class HandleItController {
...
in web.xml I have
<welcome-file-list>
<welcome-file>welcome.html</welcome-file>
</welcome-file-list>
<servlet-mapping>
<servlet-mapping>handleit</servletmapping>
<url-pattern>/rest</url-pattern>
</servlet-mapping>
However I can only get one of these settings to work as desired at a time.
lets say "Web Project Settings" has Context root set to be appname
now if I GET localhost:8080/appname I will get the welcome page as desired
however if I hit localhost:8080/appname/rest/yadda I get a warning saying
No mapping found for HTTP request with URI [/appname/rest/yadda] in
DispatcherServlet with name 'handleit'
If I change my servlet url-pattern to / then I get request routed through the servlet without the appname prepended and the servlet handles them as expected - however I cannot then hit the welcome page
I need a solution that does not involve hard coding appname into the web.xml or the controller mappings, there must be some way I can serve both the html file and the servlet that is independent of the uri to which my application is deployed - ie stop sending the context part of the url through to the servlet
The URI in the #RequestMapping will be appended to the url-mapping of the dispatcher servlet. So if both the servlet and controller is mapped to rest, the full URI will become /contextpath/rest/rest. If you don't want that, map your controller to /
Edit: The reason it doesn't work when you map your servlet to / is that the Spring dispatcher servlet handles everything under the context root. So to get that to work, you need to configure Spring MVC to serve static files.