教程:使用防病毒软件扫描文件附件

[AEM 表单]{class="badge positive" title="适用于AEM Forms"}

文件附件病毒扫描程序/验证程序功能在早期采用程序程序下。 您可以使用官方电子邮件 ID 写信给 aem-forms-ea@adobe.com,加入早期采用者计划并申请使用该功能。

本教程将带您一个接一个地学习。 您将运行ClamAV防病毒引擎,向AEM添加一个自定义验证器,使用file-upload字段创建一个示例自适应表单,连接验证器,并确认感染病毒的文件在提交时已被拒绝。 即使您是AEM的新用户,也会按照此说明进行操作。 您需要的每个命令、屏幕路径和值都会拼写出来。

有关验证器和接口引用背后的概念,请参阅随附文章使用自定义验证器在Adaptive Forms中扫描文件附件。 完成本教程不需要先阅读它,但它解释了为什么每一条都存在。

您将构建什么 what-you-will-build

到最后,您将拥有:

  • 正在运行的ClamAV守护程序(clamd),可根据请求扫描文件。
  • 自定义AEM服务(ClamAV扫描程序),用于将每个上传的文件发送到clamd并拒绝受感染的文件。
  • 名为​ 文件附件扫描程序演示 ​的自适应表单示例,带有文件上传字段和提交按钮。
  • 正在运行的测试:已成功提交普通文件,并且已阻止已知的测试病毒文件。

开始之前:假设和先决条件 assumptions

本教程假定采用以下设置。 如果缺少任何项目,请在启动之前安装或获取它。 每个项目都包括如何对其进行检查。

#
假设
如何验证
注释
1
本地AEM创作实例正在运行,可在http://localhost:4502上访问
在浏览器中打开http://localhost:4502。 您应该会看到AEM登录或“开始”屏幕。
本教程使用本地AEM as a Cloud Service SDK快速入门。
2
您可以以管理员身份登录到AEM
使用管理员帐户在http://localhost:4502登录(默认的本地凭据为admin / admin)
部署代码、更改OSGi配置和创作表单需要管理员权限。
3
您的AEM环境有权使用抢先访问文件附件验证器功能,并启用了功能切换FT_FORMS-23497
与率先采用者计划联系人确认
此切换闸门底层FileAttachmentValidatorManager功能。 提交选项卡字段本身就是本教程在步骤7中添加的内容。 无论哪种方式,它都不是现成可用的。
4
您在项目的ui.apps模块中拥有或可以创建自适应表单容器​代理组件
请参阅步骤7
这是扩展AEM核心组件的标准模式。 步骤7在其上创建对话框扩展。
5
Java JDK已安装,并且与您的AEM SDK所需的版本匹配
在终端中运行java -version
使用AEM SDK所需的Java版本(对于当前SDK,这是Java 11或Java 21)。
6
已安装Apache Maven 3.x
运行 mvn -version
用于生成和部署自定义代码。
7
已安装Docker(运行ClamAV的推荐路径)
运行 docker --version
如果无法使用Docker,请参阅步骤1中的本机安装说明。
8
您拥有或可以创建用于保存自定义代码的AEM Maven项目
请参阅步骤2
步骤2创建一个(如果您没有该步骤)。
9
您已下载AEM Forms加载项SDK的​最新可用内部版本,该内部版本捆绑了提供com.adobe.forms.common.service界面的早期访问依赖项
从Adobe软件分发门户下载aem-forms-addon-sdk-<version>.zip(需要早期采用者计划权利)
必需。 此依赖项未发布到公共Maven存储库;它是从SDK下载中提取的。 早期版本可能不包含此内部版本。 请参阅步骤3。

要替换的值 substitute-values

无论在何处看到这些占位符,都请将其替换为您自己的值:

  • <PROJECT_ROOT>:您的AEM Maven项目的文件夹。
  • <APP_ID>:项目的应用程序ID或捆绑包模块名称(例如,mysite)。
  • <SDK_DEPENDENCY_VERSION>:在您下载的SDK中捆绑的adobe-xfaforms-common版本(组id和工件id已修复;请参阅步骤3)。
NOTE
显示了用于macOS和Linux的所有终端命令。 在Windows上,在PowerShell或WSL中运行它们。 Docker和Maven命令相同。

路线图 roadmap

您可以按顺序完成这些步骤:

  1. 启动ClamAV后台程序(clamd)。
  2. 设置一个AEM Maven项目(如果您已经有一个项目,则跳过)。
  3. 添加早期访问依赖关系。
  4. 添加ClamAV验证器类。
  5. 构建并部署到AEM。
  6. 在AEM中配置clamd连接。
  7. 将验证器字段添加到表单对话框。
  8. 创建自适应表单示例。
  9. 将ClamAV扫描仪连接到窗体。
  10. 使用干净的文件和测试病毒文件进行测试。

步骤1:启动ClamAV守护程序 step-1-clamd

ClamAV作为名为clamd的后台服务运行。 您构建的验证器将每个上载的文件流式传输到clamd,后者会回复OK以获取干净的文件,或者在检测到威胁名称时报告威胁名称。

运行clamd的最快方法是Docker。

  1. 启动ClamAV:

    code language-none
    docker run -d --name clamav -p 3310:3310 clamav/clamav:latest
    
    note
    NOTE
    在Apple Silicon (arm64) Mac上,此图像没有本机arm64内部版本,并且上述命令因no matching manifest for linux/arm64/v8而失败。 添加--platform linux/amd64以改为在模拟下运行:
    code language-none
    docker run -d --name clamav --platform linux/amd64 -p 3310:3310 clamav/clamav:latest
    
  2. 等待它准备就绪。 第一次启动时,它会下载病毒数据库,这可能需要几分钟的时间。 在数据库已加载且clamd正在侦听之前,请观察日志:

    code language-none
    docker logs -f clamav
    

    按Ctrl+C可在日志准备就绪后停止关注日志。

  3. 验证是否可以在端口3310上访问clamd:

    code language-none
    printf 'PING\n' | nc localhost 3310
    

    预期结果:​响应为PONG。

NOTE
如果无法使用Docker,请从clamav.net本机安装ClamAV,在clamd.conf中的端口3310上启用clamd守护程序和TCP套接字,使用freshclam更新数据库,然后启动clamd。 然后,在上面的步骤3中运行验证。
IMPORTANT
clamd作为自己的进程运行,独立于AEM。 此本地设置用于开发。 在AEM as a Cloud Service上,您无法在AEM主机上运行clamd,因此在实际环境中,您要将它作为并存服务或外部服务运行,并将验证器指向它。 希望在同一区域有一个位于同一位置的实例,以将文件内容保留在您的环境中,以实现数据驻留和法规遵从性。

步骤2:设置AEM Maven项目 step-2-project

自定义Java代码将作为Maven项目构建的捆绑包部署到AEM。 如果您已有AEM项目,请跳至步骤3并使用其捆绑包模块(通常名为core)。

如果您没有项目原型,请使用AEM项目原型创建一个项目:

  1. 在终端中,转到保存代码的文件夹并运行原型。 将<APP_ID>替换为短小写名称,如mysite:

    code language-none
    mvn -B org.apache.maven.plugins:maven-archetype-plugin:generate -D archetypeGroupId=com.adobe.aem -D archetypeArtifactId=aem-project-archetype -D archetypeVersion=LATEST -D appId=<APP_ID> -D appTitle="<APP_ID>" -D name="<APP_ID>" -D groupId=com.example -D artifactId=<APP_ID> -D aemVersion=cloud
    

    预期结果:​包含包括core、ui.apps和all的模块的新文件夹<APP_ID> (这是您的<PROJECT_ROOT>)。

  2. 您稍后添加的Java代码将放入core模块中的:

    code language-none
    <PROJECT_ROOT>/core/src/main/java/
    
NOTE
使用当前原型版本。 请参阅AEM项目原型文档。 原型会生成许多文件,但在本教程中,您只编辑core模块。

步骤3:添加早期访问依赖关系 step-3-dependency

您的代码针对来自com.adobe.forms.foundation:adobe-xfaforms-common的FileAttachmentValidator接口进行编译(假设#9)。 此依赖项未发布到公共Maven存储库。 它捆绑在AEM Forms附加组件SDK中,因此您可以从其中提取它并将其安装到本地Maven存储库中。

  1. 从Adobe软件分发门户下载aem-forms-addon-sdk-<version>.zip的​最新可用内部版本(需要早期采用者计划权利)。 始终使用最新版本;以前的版本可能尚未包含此依赖关系。

  2. 提取依赖项jar。 SDK zip包含功能存档(.far),并且该jar捆绑在其中:

    code language-none
    unzip -p aem-forms-addon-sdk-<version>.zip aem-forms-addon-<version>.far > addon.far
    unzip -l addon.far | grep adobe-xfaforms-common
    

    第二个命令显示下载中捆绑的准确jar路径和版本,例如com/adobe/forms/foundation/adobe-xfaforms-common/<version>/adobe-xfaforms-common-<version>.jar。 在下一步中使用该路径和版本。

    code language-none
    unzip -p addon.far "com/adobe/forms/foundation/adobe-xfaforms-common/<version>/adobe-xfaforms-common-<version>.jar" > adobe-xfaforms-common.jar
    
  3. 将提取的jar安装到本地Maven存储库中,以便pom.xml可以解析它。 包括-DgeneratePom=true: jar嵌入了其内部Adobe生成POM,其中包含您的项目无法解析的父引用,并且此标记将其替换为干净的自包含标记:

    code language-none
    mvn install:install-file -Dfile=adobe-xfaforms-common.jar -DgroupId=com.adobe.forms.foundation -DartifactId=adobe-xfaforms-common -Dversion=<version> -Dpackaging=jar -DgeneratePom=true
    
  4. 打开 <PROJECT_ROOT>/core/pom.xml。

  5. 在<dependencies>部分中,使用您刚刚安装的相同版本添加依赖项。 使用provided作用域,因为该接口由AEM在运行时提供:

    code language-xml
    <dependency>
        <groupId>com.adobe.forms.foundation</groupId>
        <artifactId>adobe-xfaforms-common</artifactId>
        <version><SDK_DEPENDENCY_VERSION></version>
        <scope>provided</scope>
    </dependency>
    
  6. 保存文件。

IMPORTANT
如果您尚未获得软件分发门户访问权限,请向您的Adobe联系人请求早期采用者计划权利。 如果没有此依赖关系,项目将无法编译。

步骤4:添加ClamAV验证器类 step-4-class

  1. 在以下位置创建新文件:

    code language-none
    <PROJECT_ROOT>/core/src/main/java/com/example/forms/security/ClamAVFileAttachmentValidator.java
    

    创建forms/security文件夹(如果不存在)。 您可以使用自己的包名称。 如果是,请更改package行以匹配。

  2. 粘贴以下代码:

    code language-java
    package com.example.forms.security;
    
    import com.adobe.forms.common.service.FileAttachmentValidator;
    import com.adobe.forms.common.service.FileAttachmentValidationResult;
    import com.adobe.forms.common.service.FileAttachmentWrapper;
    import org.osgi.service.component.annotations.Activate;
    import org.osgi.service.component.annotations.Component;
    import org.osgi.service.metatype.annotations.AttributeDefinition;
    import org.osgi.service.metatype.annotations.Designate;
    import org.osgi.service.metatype.annotations.ObjectClassDefinition;
    
    import java.io.ByteArrayOutputStream;
    import java.io.DataOutputStream;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.InetSocketAddress;
    import java.net.Socket;
    import java.nio.charset.StandardCharsets;
    
    @Component(service = FileAttachmentValidator.class)
    @Designate(ocd = ClamAVFileAttachmentValidator.Config.class)
    public class ClamAVFileAttachmentValidator implements FileAttachmentValidator {
    
        @ObjectClassDefinition(name = "ClamAV File Attachment Scanner")
        public @interface Config {
            @AttributeDefinition(name = "clamd Host")
            String clamd_host() default "localhost";
    
            @AttributeDefinition(name = "clamd Port")
            int clamd_port() default 3310;
    
            @AttributeDefinition(name = "Scan Timeout (ms)")
            int clamd_timeout() default 30000;
        }
    
        private static final String VALIDATOR_NAME = "ClamAV Scanner";
        private static final int CHUNK_SIZE = 8192;
    
        private String host;
        private int port;
        private int timeout;
    
        @Activate
        protected void activate(Config config) {
            this.host = config.clamd_host();
            this.port = config.clamd_port();
            this.timeout = config.clamd_timeout();
        }
    
        @Override
        public FileAttachmentValidationResult validateFileAttachment(FileAttachmentWrapper wrapper) {
    
            if (wrapper == null) {
                return new FileAttachmentValidationResult(false, "No attachment was received.", wrapper);
            }
    
            byte[] content = wrapper.getValue();
            if (content == null || content.length == 0) {
                return new FileAttachmentValidationResult(false, "The attached file is empty.", wrapper);
            }
    
            try {
                String response = scan(content);
    
                if (response.endsWith("OK")) {
                    return new FileAttachmentValidationResult(true, "File passed the virus scan.", wrapper);
                }
                if (response.contains("FOUND")) {
                    return new FileAttachmentValidationResult(false,
                            "A virus was detected in the attached file. Upload was rejected.", wrapper);
                }
                return new FileAttachmentValidationResult(false,
                        "The file could not be scanned. Please try again later.", wrapper);
    
            } catch (Exception e) {
                // Fail closed: reject when clamd is unreachable rather than accept an unscanned file.
                return new FileAttachmentValidationResult(false,
                        "The virus scanner is unavailable. Please try again later.", wrapper);
            }
        }
    
        /**
         * Streams the file to clamd using the INSTREAM command and returns the daemon's response.
         */
        private String scan(byte[] data) throws Exception {
            try (Socket socket = new Socket()) {
                socket.connect(new InetSocketAddress(host, port), timeout);
                socket.setSoTimeout(timeout);
    
                try (OutputStream raw = socket.getOutputStream();
                     DataOutputStream out = new DataOutputStream(raw);
                     InputStream in = socket.getInputStream()) {
    
                    out.writeBytes("zINSTREAM\0");
    
                    for (int offset = 0; offset < data.length; offset += CHUNK_SIZE) {
                        int len = Math.min(CHUNK_SIZE, data.length - offset);
                        out.writeInt(len);
                        out.write(data, offset, len);
                    }
    
                    out.writeInt(0); // zero-length chunk signals end of stream
                    out.flush();
    
                    ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
                    byte[] buffer = new byte[512];
                    int read;
                    while ((read = in.read(buffer)) != -1) {
                        responseBuffer.write(buffer, 0, read);
                    }
                    return responseBuffer.toString(StandardCharsets.US_ASCII.name()).trim();
                }
            }
        }
    
        @Override
        public String getFileAttachmentValidatorName() {
            return VALIDATOR_NAME;
        }
    }
    
  3. 保存文件。

步骤5:构建并部署到AEM step-5-deploy

  1. 运行AEM(假设#1)后,从项目根目录进行生成和部署。 此命令将捆绑包安装到本地创作实例上:

    code language-none
    cd <PROJECT_ROOT>
    mvn clean install -PautoInstallBundle
    

    预期结果:​生成以BUILD SUCCESS结尾。

  2. 确认服务正在运行。 打开组件控制台:

    code language-none
    http://localhost:4502/system/console/components
    

    此页面没有内置搜索框:列出了5,000多个组件,请使用浏览器自己的页面查找功能(Cmd+F或Ctrl+F)并搜索ClamAVFileAttachmentValidator。

    预期结果:​组件已列出,其状态为​活动 (或​已满足)。 如果不满意,请参阅故障排除。

    组件控制台将ClamAVFileAttachmentValidator和FileAttachmentValidatorDataSourceServlet显示为活动

步骤6:配置clad连接 step-6-config

告知验证器clamd的位置。 对于本地开发,默认值(localhost:3310)已经与步骤1匹配,因此仅当值不同时才需要此步骤。 执行一次以确认设置存在。

  1. 打开配置控制台:

    code language-none
    http://localhost:4502/system/console/configMgr
    
  2. 此页面也没有内置搜索框:使用浏览器的​ ClamAV文件附件扫描程序 ​的页面内查找(Cmd+F或Ctrl+F)并打开它。

  3. 确认或设置:

    table 0-row-2 1-row-2 2-row-2 3-row-2
    设置 本教程的值
    Clamd主机 localhost
    固网端口 3310
    扫描超时(毫秒) 30000

    OSGi配置控制台中的 ClamAV文件附件扫描仪配置对话框

  4. 选择​保存。

NOTE
对于实际环境,请将这些值部署为项目中的存储库OSGi配置(每个环境一个.cfg.json文件),以便它们随每个部署一起移动,而不是手动设置。

步骤7:将验证器字段添加到表单对话框 step-7-dialog-field

现成的自适应表单容器对话框中不存在​ 文件附件病毒扫描程序/验证器 ​字段。 对于基于核心组件的表单,可在自己的项目中添加一次。 这是一个一次性步骤;如果以后添加更多验证器,则不要重复此步骤。

  1. 如果您的formcontainer代理组件不存在,请在以下位置创建它:

    code language-none
    <PROJECT_ROOT>/ui.apps/src/main/content/jcr_root/apps/<APP_ID>/components/adaptiveForm/formcontainer/.content.xml
    
    code language-xml
    <?xml version="1.0" encoding="UTF-8"?>
    <jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
        jcr:primaryType="cq:Component"
        jcr:title="Form Container"
        sling:resourceSuperType="core/fd/components/form/container/v2/container"/>
    

    如果您已经拥有此组件(大多数项目都是从核心组件原型创建的),请跳至下一步。

  2. 在下列位置创建一个对话框扩展:

    code language-none
    <PROJECT_ROOT>/ui.apps/src/main/content/jcr_root/apps/<APP_ID>/components/adaptiveForm/formcontainer/_cq_dialog/.content.xml
    
    code language-xml
    <?xml version="1.0" encoding="UTF-8"?>
    <jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
        jcr:primaryType="nt:unstructured">
      <content jcr:primaryType="nt:unstructured">
        <items jcr:primaryType="nt:unstructured">
          <tabs jcr:primaryType="nt:unstructured">
            <items jcr:primaryType="nt:unstructured">
              <submitActions jcr:primaryType="nt:unstructured">
                <items jcr:primaryType="nt:unstructured">
                  <columns jcr:primaryType="nt:unstructured">
                    <items jcr:primaryType="nt:unstructured">
                      <fileAttachmentValidator
                          jcr:primaryType="nt:unstructured"
                          sling:resourceType="granite/ui/components/coral/foundation/form/select"
                          fieldLabel="File Attachment Virus Scanner/Validator"
                          fieldDescription="Select a registered validator configuration to scan submitted file attachments. Select None to disable validation for this form."
                          emptyText="None"
                          name="./fileAttachmentValidator">
                        <datasource
                            jcr:primaryType="nt:unstructured"
                            sling:resourceType="<APP_ID>/datasources/fileattachmentvalidators"/>
                      </fileAttachmentValidator>
                    </items>
                  </columns>
                </items>
              </submitActions>
            </items>
          </tabs>
        </items>
      </content>
    </jcr:root>
    

    这仅定义一个新字段,将真实对话框的节点名称镜像到插入点。 它不会重新定义或替换任何现有的提交选项卡字段。 请参阅配套文章中的将验证器字段添加到表单对话框,以了解其工作原理。

  3. 添加列出已注册验证器的数据源servlet。 无论您使用哪个验证器引擎,该字段都是相同的;请参阅将验证器字段添加到伴随文章中的表单对话框,以了解其功能和原因。 创建时间:

    code language-none
    <PROJECT_ROOT>/core/src/main/java/com/example/forms/security/FileAttachmentValidatorDataSourceServlet.java
    
    code language-java
    package com.example.forms.security;
    
    import com.adobe.forms.common.service.FileAttachmentValidator;
    import com.adobe.forms.common.service.FileAttachmentValidatorManager;
    import com.adobe.granite.ui.components.ds.DataSource;
    import com.adobe.granite.ui.components.ds.SimpleDataSource;
    import com.adobe.granite.ui.components.ds.ValueMapResource;
    import org.apache.sling.api.SlingHttpServletRequest;
    import org.apache.sling.api.SlingHttpServletResponse;
    import org.apache.sling.api.resource.Resource;
    import org.apache.sling.api.resource.ResourceMetadata;
    import org.apache.sling.api.servlets.HttpConstants;
    import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
    import org.apache.sling.api.wrappers.ValueMapDecorator;
    import org.apache.sling.servlets.annotations.SlingServletResourceTypes;
    import org.osgi.service.component.annotations.Component;
    import org.osgi.service.component.annotations.Reference;
    import org.osgi.service.component.annotations.ReferenceCardinality;
    import org.osgi.service.component.annotations.ReferencePolicy;
    
    import javax.servlet.Servlet;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    @Component(service = { Servlet.class })
    @SlingServletResourceTypes(
            resourceTypes = "<APP_ID>/datasources/fileattachmentvalidators",
            methods = HttpConstants.METHOD_GET)
    public class FileAttachmentValidatorDataSourceServlet extends SlingSafeMethodsServlet {
    
        @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC)
        private volatile FileAttachmentValidatorManager fileAttachmentValidatorManager;
    
        @Override
        protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) {
            List<Resource> options = new ArrayList<>();
    
            FileAttachmentValidatorManager manager = fileAttachmentValidatorManager;
            if (manager != null) {
                List<FileAttachmentValidator> validators = manager.getValidators();
                if (validators != null) {
                    for (FileAttachmentValidator validator : validators) {
                        String name = validator.getFileAttachmentValidatorName();
                        if (name != null && !name.isEmpty()) {
                            options.add(createOption(request, name));
                        }
                    }
                }
            }
    
            DataSource dataSource = new SimpleDataSource(options.iterator());
            request.setAttribute(DataSource.class.getName(), dataSource);
        }
    
        private Resource createOption(SlingHttpServletRequest request, String name) {
            Map<String, Object> props = new HashMap<>();
            props.put("value", name);
            props.put("text", name);
            return new ValueMapResource(request.getResourceResolver(), new ResourceMetadata(),
                    "nt:unstructured", new ValueMapDecorator(props));
        }
    }
    

    将对话框XML和Servlet的resourceTypes中的<APP_ID>替换为项目的实际应用程序ID(匹配假设#4),如果包名称与步骤4不同,请调整包名称。

  4. 生成和部署这两个更改。 您更改了内容(对话框)和代码(servlet),因此同时使用这两个配置文件:仅autoInstallPackage部署内容并保留servlet的捆绑包未重新部署。

    code language-none
    cd <PROJECT_ROOT>
    mvn clean install -PautoInstallPackage,autoInstallBundle
    

    预期结果:​生成以BUILD SUCCESS结尾,并且同时安装了ui.apps内容包(包括此对话框更改)和core捆绑包(包括新的servlet)。

NOTE
随着时间的推移,您可能会注册多个验证器,例如每个防病毒引擎注册一个,或者注册同一引擎的多个配置不同的实例。 无需更改此处的任何内容:每个注册的FileAttachmentValidator都会自动显示在下拉列表中,因为每个表单都有自己的getFileAttachmentValidatorName(),并且emptyText="None"将保留“无验证器”作为默认值,以便在您明确选择一个现有表单之前,该表单不会受到影响。

步骤8:创建自适应表单示例 step-8-form

现在,创建一个包含file-upload字段的简单表单。

  1. 打开Forms和文档:

    code language-none
    http://localhost:4502/aem/forms.html/content/dam/formsanddocuments
    
  2. 选择​创建(右上方),然后选择​自适应表单。

  3. 从图库中选择模板。 基础不是单独的问题:它是您选择的模板的一部分,在每个模板的名称(例如,自适应表单(核心组件))下显示为小字幕。

    note important
    IMPORTANT
    多个模板全名为​空白表单:一个用于核心组件,一个用于基础组件,一个用于Edge Delivery Services。 选错一个很容易出错,并且会以静默方式失败:本教程的其余部分似乎仍然有效,但从未调用文件附件病毒扫描程序/验证器字段,因为该管道仅存在于基于核心组件的表单中。 在继续之前,请确认子标题是否为​自适应表单(核心组件)。
  4. 在​ 属性 ​中,设置:

    • 标题: File Attachment Scanner Demo
    • 主题:​选择任何可用的主题(例如,画布)

    然后选择​创建。

  5. 在确认对话框中,选择​ 打开 ​以在编辑器中打开表单。

    预期结果:​自适应表单编辑器打开时表单为空。

  6. 添加file-upload字段:

    • 打开​ 组件 ​浏览器(左边栏中的组件图标,或者选择空表单区域并选择“插入组件”选项)。
    • 找到​ 文件附件 ​组件并将其拖动到表单上。
  7. 添加提交按钮:

    • 从同一组件浏览器中,将​ 自适应表单按钮 ​拖动到表单上“文件”字段下方。
    • 选择按钮,打开其属性(扳手图标),将其​ 按钮类型 ​设置为​提交,然后进行确认。
  8. 保存表单。 编辑器会自动保存,但您可以通过退出编辑器来强制进行保存。

    预期结果:​表单现在具有文件附件字段和提交按钮。

步骤9:将ClamAV扫描仪连接到窗体 step-9-connect

  1. 在表单编辑器中,选择​指南容器 (最外部的容器)以打开​ 自适应表单容器 ​属性。 使用属性(扳手)图标。

  2. 打开​ 提交 ​选项卡。

  3. 找到​ 文件附件病毒扫描程序/验证器 ​下拉列表,然后选择​ClamAV扫描程序。

    此字段及其选项列表来自步骤7:已部署到您自己的项目的对话框扩展和数据源servlet。 条目是您的代码中由getFileAttachmentValidatorName()返回的名称。 如果您更改了VALIDATOR_NAME,请改为选择该名称。

    note important
    IMPORTANT
    如果此字段完全缺失,则说明您尚未完成步骤7,或者未部署步骤7中的ui.apps包。 对于基于核心组件的表单,此字段绝不会现成显示。
  4. 选择​完成,然后保存表单。

在“文件附件病毒扫描程序/验证器”字段中选择ClamAV扫描程序的“自适应表单容器”对话框的 提交选项卡

步骤10:测试集成 step-10-test

测试这两个结果。

测试A:接受干净的文件 test-clean

  1. 打开表单的预览:

    code language-none
    http://localhost:4502/content/dam/formsanddocuments/file-attachment-scanner-demo/jcr:content?wcmmode=disabled
    

    如果表单的路径不同,请从“Forms和文档”中打开它,然后选择​预览。

  2. 附加普通PDF或图像,然后选择​提交。

    预期结果:​已成功提交表单。

测试B:已阻止测试病毒 test-virus

  1. 创建一个名为eicar.txt的纯文本文件,其唯一内容是标准EICAR防病毒测试字符串。 这是每个防病毒引擎都会主动检测的无害文件:

    code language-none
    X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
    

    您也可以从eicar.org下载它。

  2. 在表单中附加eicar.txt并选择​提交。

    预期结果:​提交被阻止。 拒绝的显示方式因表单配置的​ 提交操作 ​而异:标准的感谢页面流程通常在文件字段上显示内联错误,而​提交到REST端点(本教程的示例表单使用此项)倾向于显示通用的提交失败消息,而不是字段级消息。 无论如何,表单都不会提交。

  3. 确认验证器实际运行并拒绝了该文件。 查看AEM错误日志,无论故障是如何在浏览器中显示的,该日志都是查看所发生情况的可靠方法:

    code language-none
    <AEM_SDK_FOLDER>/crx-quickstart/logs/error.log
    

    查找来自验证器的拒绝消息(例如,A virus was detected in the attached file)。

疑难解答 troubleshooting

docker run失败,错误为no matching manifest for linux/arm64/v8。 您使用的是Apple Silicon Mac;clamav/clamav图像没有本机arm64内部版本。 将--platform linux/amd64添加到步骤1中的命令。

PONG未在步骤1.​中返回 clamd未就绪或端口未发布。 检查docker logs clamav是否完成数据库加载,并确认容器映射端口3310 (docker ps)。

步骤2的原型命令因Unsupported class file major version...而失败。 您的默认java/mvn正在比允许使用的假设更新#5JDK上运行:原型的生成后脚本无法解析来自21以上的Java版本的类文件。 将JAVA_HOME指向Java 11或21安装,然后重新运行该命令。 如果第一次尝试已部分生成项目(模块文件夹不匹配,缺少文件),请在重试之前删除该输出目录,而不是就地重新运行。

项目未编译(找不到FileAttachmentValidator)。 早期访问依赖项(步骤3)缺失、未安装到本地Maven存储库或pom.xml的版本与安装的jar不匹配。 使用unzip -l addon.far | grep adobe-xfaforms-common重新检查版本。

步骤5.中未满足​组件 打开http://localhost:4502/system/console/components,找到ClamAVFileAttachmentValidator,然后阅读原因。 缺少接口通常意味着运行时不存在依赖关系。 确认您的AEM环境具有抢先访问功能。

文件附件病毒扫描程序/验证程序字段完全不显示在“提交”选项卡中(步骤9)。 确认实际部署了步骤7 ui.apps包(对话框扩展和数据源servlet)。 检查http://localhost:4502/system/console/components以了解FileAttachmentValidatorDataSourceServlet。 此外,请确认已为您的程序启用FT_FORMS-23497功能切换;它可审核数据源依赖的基础FileAttachmentValidatorManager功能。

出现该字段,但ClamAV扫描仪不在下拉列表中(步骤9)。 确认ClamAVFileAttachmentValidator组件处于活动状态,getFileAttachmentValidatorName()返回唯一的非空值,并在部署后重新加载表单编辑器。

每个文件都已被拒绝,即使干净的文件也是如此。 验证器无法关闭,因此这通常意味着它无法访问clamd。 重新检查步骤1(clamd是否正在运行并返回PONG?) 和步骤6(主机、端口、超时)。 在error.log中查找连接错误或ERROR响应。

扫描超时或INSTREAM size limit exceeded. 文件太大,超时或clamd的StreamMaxLength无法处理。 在步骤6中增加超时,在clamd.conf中提高StreamMaxLength,并在文件附件组件中设置最大文件大小,以便过大的文件提前停止。

EICAR提交显示一般错误,而不是内联字段消息(步骤10)。 提交到REST终结点​提交操作应具有此属性;它不会更改文件是否实际被拒绝。 检查error.log验证器的拒绝消息以进行确认。

常见问题解答 faq

AEM Forms能否使用ClamAV扫描上载?
是的。您通过服务实施FileAttachmentValidator接口,该服务在提交时将每个上载的文件流式传输到ClamAV守护程序(clamd),如果ClamAV报告有威胁,则拒绝该文件。

是否需要在AEM服务器上安装ClamAV?
否。clamd作为单独的进程运行。您的验证器通过TCP与其连接。在本地运行它以进行开发,并在实际环境中作为合用位置或外部服务。

Clamd使用哪个端口?
默认情况下,TCP端口3310。示例验证器使用此端口,并允许您通过OSGi配置更改此端口。

如何测试病毒扫描的工作方式?
提交普通文件以确认接受,然后提交EICAR测试文件(一个无害的行业标准测试字符串)以确认检测到并阻止它。

为什么我的所有文件都被拒绝?
样本验证器无法关闭,因此clamd的连接问题导致每个文件都被拒绝。确认clamd正在运行且可访问,并检查主机、端口和超时设置。

我可以将ClamAV与AEM as a Cloud Service结合使用吗?
是,但无法在AEM主机上运行clamd。将其作为共处位置或外部服务运行,并将验证器指向该服务,最好是在同一区域,以便在您的环境中保留文件内容。

文件一附上,我能否在提交之前运行检查?
是的。本教程的验证器在提交时运行。要在用户附加文件后立即进行验证,请在提交之前,使用自适应Forms规则编辑器中的​ 调用服务 ​操作在文件字段的更改事件上调用扫描服务。请参阅规则编辑器中的调用服务增强功能。

recommendation-more-help
experience-manager-cloud-service-help-main-toc