Forums de discussion

Access-Control-Allow-Origin JSONWS

thumbnail
Eric COQUELIN, modifié il y a 8 années.

Access-Control-Allow-Origin JSONWS

Expert Publications: 254 Date d'inscription: 03/11/13 Publications récentes
Dear all,

I have setup remote services accessible with JSON Web services.

When one the service is being called using AJAX, the browser reject the response as header Access-Control-Allow-Origin is not populated with "*"

How can I force Liferay to populate this value? Generally, how can we achieve architecture where there are mobile client or any other client type with interacts with the Liferay server using JSON WS ?

Thank you in advance for your help
thumbnail
Eric COQUELIN, modifié il y a 8 années.

RE: Access-Control-Allow-Origin JSONWS

Expert Publications: 254 Date d'inscription: 03/11/13 Publications récentes
Hi,

Do anybody can help ?

I make the following call from an HTML page hosted by another application running on port 8091 on the same server

var req = new XMLHttpRequest();
req.open("GET", "http://localhost:8090/api/jsonws/zp-portlet.vendor/get-vendors/company-id/20254");
//req.setRequestHeader("Authorization", "Basic " + btoa("mrjones:test12345"));
req.send();


Authentication is deactivated so far but it might be the next issue... emoticon

My portal-ext.properties

json.service.auth.token.hosts.allowed=127.0.0.1,SERVER_IP
json.service.auth.token.enabled=false
jsonws.servlet.hosts.allowed=127.0.0.1,SERVER_IP


I can't imagine it is that complicated. I assume, it is a single configuration to update somewhere...
thumbnail
Eric COQUELIN, modifié il y a 8 années.

RE: Access-Control-Allow-Origin JSONWS

Expert Publications: 254 Date d'inscription: 03/11/13 Publications récentes
Dear all,

It took time but I got it...

For JSON exchange, you can't use "XMLHttpRequest" but should use "fetch" (https://davidwalsh.name/fetch). I know fetch is not yet widely supported but I understand that it should be soon the case.

Here is an example of call

 fetch("http://localhost:8090/api/jsonws/zp-portlet.vendor/get-vendors/company-id/20254",
            {mode: 'no-cors', credentials: 'include'})
          .then(function(response) {
            console.log(response);
          }).catch(function(ex) {
            console.log('parsing failed', ex)
          })


With that call, it will reuse the cookies already received from the server, including the JSESSIONID. Which is great. Then authentication of subsequent request is solved.

Now, I need to understand how to get authenticated and have this cookie populated. If anyone has any clue, please help.

Have a good weekend.
thumbnail
Eric COQUELIN, modifié il y a 8 années.

RE: Access-Control-Allow-Origin JSONWS

Expert Publications: 254 Date d'inscription: 03/11/13 Publications récentes
I wanted to share further analysis I made on this subject.

I was wrong when writting that fetch would solve my issue. I did not understand immediatly that fetch is "just" a new generation of XMLHttpRequest... As a result, even if you have the feeling that the request is correctly processed by the server, you still have some limitations.

Indeed, when sending the request, I had the feeling that I could process the response
fetch( url, {mode: 'no-cors', credentials: 'include', headers: headers} )
          .then(function(response) {
            console.log(response);
          }).catch(function(ex) {
            console.log('parsing failed', ex)
          })


Then I realized, reading Google Chrome documentation that the response will be "opaque" because the request is not permitted between domains. Response details are not available but the request is correctly processed on server side as if it was a valid request coming from a valid host.

But, in most cases, not being able to read the response is a real problem. So I understood that the request should be sent as follows:
fetch( url, {mode: 'cors', credentials: 'include', headers: headers} )
          .then(function(response) {
            console.log(response);
          }).catch(function(ex) {
            console.log('parsing failed', ex)
          })


i/o "no-cors", I should use "cors". Now, the server rejects the request because it comes from an invalid domain.

Next step is to change Tomcat configuration so that it allows cors requests. Liferay web.xml was updated as follows
<!-- http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html#CORS_Filter -->
	<!-- Doit être ajouté pour traiter le pb des accès distants en XMLHttpRequest ou autres -->
	<filter>
	  <filter-name>CorsFilter</filter-name>
	  <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
	  <init-param>
		<param-name>cors.allowed.origins</param-name>
		<param-value>*</param-value>
	  </init-param>
	  <init-param>
		<param-name>cors.allowed.methods</param-name>
		<param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
	  </init-param>
	  <init-param>
		<param-name>cors.allowed.headers</param-name>
		<param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
	  </init-param>
	  <init-param>
		<param-name>cors.exposed.headers</param-name>
		<param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
	  </init-param>
	  <init-param>
		<param-name>cors.support.credentials</param-name>
		<param-value>true</param-value>
	  </init-param>
	  <init-param>
		<param-name>cors.preflight.maxage</param-name>
		<param-value>10</param-value>
	  </init-param>
	</filter>
	<filter-mapping>
	  <filter-name>CorsFilter</filter-name>
	  <url-pattern>/*</url-pattern>
	</filter-mapping>


After restarting Liferay, the request is correctly processed and the response can be processed as well.

I don't really know if it is the best approach but at least it works. If you have any comments, don't hesitate to add, it will be more than helpful.
thumbnail
Andrew Jardine, modifié il y a 8 années.

RE: Access-Control-Allow-Origin JSONWS

Liferay Legend Publications: 2416 Date d'inscription: 22/12/10 Publications récentes
Hi Eric,

Thanks for sharing your results and analysis with the community. Too often people post a problem, find a solution but then never update the thread with their findings.

Reading your post though I have a couple of comments .. maybe something for you to try as well if you have a moment emoticon. Changing the web.xml for the ROOT application of course can be done but from experience it leads to problems when you want to upgrade. In particular I have found that with the EE version of the product when I use the patching tool to apply service packs, the changes I have made to the web.xml are lost. The only work around to this I believe... and perhaps you could argue "the purist way" to make such changes is with a *barf* EXT plugin emoticon.

Having said that, I recently had to enable CORS on an application. In our case it was another app that was iframing content from the Liferay side but regardless, CORS was the issue. Rather than turn it on for the whole server we used the portal-ext settings --


        http.header.secure.x.frame.options=true
        http.header.secure.x.frame.options.1=<first_url>
        http.header.secure.x.frame.opti ons.2=<second_url>
        ...
</second_url></first_url>


so that only the urls that HAD to support CORS did.

I am curious, could you try this on your side to see if it allows you to fix your issue without enabling CORS for the entire server?
thumbnail
Eric COQUELIN, modifié il y a 8 années.

RE: Access-Control-Allow-Origin JSONWS

Expert Publications: 254 Date d'inscription: 03/11/13 Publications récentes
Hi Andrew,

Thank you for your response.

I never noticed that file https://docs.liferay.com/portal/6.2/propertiesdoc/system.properties.html and will try what you provided. However, I'm wondering if we cant set a prefix i/o a exact URL because I'd like to allow all JSON WS.

I will let you know after trying. Thank you for the tips.
thumbnail
Andrew Jardine, modifié il y a 8 années.

RE: Access-Control-Allow-Origin JSONWS

Liferay Legend Publications: 2416 Date d'inscription: 22/12/10 Publications récentes
Hi Eric,

If I understand you correctly, I think you are asking about using this property to configure a "root" location under which all child urls would follow the same value. I'm not sure about doing that with this option to be honest. In our case we have just a couple of URLs that we wanted to allow the external application to "iframe". I would also advocate that, even if there are 50 or 60 different URLs that you want to allow CORS on, better to specify those 50 or 60 (as painful as that might be) than to open up the server entirely. This is after all not just a pain in the ass, but a security feature emoticon
thumbnail
Eric COQUELIN, modifié il y a 8 années.

RE: Access-Control-Allow-Origin JSONWS

Expert Publications: 254 Date d'inscription: 03/11/13 Publications récentes
Hi Andrew,

Unless I missunderstood something, the XFRAME is not a solution to the CORS issue.

As stated in the system.properties comments, the default behavior is SAMEORIGIN

http.header.secure.x.frame.options=true
http.header.secure.x.frame.options.255=/|SAMEORIGIN


If you want to block other URL, you just set the URL inbetween (with a value lower than 255) or if you want to deny everything, you put DENY i/o SAMEORIGIN and allow only some of the URL.

With that configuration, it doesn't change Liferay behavior.


Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,de;q=0.2,ms;q=0.2
Access-Control-Request-Headers:authorization, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:localhost:8090
Origin:http://localhost:8091
Referer:http://localhost:8091/
User-Agent:Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B137 Safari/601.1


Liferay controls the header "Origin" and then notice that it doesn't match the server URL (running on port 8090) and the brower reject the request because Liferay did not provide "Access-Control-Allow-Origin" header in the response...

Without the CORS filter, the problem remains. And I'm still wondering why there is no easiest solution to solve this problem.
thumbnail
Eric COQUELIN, modifié il y a 8 années.

RE: Access-Control-Allow-Origin JSONWS

Expert Publications: 254 Date d'inscription: 03/11/13 Publications récentes
I have seen that a bug was opened in 2014 but closed without any modification : https://issues.liferay.com/browse/LPS-46310

However, I have the feeling it was closed for a bad reason. Security on server side is important, indeed. But if no browser can communicate with Liferay, this is an issue... At least my issue so far emoticon
thumbnail
Andrew Jardine, modifié il y a 8 années.

RE: Access-Control-Allow-Origin JSONWS

Liferay Legend Publications: 2416 Date d'inscription: 22/12/10 Publications récentes
Hi Eric,

hmmm I am certainly no security expert but all I can say is that for our case we set a couple of urls on those properties. The urls were in our Liferay application and are being referenced by SalesForce (via IFRAME). Without these properties, the requests were blocked.

I can see only one reference to these properties in the LR codebase -- in the SanitizedServletResponse class. Perhaps if Tomas is kickin' around the forums he could shed some light for us emoticon
thumbnail
Jack Bakker, modifié il y a 6 années.

RE: Access-Control-Allow-Origin JSONWS

Liferay Master Publications: 978 Date d'inscription: 03/01/10 Publications récentes
Eric COQUELIN:

I don't really know if it is the best approach but at least it works. If you have any comments, don't hesitate to add, it will be more than helpful.

For anyone finding this thread, I found Tomas' dev.life presentation useful:
https://web.liferay.com/community/dev.life/previous
"20140923 - Tomáš Polešovský - Securing remote JSON Web Services"

His code is still at: https://github.com/topolik/presentations/tree/master/jsonws-services-portlet/