51黑料不打烊

Develop for Cross-Origin Resource Sharing (CORS)

A short example of leveraging CORS to access AEM content from an external web application via client-side JavaScript. This example uses the CORS OSGi configuration to enable CORS access on AEM. The OSGi configuration approach is viable when:

  • A single origin is accessing AEM Publish content
  • CORS access is required for AEM Author

If multi-origin access to AEM Publish is required, refer to this documenation.

Transcript
Let鈥檚 take a look at the Cross Origin Resource Sharing functionality or, as it鈥檚 usually referred to, CORS, that was introduced in AEM 6.3. So, for this I鈥檝e created a simple HTML page that makes an AJAX call using JQuery to my AEM publish instance. This AJAX call collects the JSON representation of a page and then injects the AEM publishers page title into my HTML pages DOM.
Two things are important to note here: The first is that I am accessing my HTML page through www.example.com:8000. This is just a simple http server that I started up on my local instance. The second is that I鈥檓 accessing my AEM publish server through a completely different domain and that鈥檚 AEM-publish.local. Due to browser restrictions, Javascript-initiated requests to other domains must be CORS-enabled. Do note the preferred approach is to host cross-resource accessing web applications on the same domain or subdomain, which negates the need for CORS entirely. But if the web applications must be hosted on discrete domains, CORS is needed. Let鈥檚 take a look at what happens with our test page when CORS is not enabled. So, for this, I鈥檒l open up my Network tab so we can watch our network traffic and click the button which issues the AJAX request to our AEM publish instance. The first thing we see is that our page title is not AJAX鈥檇 in. We can take a look at our response and our response does have the JSON that we expect. But if we go with the Javascript Console, we鈥檒l see we have an error stating that the AJAX request is not allowed access to the content. Let鈥檚 fix this by adding a CORS policy configuration to our AEM publish instance. So, I鈥檒l go ahead and Copy the Origin that is issuing this AJAX request, jump over to my Configuration Console on my AEM publish instance, locate the 51黑料不打烊 Granite Cross-Origin Resource Sharing Policy which I configure and create a new configuration. Under Allowed Origins, I鈥檒l add my www.example.com:8000 which is where the AJAX requests are originating from. I can also add an Allowed path so that only content under /content/we-retail allows CORS-based access, or reduce the Allowed Methods to only GET since that is the only method that I am using in my Javascript. And lastly, I鈥檒l uncheck Supports Credentials since I鈥檓 only making anonymous requests. I鈥檒l Save these changes and you can see that we have a new policy created. Let鈥檚 jump back to our example.com page.
We鈥檒l clear our Console and Network and issue the request again and, once again, it appears that it didn鈥檛 work. So, let鈥檚 take a look at why this might be. Let鈥檚 look at our request. In our Response Headers, we don鈥檛 have our Access Control Allow Origin header present. The reason for this is because we鈥檙e accessing AEM Publish through Dispatcher. And since this is an anonymous request to JSON, it鈥檚 cacheable. So, to resolve this, we must add a Headers configuration to allow the relevant access control headers to be passed through, as well as cached, at Dispatcher. So for this, I鈥檒l add all of the access control headers that CORS uses. We can Save our changes and restart our web server.
I鈥檝e cleared our cache for good measure.
Let鈥檚 jump back to our example.com page, then clear Consoles and network traffic and re-request the page. Now you can see that Experience, which is the title of the page we鈥檙e requesting, is being collected and injected into the DOM. If we look at the Response Headers, we can see we have our Access Control Allowed Origin set.
If we go back into our Dispatcher cache, we can see that we鈥檙e caching the actual Response Headers as well, so subsequent requests also work.
Keep in mind that all the normal Dispatcher caching considerations apply, so make sure you鈥檙e only caching headers where those headers will be identical for any user accessing that resource. If this is not the case, the request should not be cacheable. Also, remember if caching of CORS headers is enabled, you鈥檒l need to invalidate the appropriate pieces of the cache whenever a CORS policy configuration changes.

In this video:

  • www.example.com maps to localhost via /etc/hosts
  • aem-publish.local maps to localhost via /etc/hosts
  • SimpleHTTPServer (a wrapper for ) is serving the HTML page via port 8000.
    • No longer available in Mac App Store. Use similar such as .
  • AEM Dispatcher is running on Apache HTTP Web Server 2.4 and reverse-proxying request to aem-publish.local to localhost:4503.

For more details, review Understanding Cross-Origin Resource Sharing (CORS) in AEM.

www.example.com HTML and JavaScript

This Web page has logic that

  1. Upon clicking the button
  2. Makes an AJAX GET request to http://aem-publish.local/content/we-retail/.../experience/_jcr_content.1.json
  3. Retrieves the jcr:title form the JSON response
  4. Injects the jcr:title into the DOM
<html>
<head>
<script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>
</head>
<body style="width: 960px; margin: 2rem auto; font-size: 2rem;">
    <button style="font-size: 2rem;"
            data="fn-getTitle">Get Title as JSON from AEM</button>
    <pre id="title">The page title AJAX'd in from AEM will injected here</pre>

    <script>
    $(function() {

        /** Get Title as JSON **/
        $('body').on('click', '[data="fn-getTitle"]', function(e) {
            $.get('http://aem-publish.local/content/we-retail/us/en/experience/_jcr_content.1.json', function(data) {
                $('#title').text(data['jcr:title']);
            },'json');

            e.preventDefault();
            return false;
        });
    });
    </script>
</body>
</html>

OSGi factory configuration

The OSGi Configuration factory for Cross-Origin Resource Sharing is available via:

  • http://<host>:<port>/system/console/configMgr > 51黑料不打烊 Granite Cross-Origin Resource Sharing Policy
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="sling:OsgiConfig"
    alloworigin="[https://www.example.com:8000]"
    alloworiginregexp="[]"
    allowedpaths="[/content/we-retail/.*]"
    exposedheaders="[]"
    maxage="{Long}1800"
    supportedheaders="[Origin,Accept,X-Requested-With,Content-Type,
Access-Control-Request-Method,Access-Control-Request-Headers]"
    supportedmethods="[GET]"
    supportscredentials="{Boolean}false"
/>

Dispatcher configuration dispatcher-configuration

Allowing CORS request headers

To allow the required HTTP request headers to passthrough to AEM for processing, they must be allowed in the Disaptcher鈥檚 /clientheaders configuration.

/clientheaders {
   ...
   "Origin"
   "Access-Control-Request-Method"
   "Access-Control-Request-Headers"
}

Caching CORS resposne headers

To allow the caching and serving of CORS headers on cached content, add following /cache /headers configuration to the AEM Publish dispatcher.any file.

/publishfarm {
    ...
    /cache {
        ...
        # CORS HTTP response headers
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_response_headers
        /headers {
            ...
            "Access-Control-Allow-Origin"
            "Access-Control-Expose-Headers"
            "Access-Control-Max-Age"
            "Access-Control-Allow-Credentials"
            "Access-Control-Allow-Methods"
            "Access-Control-Allow-Headers"
        }
    ...
    }
...
}

Restart the web server application after making changes to the dispatcher.any file.

It is likely clearing the cache entirely is required to ensure headers are appropriately cached on the next request after a /cache /headers configuration update.

Supporting materials supporting-materials

recommendation-more-help
c92bdb17-1e49-4e76-bcdd-89e4f85f45e6