教學課程:使用防毒軟體掃描檔案附件

[AEM Forms]{class="badge positive" title="套用至AEM Forms"}

檔案附件病毒掃描程式/驗證程式功能屬於早期採用者程式。 您可以使用官方電子郵件 ID 寫信至 aem-forms-ea@adobe.com,以加入早期採用者計劃並要求存取該功能。

本教學課程將帶您進行端對端教學。 您將執行ClamAV防毒引擎、將自訂驗證器新增至AEM、使用檔案上傳欄位建立範例調適型表單、連線驗證器,並確認在提交時已拒絕感染病毒的檔案。 即使您是AEM的新手,也會寫入以供遵循。 您需要的每個命令、熒幕路徑和值都會標示出來。

如需驗證器和介面參考背後的概念,請參閱隨附文章使用自訂驗證器在Adaptive Forms中掃描檔案附件。 您不需要先閱讀本教學課程就能完成本教學課程,但本教學課程會說明為何每篇文章都存在。

您將要建置的內容 what-you-will-build

到最後,您將擁有:

  • 執行中的ClamAV常駐程式(clamd),可依要求掃描檔案。
  • 自訂AEM服務(ClamAV掃描程式),可將每個上傳的檔案傳送至clamd並拒絕受感染的檔案。
  • 名為​ File Attachment Scanner Demo ​的最適化表單範例,包含檔案上傳欄位和提交按鈕。
  • 有效測試:成功提交普通檔案,且封鎖已知的測試病毒檔案。

開始之前:假設和先決條件 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模組中建立最適化表單容器​Proxy元件
請參閱步驟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介面的Early Access相依性
Adobe軟體發佈入口網站下載aem-forms-addon-sdk-<version>.zip (需要早期採用者程式權益)
必要。 此相依性不會發佈至公用Maven存放庫,而是從SDK下載擷取。 舊版組建可能不包含此功能。 請參閱步驟3。

您將取代的值 substitute-values

無論您在何處看到這些預留位置,都請將其取代為您自己的值:

  • <PROJECT_ROOT>:您AEM Maven專案的資料夾。
  • <APP_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
    

    預期結果:​包含包含coreui.appsall等模組的新資料夾<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-commonFileAttachmentValidator介面進行編譯(假設#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:設定夾式連線 step-6-config

告知驗證器clamd的位置。 對於本機開發,預設值(localhost:3310)已經與步驟1相符,因此只有在您的值不同時才需要此步驟。 執行一次以確認設定存在。

  1. 開啟設定主控台:

    code language-none
    http://localhost:4502/system/console/configMgr
    
  2. 此頁面也沒有內建搜尋方塊:請使用瀏覽器的頁面內尋找(Cmd+F或Ctrl+F)來尋找​ ClamAV檔案附件掃描器 ​並開啟它。

  3. 確認或設定:

    table 0-row-2 1-row-2 2-row-2 3-row-2
    設定 本教學課程的值
    夾式主機 localhost
    固定連線埠 3310
    掃描逾時(毫秒) 30000

    OSGi設定主控台中的 ClamAV檔案附件掃描器設定對話方塊

  4. 選取「儲存」。

NOTE
對於真實環境,請將這些值部署為您的專案中的存放庫OSGi設定(每個環境一個.cfg.json檔案),以便它們隨著每個部署移動,而不是手動設定。

步驟7:將驗證器欄位新增至表單對話方塊 step-7-dialog-field

檔案附件病毒掃描程式/驗證器​欄位不在現成的最適化表單容器對話方塊中。 對於以核心元件為基礎的表單,您可在自己的專案中新增一次。 此為一次性步驟;若稍後新增更多驗證器,請勿重複此步驟。

  1. 如果您的formcontainer Proxy元件尚不存在,請於下列位置建立它:

    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. 新增檔案上傳欄位:

    • 開啟​ 元件 ​瀏覽器(左側邊欄中的元件圖示,或選取空白表單區域並選擇「插入元件」選項)。
    • 尋找​ 檔案附件 ​元件,並將其拖曳至表單上。
  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 & Documents開啟它,並選取​預覽

  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中的命令。

步驟1中未傳回​PONG clamd未就緒或連線埠未發佈。 檢查docker logs clamav的資料庫載入完成,並確認容器對應連線埠3310 (docker ps)。

步驟2的原型命令因Unsupported class file major version...而失敗。 您的預設java/mvn正在較新的JDK上執行,而非#5允許的假設:原型的產生後指令碼無法從較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)已實際部署。 檢查FileAttachmentValidatorDataSourceServlethttp://localhost:4502/system/console/components。 同時確認已為您的程式啟用FT_FORMS-23497功能切換;它會啟動資料來源所依賴的基礎FileAttachmentValidatorManager功能。

欄位已顯示,但ClamAV掃描器不在下拉式清單中(步驟9)。 確認ClamAVFileAttachmentValidator元件作用中,getFileAttachmentValidatorName()傳回唯一的非空白值,並在部署後重新載入表單編輯器。

每個檔案都遭到拒絕,即使是乾淨的檔案也是如此。 驗證器無法關閉,因此這通常表示它無法達到clamd。 重新檢查步驟1 (clamd是否正在執行並傳回PONG?) 和步驟6 (主機、連線埠、逾時)。 檢視error.log中的連線錯誤或ERROR回應。

掃描逾時或INSTREAM size limit exceeded. 檔案太大,逾時或clamdStreamMaxLength無法存取。 在步驟6中增加逾時,在clamd.conf中引發StreamMaxLength,並在檔案附件元件上設定檔案大小上限,如此一來,過大的檔案會提早停止。

EICAR提交會顯示一般錯誤,而非內嵌欄位訊息(步驟10)。 這是​ 提交至REST端點 ​提交動作的預期結果;它不會變更檔案是否實際遭拒。 檢查error.log以確認驗證程式的拒絕訊息。

常見問題 faq

AEM Forms是否可以使用ClamAV掃描上傳?
是。您實作FileAttachmentValidator介面的服務會在提交時將每個上傳的檔案串流至ClamAV精靈(clamd),並在ClamAV回報威脅時拒絕檔案。

我需要在AEM伺服器上安裝ClamAV嗎?
clamd個以個別處理程式執行。您的驗證器會透過TCP連線到它。在本機執行以進行開發,並在實際環境中以共用位置或外部服務的形式執行。

夾住使用哪個連線埠?
依預設,TCP連線埠3310。範例驗證器會使用此連線埠,並讓您透過OSGi設定加以變更。

如何測試病毒掃描的運作方式?
提交普通檔案以確認其被接受,然後提交EICAR測試檔案(無害的產業標準測試字串)以確認其被偵測到並遭到封鎖。

為什麼我的所有檔案都被拒絕?
範例驗證器無法關閉,因此clamd的連線問題會導致每個檔案都遭到拒絕。確認clamd正在執行且可連線,並檢查主機、連線埠和逾時設定。

我可以搭配AEM as a Cloud Service使用ClamAV嗎?
是,但您無法在AEM主機上執行clamd。請將它當成並存服務或外部服務來執行,並將驗證器指向它,最好是在相同的區域,以將檔案內容儲存在您的環境中。

我可以在附加檔案後,立即執行早於提交的檢查嗎?
是。本教學課程的驗證器會在提交時執行。若要在使用者附加檔案後立即進行驗證,在提交之前,請使用Adaptive Forms規則編輯器中的​ 叫用服務 ​操作,在檔案欄位的變更事件上呼叫掃描服務。請參閱規則編輯器中的啟動服務增強功能

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