首页 > 解决方案 > sling servlet 上的发布请求不适用于发布实例

问题描述

昨天,我在使用资源类型属性注册的 AEM 6.3 中编写 sling post servlet 时遇到了问题。实际上我的代码适用于作者实例,但不适用于发布实例。下面的代码使用用户给定的数据在项目的内容路径上创建一个节点和属性。(以下代码中忽略import语句和分号,它是用groovy编写的)

我不确定,通过使用 SlingHttpServletRequest实例并获取会话来解析资源是否是一种好习惯?

而且,我能够找到 session.save() 或 resolver.commit 之间的任何区别。

有人可以帮忙吗?

@SlingServlet(
    resourceTypes = ["app/project/components/formComp"],
    extensions = ['json'],
    methods = "POST")
@Properties([
    @Property(name = "Code ABC", value = 'Project ABC'),
    @Property(name = "Description ABC", value = 'servlet form')])
@CompileStatic
@Slf4j
class PostFormServlet extends SlingAllMethodsServlet {

    ResourceResolver resolver
    Session session

    @Override
    void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) {

        String myNodePath = 'content/project/en/mynode'
        String requestParam = 'param'
        try {
            resolver = request.getResourceResolver()
            session = resolver.adaptTo(Session)
            Node root = session.getRootNode()

            Node myDestinationNode
            if (rootNode.hasNode(myNodePath)) {
                myDestinationNode = rootNode.getNode(myNodePath)
            } else {
                myDestinationNode = rootNode.addNode(myNodePath, NT_UNSTRUCTURED)
            }
            String paramValue = request.getParameter(requestParam)

            if (myDestinationNode) {
                Date date = new Date();
                timeStamp = date.getTime() as String
                Node dateTimeNode = myDestinationNode.addNode(timeStamp, NT_UNSTRUCTURED)
                if (dateTimeNode) {
                    dateTimeNode.setProperty(requestParam, paramValue)
                }
            }
            session.save()
        } catch (Exception ex) {
           //log error
        }


        response.contentType = 'application/json'
        response.getWriter().write("Node Created")
    }
}

标签: javaservletsaemsling

解决方案


如果 POST 请求是作为管理员用户发出的,那么您的 Servlet 可以工作。所以脚本解析工作正常,这是权限问题。

权限检查在几个层次:

权限检查 - 调度程序: 调度程序可能不允许某些路径的 POST 请求,或者需要某种身份验证,或者只是从请求中删除一些查询参数或其他有效负载。但是很容易识别,错误消息是来自 Apache 还是来自 Publisher。否则,您可以通过 Dispatcher 发送 POST 请求一次,然后直接向 Publisher 发送一次。如果结果不同,那么您必须首先照顾 Dispatcher。

权限检查 - Apache Sling 身份验证服务:这主要与作者相关,因为默认情况下发布者非常开放。该服务简单地说,哪些路径可以匿名访问(意味着无需任何身份验证),以及哪些路径用户被转发到登录页面。该服务可以通过 OSGi 进行配置,或者sling.auth.requirements直接在您的 Servlet 中指定一个属性。身份验证服务将读取此类属性,并将其视为自身的配置。如前所述,它主要与作者相关 - 因为默认情况下只有登录页面可以在没有身份验证的情况下访问。

权限检查 - 内容资源:如果您通过 SlingResourceType 注册 Servlet,则脚本解析过程需要对所请求资源的读取权限。在 Publisher 中,/content/...-tree 通常对于匿名者来说是可读的,但不是/app/.... 如果您在路径上注册 Servlet,您可以轻松避免这种情况。对于这样的 servlet,没有 Sling 权限检查(好坏,取决于你想要什么)。


Servlet 的权限

上述权限检查是相关的,即调用了您的 Servlet。但是如果被调用,Servlet 仍然只有调用用户的读写权限。这对于匿名用户来说非常非常少(阅读/内容/...,没有写权限)。

因此,您需要与服务用户打开一个新会话。

public class TestServlet extends SlingAllMethodsServlet {

    @Reference
    private ResourceResolverFactory resolverFactory;

    @Override
    protected void doPost(@Nonnull SlingHttpServletRequest request, @Nonnull SlingHttpServletResponse response) throws ServletException, IOException {

        final Map<String, Object> authenticationInfo = Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, "testservlet");
        try (ResourceResolver resolver = resolverFactory.getServiceResourceResolver(authenticationInfo)) {

            Resource rootRes = resolver.getResource("/content/....");
            resolver.create(rootRes, "test", null);
            resolver.commit();

        } catch (Exception e) {
            response.setContentType("text/plain");
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            e.printStackTrace(response.getWriter());
        }
    }
}

一开始,它会说类似org.apache.sling.api.resource.LoginException: Cannot derive user name for bundle ....

你需要:

  1. 创建一个系统用户(普通用户将无法使用!)
  2. 添加服务用户映射 (OSGi-config)
  3. 授予系统用户读/写权限

请遵循以下说明:

https://helpx.adobe.com/experience-manager/6-3/sites/administering/using/security-service-users.html

或者使用工具来创建服务用户并为他们授予权限。要么你已经使用了,要么我推荐https://github.com/Netcentric/accesscontroltool。映射仍然需要 OSGi 配置。


If the service user is too complicated for a first trial, you can still use the deprecated ResourceResolver resolver = resolverFactory.getAdministrativeResourceResolver(null). It is not secure, and therefore deprecated. You only have to whitelist your bundle via an OSGi config (Apache Sling Login Admin Whitelist - additional bundles)


Final question :

difference between session.save() or resolver.commit

The resolver is the Sling-wrapper around the Jackrabbit-Oak session. So resolver.commit() is calling session.save() automatically (but NOT the other way around).

It is strongly recommended to use the most high-level API you can - and NOT mixing high-level with low-level API's. Exceptions may occur - but not for beginners. (e.g. the PageManager-API is built on top of the Slings Resource-API, which is built on top of the Jackrabbit OAK's Node-API. The difficulty is to know, which API's exist)


推荐阅读