This is an automated email from the ASF dual-hosted git repository.

fanjia pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/seatunnel.git


The following commit(s) were added to refs/heads/dev by this push:
     new 19871a7314 [Feature][Zeta] Support basic authentication for web ui 
(#9171)
19871a7314 is described below

commit 19871a7314db802f43606e2eaa1f99606b0cc4c6
Author: CosmosNi <[email protected]>
AuthorDate: Thu Apr 17 13:38:21 2025 +0800

    [Feature][Zeta] Support basic authentication for web ui (#9171)
---
 config/seatunnel.yaml                              |   4 +
 docs/en/seatunnel-engine/security.md               |  21 ++
 docs/zh/seatunnel-engine/security.md               |  21 ++
 .../engine/e2e/BasicAuthenticationIT.java          | 266 +++++++++++++++++++++
 .../src/test/resources/basic-auth}/seatunnel.yaml  |  36 +--
 .../config/YamlSeaTunnelDomConfigProcessor.java    |  12 +
 .../engine/common/config/server/HttpConfig.java    |  12 +
 .../common/config/server/ServerConfigOptions.java  |  18 ++
 .../seatunnel/engine/server/JettyService.java      |  14 +-
 .../engine/server/rest/filter/BasicAuthFilter.java | 105 ++++++++
 10 files changed, 489 insertions(+), 20 deletions(-)

diff --git a/config/seatunnel.yaml b/config/seatunnel.yaml
index 79a713a71e..6f6c5ef4ce 100644
--- a/config/seatunnel.yaml
+++ b/config/seatunnel.yaml
@@ -44,3 +44,7 @@ seatunnel:
       enable-http: true
       port: 8080
       enable-dynamic-port: false
+      # Uncomment the following lines to enable basic authentication for web UI
+      # enable-basic-auth: true
+      # basic-auth-username: admin
+      # basic-auth-password: admin
diff --git a/docs/en/seatunnel-engine/security.md 
b/docs/en/seatunnel-engine/security.md
index 363f1b9200..b3c1b4257c 100644
--- a/docs/en/seatunnel-engine/security.md
+++ b/docs/en/seatunnel-engine/security.md
@@ -1,5 +1,26 @@
 # Security
 
+## Basic Authentication
+
+You can secure your Web UI by enabling basic authentication. This will require 
users to enter a username and password when accessing the web interface.
+
+| Parameter Name | Required | Description |
+|----------------|----------|-------------|
+| `enable-basic-auth` | No | Whether to enable basic authentication, default 
is `false` |
+| `basic-auth-username` | No | The username for basic authentication, default 
is `admin` |
+| `basic-auth-password` | No | The password for basic authentication, default 
is `admin` |
+
+```yaml
+seatunnel:
+  engine:
+    http:
+      enable-http: true
+      port: 8080
+      enable-basic-auth: true
+      basic-auth-username: "your_username"
+      basic-auth-password: "your_password"
+```
+
 ## HTTPS Configuration
 
 You can secure your REST-API-V2 service by enabling HTTPS. Both HTTP and HTTPS 
can be enabled simultaneously, or only one of them can be enabled.
diff --git a/docs/zh/seatunnel-engine/security.md 
b/docs/zh/seatunnel-engine/security.md
index d4d228880e..565771fd1d 100644
--- a/docs/zh/seatunnel-engine/security.md
+++ b/docs/zh/seatunnel-engine/security.md
@@ -4,6 +4,27 @@ sidebar_position: 16
 
 # Security
 
+## Basic 认证
+
+您可以通过开启 Basic 认证来保护您的 Web UI。这将要求用户在访问 Web 界面时输入用户名和密码。
+
+| 参数名称 | 是否必填 | 参数描述 |
+|--------|---------|--------|
+| `enable-basic-auth` | 否 | 是否开启Basic 认证,默认为 `false` |
+| `basic-auth-username` | 否 | Basic 认证的用户名,默认为 `admin` |
+| `basic-auth-password` | 否 | Basic 认证的密码,默认为 `admin` |
+
+```yaml
+seatunnel:
+  engine:
+    http:
+      enable-http: true
+      port: 8080
+      enable-basic-auth: true
+      basic-auth-username: "your_username"
+      basic-auth-password: "your_password"
+```
+
 ## HTTPS 配置
 
 您可以通过开启 HTTPS 来保护您的 API 服务。HTTP 和 HTTPS 可同时开启,也可以只开启其中一个。
diff --git 
a/seatunnel-e2e/seatunnel-engine-e2e/connector-seatunnel-e2e-base/src/test/java/org/apache/seatunnel/engine/e2e/BasicAuthenticationIT.java
 
b/seatunnel-e2e/seatunnel-engine-e2e/connector-seatunnel-e2e-base/src/test/java/org/apache/seatunnel/engine/e2e/BasicAuthenticationIT.java
new file mode 100644
index 0000000000..4c0402f5cb
--- /dev/null
+++ 
b/seatunnel-e2e/seatunnel-engine-e2e/connector-seatunnel-e2e-base/src/test/java/org/apache/seatunnel/engine/e2e/BasicAuthenticationIT.java
@@ -0,0 +1,266 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.engine.e2e;
+
+import org.apache.seatunnel.engine.server.rest.RestConstant;
+
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.containers.GenericContainer;
+
+import io.restassured.http.ContentType;
+import io.restassured.response.Response;
+
+import java.io.IOException;
+import java.util.Base64;
+import java.util.concurrent.TimeUnit;
+
+import static io.restassured.RestAssured.given;
+import static 
org.apache.seatunnel.e2e.common.util.ContainerUtil.PROJECT_ROOT_PATH;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.notNullValue;
+
+/** Integration test for basic authentication in SeaTunnel Engine. */
+public class BasicAuthenticationIT extends SeaTunnelEngineContainer {
+
+    private static final String HTTP = "http://";;
+    private static final String COLON = ":";
+    private static final String USERNAME = "testuser";
+    private static final String PASSWORD = "testpassword";
+    private static final String BASIC_AUTH_HEADER = "Authorization";
+    private static final String BASIC_AUTH_PREFIX = "Basic ";
+
+    @Override
+    @BeforeEach
+    public void startUp() throws Exception {
+        // Create server with basic authentication enabled
+
+        server = createSeaTunnelContainerWithBasicAuth();
+        // Wait for server to be ready
+        Awaitility.await()
+                .atMost(2, TimeUnit.MINUTES)
+                .until(
+                        () -> {
+                            try {
+                                // Try to access with correct credentials
+                                String credentials = USERNAME + ":" + PASSWORD;
+                                String encodedCredentials =
+                                        
Base64.getEncoder().encodeToString(credentials.getBytes());
+
+                                given().header(
+                                                BASIC_AUTH_HEADER,
+                                                BASIC_AUTH_PREFIX + 
encodedCredentials)
+                                        .get(
+                                                HTTP
+                                                        + server.getHost()
+                                                        + COLON
+                                                        + 
server.getMappedPort(8080)
+                                                        + "/")
+                                        .then()
+                                        .statusCode(200);
+                                return true;
+                            } catch (Exception e) {
+                                return false;
+                            }
+                        });
+    }
+
+    @Override
+    @AfterEach
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Test that accessing the web UI without authentication credentials 
returns 401 Unauthorized.
+     */
+    @Test
+    public void testAccessWithoutCredentials() {
+        given().get(HTTP + server.getHost() + COLON + 
server.getMappedPort(8080) + "/")
+                .then()
+                .statusCode(401);
+    }
+
+    /** Test that accessing the web UI with incorrect credentials returns 401 
Unauthorized. */
+    @Test
+    public void testAccessWithIncorrectCredentials() {
+        String credentials = "wronguser:wrongpassword";
+        String encodedCredentials = 
Base64.getEncoder().encodeToString(credentials.getBytes());
+
+        given().header(BASIC_AUTH_HEADER, BASIC_AUTH_PREFIX + 
encodedCredentials)
+                .get(HTTP + server.getHost() + COLON + 
server.getMappedPort(8080) + "/")
+                .then()
+                .statusCode(401);
+    }
+
+    /** Test that accessing the web UI with correct credentials returns 200 
OK. */
+    @Test
+    public void testAccessWithCorrectCredentials() {
+        String credentials = USERNAME + ":" + PASSWORD;
+        String encodedCredentials = 
Base64.getEncoder().encodeToString(credentials.getBytes());
+
+        given().header(BASIC_AUTH_HEADER, BASIC_AUTH_PREFIX + 
encodedCredentials)
+                .get(HTTP + server.getHost() + COLON + 
server.getMappedPort(8080) + "/")
+                .then()
+                .statusCode(200)
+                .contentType(containsString("text/html"))
+                .body(containsString("<title>Seatunnel Engine UI</title>"));
+    }
+
+    /** Test that accessing the REST API with correct credentials returns 200 
OK. */
+    @Test
+    public void testRestApiAccessWithCorrectCredentials() {
+        String credentials = USERNAME + ":" + PASSWORD;
+        String encodedCredentials = 
Base64.getEncoder().encodeToString(credentials.getBytes());
+
+        given().header(BASIC_AUTH_HEADER, BASIC_AUTH_PREFIX + 
encodedCredentials)
+                .get(
+                        HTTP
+                                + server.getHost()
+                                + COLON
+                                + server.getMappedPort(8080)
+                                + RestConstant.REST_URL_OVERVIEW)
+                .then()
+                .statusCode(200)
+                .body("projectVersion", notNullValue());
+    }
+
+    /** Test that accessing the REST API with Incorrect credentials returns 
200 OK. */
+    @Test
+    public void testRestApiAccessWithIncorrectCredentials() {
+        String credentials = "wronguser:wrongpassword";
+        String encodedCredentials = 
Base64.getEncoder().encodeToString(credentials.getBytes());
+
+        given().header(BASIC_AUTH_HEADER, BASIC_AUTH_PREFIX + 
encodedCredentials)
+                .get(
+                        HTTP
+                                + server.getHost()
+                                + COLON
+                                + server.getMappedPort(8080)
+                                + RestConstant.REST_URL_OVERVIEW)
+                .then()
+                .statusCode(401);
+    }
+
+    /** Test submitting a job via REST API with correct credentials. */
+    @Test
+    public void testSubmitJobWithCorrectCredentials() {
+        String credentials = USERNAME + ":" + PASSWORD;
+        String encodedCredentials = 
Base64.getEncoder().encodeToString(credentials.getBytes());
+
+        // Simple batch job configuration
+        String jobConfig =
+                "{\n"
+                        + "    \"env\": {\n"
+                        + "        \"job.mode\": \"batch\"\n"
+                        + "    },\n"
+                        + "    \"source\": [\n"
+                        + "        {\n"
+                        + "            \"plugin_name\": \"FakeSource\",\n"
+                        + "            \"plugin_output\": \"fake\",\n"
+                        + "            \"row.num\": 100,\n"
+                        + "            \"schema\": {\n"
+                        + "                \"fields\": {\n"
+                        + "                    \"name\": \"string\",\n"
+                        + "                    \"age\": \"int\",\n"
+                        + "                    \"card\": \"int\"\n"
+                        + "                }\n"
+                        + "            }\n"
+                        + "        }\n"
+                        + "    ],\n"
+                        + "    \"transform\": [\n"
+                        + "    ],\n"
+                        + "    \"sink\": [\n"
+                        + "        {\n"
+                        + "            \"plugin_name\": \"InMemory\",\n"
+                        + "            \"plugin_input\": \"fake\",\n"
+                        + "            \"throw_exception\": true\n"
+                        + "        }\n"
+                        + "    ]\n"
+                        + "}";
+
+        Response response =
+                given().header(BASIC_AUTH_HEADER, BASIC_AUTH_PREFIX + 
encodedCredentials)
+                        .contentType(ContentType.JSON)
+                        .body(jobConfig)
+                        .post(
+                                HTTP
+                                        + server.getHost()
+                                        + COLON
+                                        + server.getMappedPort(8080)
+                                        + RestConstant.REST_URL_SUBMIT_JOB);
+
+        response.then().statusCode(200).body("jobId", notNullValue());
+    }
+
+    /** Test submitting a job via REST API with incorrect credentials. */
+    @Test
+    public void testSubmitJobWithIncorrectCredentials() {
+        String credentials = "wronguser:wrongpassword";
+        String encodedCredentials = 
Base64.getEncoder().encodeToString(credentials.getBytes());
+
+        // Simple batch job configuration
+        String jobConfig =
+                "{\n"
+                        + "  \"env\": {\n"
+                        + "    \"job.mode\": \"BATCH\"\n"
+                        + "  },\n"
+                        + "  \"source\": {\n"
+                        + "    \"FakeSource\": {\n"
+                        + "      \"plugin_output\": \"fake\",\n"
+                        + "      \"row.num\": 100,\n"
+                        + "      \"schema\": {\n"
+                        + "        \"fields\": {\n"
+                        + "          \"id\": \"int\",\n"
+                        + "          \"name\": \"string\"\n"
+                        + "        }\n"
+                        + "      }\n"
+                        + "    }\n"
+                        + "  },\n"
+                        + "  \"sink\": {\n"
+                        + "    \"Console\": {\n"
+                        + "      \"plugin_input\": \"fake\"\n"
+                        + "    }\n"
+                        + "  }\n"
+                        + "}";
+
+        given().header(BASIC_AUTH_HEADER, BASIC_AUTH_PREFIX + 
encodedCredentials)
+                .contentType(ContentType.JSON)
+                .body(jobConfig)
+                .post(
+                        HTTP
+                                + server.getHost()
+                                + COLON
+                                + server.getMappedPort(8080)
+                                + RestConstant.REST_URL_SUBMIT_JOB)
+                .then()
+                .statusCode(401);
+    }
+
+    /** Create a SeaTunnel container with basic authentication enabled. */
+    private GenericContainer<?> createSeaTunnelContainerWithBasicAuth()
+            throws IOException, InterruptedException {
+        String configPath =
+                PROJECT_ROOT_PATH
+                        + 
"/seatunnel-e2e/seatunnel-engine-e2e/connector-seatunnel-e2e-base/src/test/resources/basic-auth/seatunnel.yaml";
+
+        return 
createSeaTunnelContainerWithFakeSourceAndInMemorySink(configPath);
+    }
+}
diff --git a/config/seatunnel.yaml 
b/seatunnel-e2e/seatunnel-engine-e2e/connector-seatunnel-e2e-base/src/test/resources/basic-auth/seatunnel.yaml
similarity index 64%
copy from config/seatunnel.yaml
copy to 
seatunnel-e2e/seatunnel-engine-e2e/connector-seatunnel-e2e-base/src/test/resources/basic-auth/seatunnel.yaml
index 79a713a71e..2f27e3e19f 100644
--- a/config/seatunnel.yaml
+++ 
b/seatunnel-e2e/seatunnel-engine-e2e/connector-seatunnel-e2e-base/src/test/resources/basic-auth/seatunnel.yaml
@@ -17,30 +17,30 @@
 
 seatunnel:
   engine:
-    classloader-cache-mode: true
-    history-job-expire-minutes: 1440
-    backup-count: 1
+    history-job-expire-minutes: 1
+    backup-count: 2
     queue-type: blockingqueue
-    print-execution-info-interval: 60
-    print-job-metrics-info-interval: 60
+    print-execution-info-interval: 10
+    classloader-cache-mode: false
     slot-service:
       dynamic-slot: true
     checkpoint:
-      interval: 10000
-      timeout: 60000
+      interval: 300000
+      timeout: 100000
       storage:
-        type: hdfs
+        type: localfile
         max-retained: 3
         plugin-config:
-          namespace: /tmp/seatunnel/checkpoint_snapshot
-          storage.type: hdfs
-          fs.defaultFS: file:///tmp/ # Ensure that the directory has written 
permission
+          namespace: /tmp/seatunnel/checkpoint_snapshot/
+    http:
+        enable-http: true
+        port: 8080
+        enable-dynamic-port: false
+        enable-basic-auth: true
+        basic-auth-username: "testuser"
+        basic-auth-password: "testpassword"
     telemetry:
       metric:
-        enabled: false
-      log:
-        scheduled-deletion-enable: true
-    http:
-      enable-http: true
-      port: 8080
-      enable-dynamic-port: false
+         enabled: false
+      logs:
+         scheduled-deletion-enable: false
diff --git 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/YamlSeaTunnelDomConfigProcessor.java
 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/YamlSeaTunnelDomConfigProcessor.java
index c54b2305de..f4aec7dfc3 100644
--- 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/YamlSeaTunnelDomConfigProcessor.java
+++ 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/YamlSeaTunnelDomConfigProcessor.java
@@ -549,6 +549,18 @@ public class YamlSeaTunnelDomConfigProcessor extends 
AbstractDomConfigProcessor
                     .key()
                     .equals(name)) {
                 httpConfig.setTrustStorePassword(getTextContent(node));
+            } else if 
(ServerConfigOptions.MasterServerConfigOptions.ENABLE_BASIC_AUTH
+                    .key()
+                    .equals(name)) {
+                
httpConfig.setEnableBasicAuth(getBooleanValue(getTextContent(node)));
+            } else if 
(ServerConfigOptions.MasterServerConfigOptions.BASIC_AUTH_USERNAME
+                    .key()
+                    .equals(name)) {
+                httpConfig.setBasicAuthUsername(getTextContent(node));
+            } else if 
(ServerConfigOptions.MasterServerConfigOptions.BASIC_AUTH_PASSWORD
+                    .key()
+                    .equals(name)) {
+                httpConfig.setBasicAuthPassword(getTextContent(node));
             } else {
                 LOGGER.warning("Unrecognized element: " + name);
             }
diff --git 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/HttpConfig.java
 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/HttpConfig.java
index ba7c8e8492..fe7324bf31 100644
--- 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/HttpConfig.java
+++ 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/HttpConfig.java
@@ -66,6 +66,18 @@ public class HttpConfig implements Serializable {
 
     private int portRange = 
ServerConfigOptions.MasterServerConfigOptions.PORT_RANGE.defaultValue();
 
+    /** Whether to enable basic authentication. */
+    private boolean enableBasicAuth =
+            
ServerConfigOptions.MasterServerConfigOptions.ENABLE_BASIC_AUTH.defaultValue();
+
+    /** The username for basic authentication. */
+    private String basicAuthUsername =
+            
ServerConfigOptions.MasterServerConfigOptions.BASIC_AUTH_USERNAME.defaultValue();
+
+    /** The password for basic authentication. */
+    private String basicAuthPassword =
+            
ServerConfigOptions.MasterServerConfigOptions.BASIC_AUTH_PASSWORD.defaultValue();
+
     public void setPort(int port) {
         checkPositive(port, ServerConfigOptions.MasterServerConfigOptions.HTTP 
+ " must be > 0");
         this.port = port;
diff --git 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/ServerConfigOptions.java
 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/ServerConfigOptions.java
index cc7fb3503a..9deb98f29b 100644
--- 
a/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/ServerConfigOptions.java
+++ 
b/seatunnel-engine/seatunnel-engine-common/src/main/java/org/apache/seatunnel/engine/common/config/server/ServerConfigOptions.java
@@ -255,6 +255,24 @@ public class ServerConfigOptions {
                         .withDescription(
                                 "The port range of the http server. If 
enable-dynamic-port is true, We will use the unused port in the range");
 
+        public static final Option<Boolean> ENABLE_BASIC_AUTH =
+                Options.key("enable-basic-auth")
+                        .booleanType()
+                        .defaultValue(false)
+                        .withDescription("Whether to enable basic 
authentication for the web UI.");
+
+        public static final Option<String> BASIC_AUTH_USERNAME =
+                Options.key("basic-auth-username")
+                        .stringType()
+                        .defaultValue("admin")
+                        .withDescription("The username for basic 
authentication.");
+
+        public static final Option<String> BASIC_AUTH_PASSWORD =
+                Options.key("basic-auth-password")
+                        .stringType()
+                        .defaultValue("admin")
+                        .withDescription("The password for basic 
authentication.");
+
         public static final Option<HttpConfig> HTTP =
                 Options.key("http")
                         .type(new TypeReference<HttpConfig>() {})
diff --git 
a/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/JettyService.java
 
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/JettyService.java
index 7f88c25662..1d98f25843 100644
--- 
a/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/JettyService.java
+++ 
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/JettyService.java
@@ -27,6 +27,7 @@ import 
org.apache.seatunnel.shade.org.eclipse.jetty.util.ssl.SslContextFactory;
 
 import org.apache.seatunnel.engine.common.config.SeaTunnelConfig;
 import org.apache.seatunnel.engine.common.config.server.HttpConfig;
+import org.apache.seatunnel.engine.server.rest.filter.BasicAuthFilter;
 import org.apache.seatunnel.engine.server.rest.filter.ExceptionHandlingFilter;
 import org.apache.seatunnel.engine.server.rest.servlet.AllLogNameServlet;
 import org.apache.seatunnel.engine.server.rest.servlet.AllNodeLogServlet;
@@ -149,8 +150,17 @@ public class JettyService {
         ServletContextHandler context = new 
ServletContextHandler(ServletContextHandler.SESSIONS);
         
context.setContextPath(seaTunnelConfig.getEngineConfig().getHttpConfig().getContextPath());
 
-        FilterHolder filterHolder = new FilterHolder(new 
ExceptionHandlingFilter());
-        context.addFilter(filterHolder, "/*", 
EnumSet.of(DispatcherType.REQUEST));
+        // Add exception handling filter
+        FilterHolder exceptionFilterHolder = new FilterHolder(new 
ExceptionHandlingFilter());
+        context.addFilter(exceptionFilterHolder, "/*", 
EnumSet.of(DispatcherType.REQUEST));
+
+        // Add basic authentication filter if enabled
+        HttpConfig httpConfig = 
seaTunnelConfig.getEngineConfig().getHttpConfig();
+        if (httpConfig.isEnableBasicAuth()) {
+            log.info("Basic authentication is enabled for web UI");
+            FilterHolder basicAuthFilterHolder = new FilterHolder(new 
BasicAuthFilter(httpConfig));
+            context.addFilter(basicAuthFilterHolder, "/*", 
EnumSet.of(DispatcherType.REQUEST));
+        }
 
         ServletHolder defaultServlet = new ServletHolder("default", 
DefaultServlet.class);
         URL uiResource = JettyService.class.getClassLoader().getResource("ui");
diff --git 
a/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/filter/BasicAuthFilter.java
 
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/filter/BasicAuthFilter.java
new file mode 100644
index 0000000000..373b41babb
--- /dev/null
+++ 
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/filter/BasicAuthFilter.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.engine.server.rest.filter;
+
+import org.apache.seatunnel.engine.common.config.server.HttpConfig;
+
+import org.apache.commons.codec.binary.Base64;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/** Basic authentication filter for the web UI. */
+@Slf4j
+public class BasicAuthFilter implements Filter {
+
+    private final HttpConfig httpConfig;
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    private static final String BASIC_PREFIX = "Basic ";
+    private static final String WWW_AUTHENTICATE_HEADER = "WWW-Authenticate";
+    private static final String BASIC_REALM = "Basic realm=\"SeaTunnel Web 
UI\"";
+
+    public BasicAuthFilter(HttpConfig httpConfig) {
+        this.httpConfig = httpConfig;
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        // No initialization needed
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain)
+            throws IOException, ServletException {
+
+        // Skip authentication if not enabled
+        if (!httpConfig.isEnableBasicAuth()) {
+            chain.doFilter(request, response);
+            return;
+        }
+
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        HttpServletResponse httpResponse = (HttpServletResponse) response;
+
+        // Get the Authorization header from the request
+        String authHeader = httpRequest.getHeader(AUTHORIZATION_HEADER);
+
+        // Check if the Authorization header exists and starts with "Basic "
+        if (authHeader != null && authHeader.startsWith(BASIC_PREFIX)) {
+            // Extract the Base64 encoded username:password
+            String base64Credentials = 
authHeader.substring(BASIC_PREFIX.length());
+            String credentials =
+                    new String(Base64.decodeBase64(base64Credentials), 
StandardCharsets.UTF_8);
+
+            // Split the username and password
+            final String[] values = credentials.split(":", 2);
+            if (values.length == 2) {
+                String username = values[0];
+                String password = values[1];
+
+                // Check if the username and password match the configured 
values
+                if (username.equals(httpConfig.getBasicAuthUsername())
+                        && password.equals(httpConfig.getBasicAuthPassword())) 
{
+                    // Authentication successful, proceed with the request
+                    chain.doFilter(request, response);
+                    return;
+                }
+            }
+        }
+
+        // Authentication failed, send 401 Unauthorized response
+        httpResponse.setHeader(WWW_AUTHENTICATE_HEADER, BASIC_REALM);
+        httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, 
"Unauthorized");
+    }
+
+    @Override
+    public void destroy() {
+        // No resources to release
+    }
+}

Reply via email to