Permission-sensitive caching enables you to cache secured pages. Dispatcher checks user’s access permissions for a page before delivering the cached page.
Dispatcher includes the AuthChecker module that implements permission-sensitive caching. When the module is activated, the Dispatcher calls an AEM servlet to perform user authentication and authorization for the requested content. The servlet response determines whether the content is delivered to the web browser from the cache or not.
Because the methods of authentication and authorization are specific to the AEM deployment, you are required to create the servlet.
Use deny
filters to enforce blanket security restrictions. Use permission-sensitive caching for pages that are configured to permit access to a subset of users or groups.
The following diagrams illustrate the order of events that occur when a web browser requests a page for which permission-sensitive caching is used.
To implement permission-sensitive caching, perform the following tasks:
Typically, secure resources are stored in a separate folder than unsecure files. For example, /content/secure/
When there is a CDN (or any other cache) in front of the dispatcher then you should set the caching headers accordingly so that the CDN does not cache the private content. For example: Header always set Cache-Control private
.
For AEM as a Cloud Service see the Caching page for more details on how to set private caching headers.
Create and deploy a servlet that performs the authentication and authorization of the user who requests the web content. The servlet can use any authentication and authorization method, such as the AEM user account and repository ACLs, or an LDAP lookup service. You deploy the servlet to the AEM instance that Dispatcher uses as the render.
The servlet must be accessible to all users. Therefore, your servlet should extend the org.apache.sling.api.servlets.SlingSafeMethodsServlet
class, which provides read-only access to the system.
The servlet recieves only HEAD requests from the render, so you only need to implement the doHead
method.
The render includes the URI of the requested resource as a parameter of the HTTP request. For example, an authorization servlet is accessed via /bin/permissioncheck
. To perform a security check on the /content/geometrixx-outdoors/en.html page, the render includes the following URL in the HTTP request:
/bin/permissioncheck?uri=/content/geometrixx-outdoors/en.html
The servlet response message must contain the following HTTP status codes:
The following example servlet obtains the URL of the requested resource from the HTTP request. The code uses the Felix SCR Property
annotation to set the value of the sling.servlet.paths
property to /bin/permissioncheck. In the doHead
method, the servlet obtains the session object and uses the checkPermission
method to determine the appropriate response code.
The value of the sling.servlet.paths property must be enabled in the Sling Servlet Resolver (org.apache.sling.servlets.resolver.SlingServletResolver) service.
package com.adobe.example;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.apache.felix.scr.annotations.Property;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Session;
@Component(metatype=false)
@Service
public class AuthcheckerServlet extends SlingSafeMethodsServlet {
@Property(value="/bin/permissioncheck")
static final String SERVLET_PATH="sling.servlet.paths";
private Logger logger = LoggerFactory.getLogger(this.getClass());
public void doHead(SlingHttpServletRequest request, SlingHttpServletResponse response) {
try{
//retrieve the requested URL
String uri = request.getParameter("uri");
//obtain the session from the request
Session session = request.getResourceResolver().adaptTo(javax.jcr.Session.class);
//perform the permissions check
try {
session.checkPermission(uri, Session.ACTION_READ);
logger.info("authchecker says OK");
response.setStatus(SlingHttpServletResponse.SC_OK);
} catch(Exception e) {
logger.info("authchecker says READ access DENIED!");
response.setStatus(SlingHttpServletResponse.SC_FORBIDDEN);
}
}catch(Exception e){
logger.error("authchecker servlet exception: " + e.getMessage());
}
}
}
If your requirements permit the caching of authenticated documents, set the /allowAuthorized property under the /cache section to /allowAuthorized 1
. See Caching When Authentication is Used for more details.
The auth_checker section of the dispatcher.any file controls the behavior of permission-sensitive caching. The auth_checker section includes the following subsections:
url
: The value of the sling.servlet.paths
property of the servlet that performs the security check.
filter
: Filters that specify the folders to which permission-sensitive caching is applied. Typically, a deny
filter is applied to all folders, and allow
filters are applied to secured folders.
headers
: Specifies the HTTP headers that the authorization servlet includes in the response.
When Dispatcher starts, the Dispatcher log file includes the following debug-level message:
AuthChecker: initialized with URL 'configured_url'.
The following example auth_checker section configures Dispatcher to use the servlet of the previous topic. The filter section causes permission checks to be performed only on secure HTML resources.
/auth_checker
{
# request is sent to this URL with '?uri=<page>' appended
/url "/bin/permissioncheck"
# only the requested pages matching the filter section below are checked,
# all other pages get delivered unchecked
/filter
{
/0000
{
/glob "*"
/type "deny"
}
/0001
{
/glob "/content/secure/*.html"
/type "allow"
}
}
# any header line returned from the auth_checker's HEAD request matching
# the section below will be returned as well
/headers
{
/0000
{
/glob "*"
/type "deny"
}
/0001
{
/glob "Set-Cookie:*"
/type "allow"
}
}
}