Failed to connect GWT to BigCommerce API - gwt

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>

Related

Strange issue with Vertx Http request

I configured an HTTPS website on AWS, which allows visiting from a white list of IPs.
My local machine runs with a VPN connection, which is in the white list.
I could visit the website from web browser or by the java.net.http package with the below code:
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://mywebsite/route"))
.GET() // GET is default
.build();
HttpResponse<Void> response = client.send(request,
HttpResponse.BodyHandlers.discarding());
But if I replaced the code with a Vertx implementation from io.vertx.ext.web.client package, I got a 403 forbidden response from the same website.
WebClientOptions options = new WebClientOptions().setTryUseCompression(true).setTrustAll(true);
HttpRequest<Buffer> request = WebClient.create(vertx, options)
.getAbs("https://mywebsite/route")
.ssl(true).putHeaders(headers);
request.send(asyncResult -> {
if (asyncResult.succeeded()) {
HttpResponse response = asyncResult.result();
}
});
Does anyone have an idea why the Vertx implementation is rejected?
Finally got the root cause. I started a local server that accepts the testing request and forwards it to the server on AWS. The testing client sent the request to localhost and thus "Host=localhost:8080/..." is in the request header. In the Vert.X implementation, a new header entry "Host=localhost:443/..." is wrongly put into the request headers. I haven't debug the Vert.X implementation so I have no idea why it behaviors as this. But then the AWS firewall rejected the request with a rule that a request could not come from localhost.

Spring boot: Can not access secured resource from another Origin - CORS - Spring Security - Spring data rest

I can not access secured resource from another Origin. Searched a few days for solution and didn't find it, so I posted question here.
This is the story:
I created first Spring Boot Application that runs on default port 8080.
It depends on spring-boot-starter-data-rest and other dependencies and it has a GreetingRepository:
public interface GreetingRepository extends JpaRepository<Greeting, Long>, JpaSpecificationExecutor<Greeting> {}
globally enables CORS with RepositoryRestConfigurerAdapter:
#Configuration
public class GlobalRepositoryRestConfigurer extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.getCorsRegistry()
.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("*");
}
}
I created second Spring Boot Application that runs on port 9000 that will access this Greetings resource.
And it works. Second application sends HTTP request with method GET to http://localhost:8080/api/greetings and it gets response with Json data, with HEADER Access-Control-Allow-Origin: *. Everything is fine.
But.
Then I wanted to secure my resource in first application. There I included spring-boot-starter-security dependency and made configuration in WebSecurityConfigurerAdapter:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService myUserDetailsService;
#Autowired
PasswordEncoder myPasswordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.httpBasic()
.and()
.csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(myPasswordEncoder);
}
#Bean
public UserDetailsService createBeanUserDetailService() {
return new MyUserDetailsService();
}
#Bean
public PasswordEncoder createBeanPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
and made UserDetailsService and so on. (Important: I tested security before adding CORS, so this security configuration works and that is not a problem).
Then, after adding security in first application, second application sends same HTTP request with method GET to http://localhost:8080/api/greetings as the first time.
Now it gets an error:
Failed to load http://localhost:8080/api/greetings: Redirect from 'http://localhost:8080/api/greetings' to 'http://localhost:8080/login' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access.
I can not find solution for this problem. So CORS works for Spring Repository resources, and Spring Security works, but I can not access secured resource from another Origin because of /login page. How to solve this?
When Spring security is enabled, the security filters take precedence over CorsFilter. To make Spring Security aware of your CORS configuration call the cors() method in your HttpSecurity setup.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.httpBasic()
.and()
.csrf().disable();
}
This will give CorsFilter precedence over other Spring security filters. CorsFilter recognizes CORS preflight requests (HTTP OPTIONS calls) and allows it to go through without any security.

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.

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.

GWT Client files in one Server & GWT Servlets in other Server

I'm trying to separate GWT Client & Server. If i'm not wrong, GWT client code is getting server reponses by connecting to the servlet we mentioned in GWT Project's web.xml. If So, can i host my GWT Servlets in one Tomcat Server & GWT Client code in other tomcat server ..?
Will it work ..? If so how to do that, i have already tried something working with hosted.html in GWT Client files. But it didn't worked
Yes, you can host client files in any web-server since they are static stuff, actually what you need is to pick your index.html, the .nocache.js and all the *.cache.(js|html) files and put them in any web server (apache, nginx, iis, jetty, etc).
You could even replace the index.html by any other html generator like php, jsp etc.
But of course the server side should be hosted in a servlet container.
What you have to be aware about, is that when the server with your static files are in a different domain than the servlet server, ajax requests will fail because of security constrains (see CORS).
To avoid that restriction there are many ways in gwt (gwtquery-jsonp, gwt-xdm, etc).
What I'm using is a filter (see code above) able to enable CORS when an options request is received.
You have to modify your client code as well to configure correctly the url of the servlet-container. Here is an example for changing the url with RequestFactory.
Client side code for RF:
myFactory = GWT.create(MyRFFactory.class);
DefaultRequestTransport transport = new DefaultRequestTransport();
transport.setRequestUrl("http://my.servletcontainer.com/gwtRequest");
myFactory.initialize(eventBus, transport);
web.xml configuration
<filter>
<filter-name>CORSFilter</filter-name>
<filter-class>my.namespace.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Server filter
public class CORSFilter implements Filter {
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
String o = req.getHeader("Origin");
if ("options".equalsIgnoreCase(req.getMethod())) {
resp.setHeader("Allow", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS");
if (o != null) {
resp.addHeader("Access-Control-Allow-Origin", o);
resp.addHeader("Access-Control-Allow-Methods",
"POST, GET, OPTIONS");
resp.addHeader("Access-Control-Allow-Headers",
"content-type,pageurl,x-gwt-permutation");
resp.setContentType("text/plain");
}
resp.getWriter().flush();
return;
}
if (o != null) {
resp.addHeader("Access-Control-Allow-Origin", o);
}
if (filterChain != null) {
filterChain.doFilter(req, resp);
}
}
#Override public void destroy() {
}
#Override public void init(FilterConfig arg0) throws ServletException {
}
}
The same question has been asked in the past.
gwt-split-client-and-server
What you can do is use the servlet as a proxy to another server, where you have implemented your model logic.