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

yuqi4733 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new e462bb3c0 [#4056] improvement(server-common): Support multiple 
authentication at the same time on the server side (#4160)
e462bb3c0 is described below

commit e462bb3c01376112744163b2f90f77550106b16c
Author: lwyang <[email protected]>
AuthorDate: Wed Jul 31 11:56:37 2024 +0800

    [#4056] improvement(server-common): Support multiple authentication at the 
same time on the server side (#4160)
    
    ### What changes were proposed in this pull request?
    
    Support multiple authentication at the same time on the server side
    
    ### Why are the changes needed?
    
    Fix: #4056
    
    ### Does this PR introduce _any_ user-facing change?
    
    `gravitino.authenticator` support configuring multiple authenticators
    
    ### How was this patch tested?
    
    IT,UT
    
    ---------
    
    Co-authored-by: Qi Yu <[email protected]>
---
 .../test/HadoopUserAuthenticationIT.java           |   3 +-
 .../integration/test/HiveUserAuthenticationIT.java |   3 +-
 .../hive/integration/test/ProxyCatalogHiveIT.java  |   2 +-
 .../integration/test/AuditCatalogMysqlIT.java      |   2 +-
 .../test/CatalogIcebergKerberosHiveIT.java         |   3 +-
 .../test/CatalogPaimonKerberosFilesystemIT.java    |   3 +-
 .../filesystem/hadoop/TestOauth2Client.java        |   7 +-
 .../src/main/java/org/apache/gravitino/Config.java |   2 +
 .../main/java/org/apache/gravitino/Configs.java    |  20 +++-
 .../gravitino/integration/test/MiniGravitino.java  |  21 ++--
 .../integration/test/util/AbstractIT.java          |  27 ++---
 .../gravitino/integration/test/client/AuditIT.java |   2 +-
 .../test/web/rest/KerberosOperationsIT.java        |   2 +-
 ...perationsIT.java => MultiAuthOperationsIT.java} | 111 ++++++++++++++++-----
 .../test/web/rest/OAuth2OperationsIT.java          |   2 +-
 .../authentication/AuthenticationFilter.java       |  34 +++++--
 .../server/authentication/Authenticator.java       |  15 +++
 .../authentication/AuthenticatorFactory.java       |  24 +++--
 .../authentication/KerberosAuthenticator.java      |   7 ++
 .../authentication/OAuth2TokenAuthenticator.java   |   7 ++
 .../server/authentication/ServerAuthenticator.java |  13 ++-
 .../server/authentication/SimpleAuthenticator.java |   7 ++
 .../authentication/TestAuthenticationFilter.java   |  54 +++++++++-
 .../apache/gravitino/server/web/ConfigServlet.java |   7 +-
 web/src/lib/store/auth/index.js                    |   4 +-
 25 files changed, 294 insertions(+), 88 deletions(-)

diff --git 
a/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserAuthenticationIT.java
 
b/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserAuthenticationIT.java
index ba495ef6e..9ca01edd8 100644
--- 
a/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserAuthenticationIT.java
+++ 
b/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserAuthenticationIT.java
@@ -36,6 +36,7 @@ import java.util.HashMap;
 import java.util.Map;
 import org.apache.commons.io.FileUtils;
 import org.apache.gravitino.Catalog;
+import org.apache.gravitino.Configs;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.SchemaChange;
 import org.apache.gravitino.client.GravitinoAdminClient;
@@ -221,7 +222,7 @@ public class HadoopUserAuthenticationIT extends AbstractIT {
   }
 
   private static void addKerberosConfig() {
-    AbstractIT.customConfigs.put("gravitino.authenticator", "kerberos");
+    AbstractIT.customConfigs.put(Configs.AUTHENTICATORS.getKey(), "kerberos");
     AbstractIT.customConfigs.put(
         "gravitino.authenticator.kerberos.principal", 
GRAVITINO_SERVER_PRINCIPAL);
     AbstractIT.customConfigs.put(
diff --git 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/HiveUserAuthenticationIT.java
 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/HiveUserAuthenticationIT.java
index c236c2de2..9aa6d2aa1 100644
--- 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/HiveUserAuthenticationIT.java
+++ 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/HiveUserAuthenticationIT.java
@@ -36,6 +36,7 @@ import java.nio.file.Files;
 import java.util.Map;
 import org.apache.commons.io.FileUtils;
 import org.apache.gravitino.Catalog;
+import org.apache.gravitino.Configs;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.client.GravitinoAdminClient;
 import org.apache.gravitino.client.GravitinoMetalake;
@@ -188,7 +189,7 @@ public class HiveUserAuthenticationIT extends AbstractIT {
   }
 
   private static void addKerberosConfig() {
-    AbstractIT.customConfigs.put("gravitino.authenticator", "kerberos");
+    AbstractIT.customConfigs.put(Configs.AUTHENTICATORS.getKey(), "kerberos");
     AbstractIT.customConfigs.put(
         "gravitino.authenticator.kerberos.principal", 
GRAVITINO_SERVER_PRINCIPAL);
     AbstractIT.customConfigs.put(
diff --git 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/ProxyCatalogHiveIT.java
 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/ProxyCatalogHiveIT.java
index 0538a59c3..0bdb1ad3f 100644
--- 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/ProxyCatalogHiveIT.java
+++ 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/ProxyCatalogHiveIT.java
@@ -94,7 +94,7 @@ public class ProxyCatalogHiveIT extends AbstractIT {
     System.setProperty("user.name", "datastrato");
 
     Map<String, String> configs = Maps.newHashMap();
-    configs.put(Configs.AUTHENTICATOR.getKey(), 
AuthenticatorType.SIMPLE.name().toLowerCase());
+    configs.put(Configs.AUTHENTICATORS.getKey(), 
AuthenticatorType.SIMPLE.name().toLowerCase());
     registerCustomConfigs(configs);
     AbstractIT.startIntegrationTest();
     containerSuite.startHiveContainer();
diff --git 
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/AuditCatalogMysqlIT.java
 
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/AuditCatalogMysqlIT.java
index e5503b0f1..b2cf57147 100644
--- 
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/AuditCatalogMysqlIT.java
+++ 
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/AuditCatalogMysqlIT.java
@@ -64,7 +64,7 @@ public class AuditCatalogMysqlIT extends AbstractIT {
   @BeforeAll
   public static void startIntegrationTest() throws Exception {
     Map<String, String> configs = Maps.newHashMap();
-    configs.put(Configs.AUTHENTICATOR.getKey(), 
AuthenticatorType.SIMPLE.name().toLowerCase());
+    configs.put(Configs.AUTHENTICATORS.getKey(), 
AuthenticatorType.SIMPLE.name().toLowerCase());
     registerCustomConfigs(configs);
     AbstractIT.startIntegrationTest();
 
diff --git 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergKerberosHiveIT.java
 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergKerberosHiveIT.java
index e20cc97f7..ebd8737f5 100644
--- 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergKerberosHiveIT.java
+++ 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergKerberosHiveIT.java
@@ -35,6 +35,7 @@ import java.nio.file.Files;
 import java.util.Map;
 import org.apache.commons.io.FileUtils;
 import org.apache.gravitino.Catalog;
+import org.apache.gravitino.Configs;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.client.GravitinoAdminClient;
 import org.apache.gravitino.client.GravitinoMetalake;
@@ -201,7 +202,7 @@ public class CatalogIcebergKerberosHiveIT extends 
AbstractIT {
   }
 
   private static void addKerberosConfig() {
-    AbstractIT.customConfigs.put("gravitino.authenticator", "kerberos");
+    AbstractIT.customConfigs.put(Configs.AUTHENTICATORS.getKey(), "kerberos");
     AbstractIT.customConfigs.put(
         "gravitino.authenticator.kerberos.principal", 
GRAVITINO_SERVER_PRINCIPAL);
     AbstractIT.customConfigs.put(
diff --git 
a/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/integration/test/CatalogPaimonKerberosFilesystemIT.java
 
b/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/integration/test/CatalogPaimonKerberosFilesystemIT.java
index 009ea39bf..233c8aff7 100644
--- 
a/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/integration/test/CatalogPaimonKerberosFilesystemIT.java
+++ 
b/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/integration/test/CatalogPaimonKerberosFilesystemIT.java
@@ -32,6 +32,7 @@ import java.nio.file.Files;
 import java.util.Map;
 import org.apache.commons.io.FileUtils;
 import org.apache.gravitino.Catalog;
+import org.apache.gravitino.Configs;
 import org.apache.gravitino.NameIdentifier;
 import 
org.apache.gravitino.catalog.lakehouse.paimon.PaimonCatalogPropertiesMetadata;
 import org.apache.gravitino.client.GravitinoAdminClient;
@@ -194,7 +195,7 @@ public class CatalogPaimonKerberosFilesystemIT extends 
AbstractIT {
   }
 
   private static void addKerberosConfig() {
-    AbstractIT.customConfigs.put("gravitino.authenticator", "kerberos");
+    AbstractIT.customConfigs.put(Configs.AUTHENTICATORS.getKey(), "kerberos");
     AbstractIT.customConfigs.put(
         "gravitino.authenticator.kerberos.principal", 
GRAVITINO_SERVER_PRINCIPAL);
     AbstractIT.customConfigs.put(
diff --git 
a/clients/filesystem-hadoop3/src/test/java/org/apache/gravitino/filesystem/hadoop/TestOauth2Client.java
 
b/clients/filesystem-hadoop3/src/test/java/org/apache/gravitino/filesystem/hadoop/TestOauth2Client.java
index 81b9da66e..374d5993d 100644
--- 
a/clients/filesystem-hadoop3/src/test/java/org/apache/gravitino/filesystem/hadoop/TestOauth2Client.java
+++ 
b/clients/filesystem-hadoop3/src/test/java/org/apache/gravitino/filesystem/hadoop/TestOauth2Client.java
@@ -40,6 +40,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import org.apache.gravitino.Config;
+import org.apache.gravitino.Configs;
 import org.apache.gravitino.client.DefaultOAuth2TokenProvider;
 import org.apache.gravitino.client.ErrorHandlers;
 import org.apache.gravitino.client.HTTPClient;
@@ -256,7 +257,7 @@ public class TestOauth2Client extends TestGvfsBase {
     // 3. test expired token
     Config config = new Config(false) {};
     config.set(
-        new 
ConfigBuilder("gravitino.authenticator").stringConf().createWithDefault("simple"),
+        new 
ConfigBuilder(Configs.AUTHENTICATORS.getKey()).stringConf().createWithDefault("simple"),
         "oauth");
     config.set(OAuthConfig.SERVICE_AUDIENCE, "service1");
     config.set(OAuthConfig.DEFAULT_SIGN_KEY, publicKey);
@@ -310,7 +311,9 @@ public class TestOauth2Client extends TestGvfsBase {
                       
header.getValues().get(0).getValue().getBytes(StandardCharsets.UTF_8);
                   // should throw an UnauthorizedException here
                   try {
-                    
authenticator.authenticator().authenticateToken(tokenValue);
+                    authenticator.authenticators().stream()
+                        .filter(i -> i.supportsToken(tokenValue))
+                        .forEach(i -> i.authenticateToken(tokenValue));
                   } catch (UnauthorizedException e) {
                     assertTrue(e.getMessage().contains("JWT parse error"));
                     throw e;
diff --git a/core/src/main/java/org/apache/gravitino/Config.java 
b/core/src/main/java/org/apache/gravitino/Config.java
index 44813a052..de61a44d1 100644
--- a/core/src/main/java/org/apache/gravitino/Config.java
+++ b/core/src/main/java/org/apache/gravitino/Config.java
@@ -53,6 +53,8 @@ public abstract class Config {
         "gravitino.entity.store.kv.deleteAfterTimeMs",
         "0.5.0",
         "Please use gravitino.entity.store.deleteAfterTimeMs instead."),
+    new DeprecatedConfig(
+        "gravitino.authenticator", "0.6.0", "Please use 
gravitino.authenticators instead.")
   };
 
   /**
diff --git a/core/src/main/java/org/apache/gravitino/Configs.java 
b/core/src/main/java/org/apache/gravitino/Configs.java
index 83021922f..4cbfebe01 100644
--- a/core/src/main/java/org/apache/gravitino/Configs.java
+++ b/core/src/main/java/org/apache/gravitino/Configs.java
@@ -199,11 +199,29 @@ public class Configs {
 
   public static final ConfigEntry<String> AUTHENTICATOR =
       new ConfigBuilder("gravitino.authenticator")
-          .doc("The authenticator which Gravitino uses")
+          .doc(
+              "The authenticator which Gravitino uses. Multiple authenticators 
"
+                  + "separated by commas")
           .version(ConfigConstants.VERSION_0_3_0)
+          .deprecated()
           .stringConf()
           .createWithDefault("simple");
 
+  public static final ConfigEntry<List<String>> AUTHENTICATORS =
+      new ConfigBuilder("gravitino.authenticators")
+          .doc(
+              "The authenticators which Gravitino uses. Multiple 
authenticators "
+                  + "separated by commas")
+          .version(ConfigConstants.VERSION_0_6_0)
+          .alternatives(Lists.newArrayList("gravitino.authenticator"))
+          .stringConf()
+          .toSequence()
+          .checkValue(
+              valueList ->
+                  valueList != null && 
valueList.stream().allMatch(StringUtils::isNotBlank),
+              ConfigConstants.NOT_BLANK_ERROR_MSG)
+          .createWithDefault(Lists.newArrayList("simple"));
+
   public static final ConfigEntry<Long> STORE_TRANSACTION_MAX_SKEW_TIME =
       new ConfigBuilder("gravitino.entity.store.maxTransactionSkewTimeMs")
           .doc("The maximum skew time of transactions in milliseconds")
diff --git 
a/integration-test-common/src/test/java/org/apache/gravitino/integration/test/MiniGravitino.java
 
b/integration-test-common/src/test/java/org/apache/gravitino/integration/test/MiniGravitino.java
index 8dcf3bf34..e20681c40 100644
--- 
a/integration-test-common/src/test/java/org/apache/gravitino/integration/test/MiniGravitino.java
+++ 
b/integration-test-common/src/test/java/org/apache/gravitino/integration/test/MiniGravitino.java
@@ -21,12 +21,15 @@ package org.apache.gravitino.integration.test;
 import static 
com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
 import static org.apache.gravitino.Configs.ENTITY_KV_ROCKSDB_BACKEND_PATH;
 
+import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableMap;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.UUID;
@@ -59,6 +62,7 @@ import org.slf4j.LoggerFactory;
 public class MiniGravitino {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(MiniGravitino.class);
+  private static final Splitter COMMA = 
Splitter.on(",").omitEmptyStrings().trimResults();
   private MiniGravitinoContext context;
   private RESTClient restClient;
   private final File mockConfDir;
@@ -116,19 +120,20 @@ public class MiniGravitino {
     this.host = jettyServerConfig.getHost();
     this.port = jettyServerConfig.getHttpPort();
     String URI = String.format("http://%s:%d";, host, port);
-    if (AuthenticatorType.OAUTH
-        .name()
-        .toLowerCase()
-        .equals(context.customConfig.get(Configs.AUTHENTICATOR.getKey()))) {
+
+    List<String> authenticators = new ArrayList<>();
+    String authenticatorStr = 
context.customConfig.get(Configs.AUTHENTICATORS.getKey());
+    if (authenticatorStr != null) {
+      authenticators = COMMA.splitToList(authenticatorStr);
+    }
+
+    if (authenticators.contains(AuthenticatorType.OAUTH.name().toLowerCase())) 
{
       restClient =
           HTTPClient.builder(ImmutableMap.of())
               .uri(URI)
               .withAuthDataProvider(OAuthMockDataProvider.getInstance())
               .build();
-    } else if (AuthenticatorType.KERBEROS
-        .name()
-        .toLowerCase()
-        .equals(context.customConfig.get(Configs.AUTHENTICATOR.getKey()))) {
+    } else if 
(authenticators.contains(AuthenticatorType.KERBEROS.name().toLowerCase())) {
       restClient =
           HTTPClient.builder(ImmutableMap.of())
               .uri(URI)
diff --git 
a/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/AbstractIT.java
 
b/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/AbstractIT.java
index 1eabe7f58..968eeeef8 100644
--- 
a/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/AbstractIT.java
+++ 
b/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/AbstractIT.java
@@ -21,6 +21,7 @@ package org.apache.gravitino.integration.test.util;
 import static org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_PATH;
 import static 
org.apache.gravitino.server.GravitinoServer.WEBSERVER_CONF_PREFIX;
 
+import com.google.common.base.Splitter;
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
@@ -30,8 +31,10 @@ import java.nio.file.Paths;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.Statement;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import org.apache.commons.io.FileUtils;
@@ -63,6 +66,8 @@ public class AbstractIT {
   protected static final ContainerSuite containerSuite = 
ContainerSuite.getInstance();
 
   private static final Logger LOG = LoggerFactory.getLogger(AbstractIT.class);
+  private static final Splitter COMMA = 
Splitter.on(",").omitEmptyStrings().trimResults();
+
   protected static GravitinoAdminClient client;
 
   private static final OAuthMockDataProvider mockDataProvider = 
OAuthMockDataProvider.getInstance();
@@ -256,20 +261,18 @@ public class AbstractIT {
         JettyServerConfig.fromConfig(serverConfig, WEBSERVER_CONF_PREFIX);
 
     serverUri = "http://"; + jettyServerConfig.getHost() + ":" + 
jettyServerConfig.getHttpPort();
-    if (AuthenticatorType.OAUTH
-        .name()
-        .toLowerCase()
-        .equals(customConfigs.get(Configs.AUTHENTICATOR.getKey()))) {
+
+    List<String> authenticators = new ArrayList<>();
+    String authenticatorStr = 
customConfigs.get(Configs.AUTHENTICATORS.getKey());
+    if (authenticatorStr != null) {
+      authenticators = COMMA.splitToList(authenticatorStr);
+    }
+
+    if (authenticators.contains(AuthenticatorType.OAUTH.name().toLowerCase())) 
{
       client = 
GravitinoAdminClient.builder(serverUri).withOAuth(mockDataProvider).build();
-    } else if (AuthenticatorType.SIMPLE
-        .name()
-        .toLowerCase()
-        .equals(customConfigs.get(Configs.AUTHENTICATOR.getKey()))) {
+    } else if 
(authenticators.contains(AuthenticatorType.SIMPLE.name().toLowerCase())) {
       client = 
GravitinoAdminClient.builder(serverUri).withSimpleAuth().build();
-    } else if (AuthenticatorType.KERBEROS
-        .name()
-        .toLowerCase()
-        .equals(customConfigs.get(Configs.AUTHENTICATOR.getKey()))) {
+    } else if 
(authenticators.contains(AuthenticatorType.KERBEROS.name().toLowerCase())) {
       serverUri = "http://localhost:"; + jettyServerConfig.getHttpPort();
       client = null;
     } else {
diff --git 
a/integration-test/src/test/java/org/apache/gravitino/integration/test/client/AuditIT.java
 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/client/AuditIT.java
index 8541e3ebe..b635dc1f9 100644
--- 
a/integration-test/src/test/java/org/apache/gravitino/integration/test/client/AuditIT.java
+++ 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/client/AuditIT.java
@@ -41,7 +41,7 @@ public class AuditIT extends AbstractIT {
   @BeforeAll
   public static void startIntegrationTest() throws Exception {
     Map<String, String> configs = Maps.newHashMap();
-    configs.put(Configs.AUTHENTICATOR.getKey(), 
AuthenticatorType.SIMPLE.name().toLowerCase());
+    configs.put(Configs.AUTHENTICATORS.getKey(), 
AuthenticatorType.SIMPLE.name().toLowerCase());
     registerCustomConfigs(configs);
     AbstractIT.startIntegrationTest();
   }
diff --git 
a/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/KerberosOperationsIT.java
 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/KerberosOperationsIT.java
index 4f00de993..16de7b06e 100644
--- 
a/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/KerberosOperationsIT.java
+++ 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/KerberosOperationsIT.java
@@ -72,7 +72,7 @@ public class KerberosOperationsIT extends AbstractIT {
     initKeyTab();
 
     Map<String, String> configs = Maps.newHashMap();
-    configs.put(Configs.AUTHENTICATOR.getKey(), 
AuthenticatorType.KERBEROS.name().toLowerCase());
+    configs.put(Configs.AUTHENTICATORS.getKey(), 
AuthenticatorType.KERBEROS.name().toLowerCase());
     configs.put(PRINCIPAL.getKey(), serverPrincipal);
     configs.put(KEYTAB.getKey(), keytabFile);
     configs.put("client.kerberos.principal", clientPrincipal);
diff --git 
a/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/KerberosOperationsIT.java
 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/MultiAuthOperationsIT.java
similarity index 55%
copy from 
integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/KerberosOperationsIT.java
copy to 
integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/MultiAuthOperationsIT.java
index 4f00de993..55fbf1ed8 100644
--- 
a/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/KerberosOperationsIT.java
+++ 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/MultiAuthOperationsIT.java
@@ -16,16 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.gravitino.integration.test.web.rest;
 
+import static 
org.apache.gravitino.server.GravitinoServer.WEBSERVER_CONF_PREFIX;
 import static org.apache.gravitino.server.authentication.KerberosConfig.KEYTAB;
 import static 
org.apache.gravitino.server.authentication.KerberosConfig.PRINCIPAL;
 import static org.apache.hadoop.minikdc.MiniKdc.MAX_TICKET_LIFETIME;
 
 import com.google.common.collect.Maps;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
 import java.io.File;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.util.Base64;
+import java.util.Date;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
@@ -36,6 +43,9 @@ import org.apache.gravitino.client.GravitinoVersion;
 import org.apache.gravitino.client.KerberosTokenProvider;
 import org.apache.gravitino.integration.test.util.AbstractIT;
 import org.apache.gravitino.integration.test.util.ITUtils;
+import org.apache.gravitino.integration.test.util.OAuthMockDataProvider;
+import org.apache.gravitino.server.authentication.OAuthConfig;
+import org.apache.gravitino.server.web.JettyServerConfig;
 import org.apache.hadoop.minikdc.KerberosSecurityTestcase;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
@@ -45,8 +55,7 @@ import org.junit.jupiter.api.Test;
 import 
org.testcontainers.shaded.com.google.common.util.concurrent.Uninterruptibles;
 
 @Tag("gravitino-docker-test")
-public class KerberosOperationsIT extends AbstractIT {
-
+public class MultiAuthOperationsIT extends AbstractIT {
   private static final KerberosSecurityTestcase kdc =
       new KerberosSecurityTestcase() {
         @Override
@@ -55,34 +64,39 @@ public class KerberosOperationsIT extends AbstractIT {
           getConf().setProperty(MAX_TICKET_LIFETIME, "5");
         }
       };
-
+  private static final String clientPrincipal = "[email protected]";
   private static final String keytabFile =
       new File(System.getProperty("test.dir", "target"), 
UUID.randomUUID().toString())
           .getAbsolutePath();
 
-  // The following two keytab are needed both.
-  private static final String serverPrincipal = "HTTP/[email protected]";
-  private static final String serverPrincipalWithAll = 
"HTTP/[email protected]";
-
-  private static final String clientPrincipal = "[email protected]";
+  private static GravitinoAdminClient oauthClient;
+  private static GravitinoAdminClient kerberosClient;
 
   @BeforeAll
   public static void startIntegrationTest() throws Exception {
-    kdc.startMiniKdc();
-    initKeyTab();
-
     Map<String, String> configs = Maps.newHashMap();
-    configs.put(Configs.AUTHENTICATOR.getKey(), 
AuthenticatorType.KERBEROS.name().toLowerCase());
-    configs.put(PRINCIPAL.getKey(), serverPrincipal);
-    configs.put(KEYTAB.getKey(), keytabFile);
-    configs.put("client.kerberos.principal", clientPrincipal);
-    configs.put("client.kerberos.keytab", keytabFile);
+    configs.put(
+        Configs.AUTHENTICATORS.getKey(),
+        String.join(
+            ",",
+            AuthenticatorType.KERBEROS.name().toLowerCase(),
+            AuthenticatorType.OAUTH.name().toLowerCase()));
 
-    registerCustomConfigs(configs);
+    configOAuth(configs);
+    configKerberos(configs);
 
+    registerCustomConfigs(configs);
     AbstractIT.startIntegrationTest();
 
-    client =
+    oauthClient =
+        GravitinoAdminClient.builder(serverUri)
+            .withOAuth(OAuthMockDataProvider.getInstance())
+            .build();
+
+    JettyServerConfig jettyServerConfig =
+        JettyServerConfig.fromConfig(serverConfig, WEBSERVER_CONF_PREFIX);
+    serverUri = "http://localhost:"; + jettyServerConfig.getHttpPort();
+    kerberosClient =
         GravitinoAdminClient.builder(serverUri)
             .withKerberosAuth(
                 KerberosTokenProvider.builder()
@@ -99,23 +113,61 @@ public class KerberosOperationsIT extends AbstractIT {
   }
 
   @Test
-  public void testAuthenticationApi() throws Exception {
-    GravitinoVersion gravitinoVersion = client.serverVersion();
-    Assertions.assertEquals(System.getenv("PROJECT_VERSION"), 
gravitinoVersion.version());
-    Assertions.assertFalse(gravitinoVersion.compileDate().isEmpty());
+  public void testMultiAuthenticationSuccess() throws Exception {
+    GravitinoVersion gravitinoVersion1 = oauthClient.serverVersion();
+    Assertions.assertEquals(System.getenv("PROJECT_VERSION"), 
gravitinoVersion1.version());
+    Assertions.assertFalse(gravitinoVersion1.compileDate().isEmpty());
+    if (testMode.equals(ITUtils.EMBEDDED_TEST_MODE)) {
+      final String gitCommitId = readGitCommitIdFromGitFile();
+      Assertions.assertEquals(gitCommitId, gravitinoVersion1.gitCommit());
+    }
+    new KerberosOperationsIT().testAuthenticationApi();
+
+    GravitinoVersion gravitinoVersion2 = kerberosClient.serverVersion();
+    Assertions.assertEquals(System.getenv("PROJECT_VERSION"), 
gravitinoVersion2.version());
+    Assertions.assertFalse(gravitinoVersion2.compileDate().isEmpty());
 
     if (testMode.equals(ITUtils.EMBEDDED_TEST_MODE)) {
       final String gitCommitId = readGitCommitIdFromGitFile();
-      Assertions.assertEquals(gitCommitId, gravitinoVersion.gitCommit());
+      Assertions.assertEquals(gitCommitId, gravitinoVersion2.gitCommit());
     }
 
     // Test to re-login with the keytab
     Uninterruptibles.sleepUninterruptibly(6, TimeUnit.SECONDS);
-    Assertions.assertEquals(System.getenv("PROJECT_VERSION"), 
gravitinoVersion.version());
-    Assertions.assertFalse(gravitinoVersion.compileDate().isEmpty());
+    Assertions.assertEquals(System.getenv("PROJECT_VERSION"), 
gravitinoVersion2.version());
+    Assertions.assertFalse(gravitinoVersion2.compileDate().isEmpty());
+  }
+
+  @SuppressWarnings("JavaUtilDate")
+  private static void configOAuth(Map<String, String> configs) {
+    KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
+    String publicKey =
+        new String(
+            Base64.getEncoder().encode(keyPair.getPublic().getEncoded()), 
StandardCharsets.UTF_8);
+
+    configs.put(OAuthConfig.SERVICE_AUDIENCE.getKey(), "service1");
+    configs.put(OAuthConfig.DEFAULT_SIGN_KEY.getKey(), publicKey);
+    configs.put(OAuthConfig.ALLOW_SKEW_SECONDS.getKey(), "6");
+    configs.put(OAuthConfig.DEFAULT_SERVER_URI.getKey(), "test");
+    configs.put(OAuthConfig.DEFAULT_TOKEN_PATH.getKey(), "test");
+
+    String token =
+        Jwts.builder()
+            .setSubject("gravitino")
+            .setExpiration(new Date(System.currentTimeMillis() + 1000 * 1000))
+            .setAudience("service1")
+            .signWith(keyPair.getPrivate(), SignatureAlgorithm.RS256)
+            .compact();
+    OAuthMockDataProvider mockDataProvider = 
OAuthMockDataProvider.getInstance();
+    mockDataProvider.setTokenData(token.getBytes(StandardCharsets.UTF_8));
   }
 
-  private static void initKeyTab() throws Exception {
+  private static void configKerberos(Map<String, String> configs) throws 
Exception {
+    kdc.startMiniKdc();
+
+    final String serverPrincipal = "HTTP/[email protected]";
+    final String serverPrincipalWithAll = "HTTP/[email protected]";
+
     File newKeytabFile = new File(keytabFile);
     String newClientPrincipal = removeRealm(clientPrincipal);
     String newServerPrincipal = removeRealm(serverPrincipal);
@@ -123,6 +175,11 @@ public class KerberosOperationsIT extends AbstractIT {
     kdc.getKdc()
         .createPrincipal(
             newKeytabFile, newClientPrincipal, newServerPrincipal, 
newServerPrincipalAll);
+
+    configs.put(PRINCIPAL.getKey(), serverPrincipal);
+    configs.put(KEYTAB.getKey(), keytabFile);
+    configs.put("client.kerberos.principal", clientPrincipal);
+    configs.put("client.kerberos.keytab", keytabFile);
   }
 
   private static String removeRealm(String principal) {
diff --git 
a/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/OAuth2OperationsIT.java
 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/OAuth2OperationsIT.java
index 13a2887d4..e4f10769b 100644
--- 
a/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/OAuth2OperationsIT.java
+++ 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/web/rest/OAuth2OperationsIT.java
@@ -60,7 +60,7 @@ public class OAuth2OperationsIT extends AbstractIT {
             .setAudience("service1")
             .signWith(keyPair.getPrivate(), SignatureAlgorithm.RS256)
             .compact();
-    configs.put(Configs.AUTHENTICATOR.getKey(), 
AuthenticatorType.OAUTH.name().toLowerCase());
+    configs.put(Configs.AUTHENTICATORS.getKey(), 
AuthenticatorType.OAUTH.name().toLowerCase());
     configs.put(OAuthConfig.SERVICE_AUDIENCE.getKey(), "service1");
     configs.put(OAuthConfig.DEFAULT_SIGN_KEY.getKey(), publicKey);
     configs.put(OAuthConfig.ALLOW_SKEW_SECONDS.getKey(), "6");
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticationFilter.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticationFilter.java
index 2f031444a..3479503d4 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticationFilter.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticationFilter.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.security.Principal;
 import java.util.Enumeration;
+import java.util.List;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
@@ -36,15 +37,15 @@ import 
org.apache.gravitino.exceptions.UnauthorizedException;
 
 public class AuthenticationFilter implements Filter {
 
-  private final Authenticator filterAuthenticator;
+  private final List<Authenticator> filterAuthenticators;
 
   public AuthenticationFilter() {
-    filterAuthenticator = null;
+    filterAuthenticators = null;
   }
 
   @VisibleForTesting
-  AuthenticationFilter(Authenticator authenticator) {
-    this.filterAuthenticator = authenticator;
+  AuthenticationFilter(List<Authenticator> authenticators) {
+    this.filterAuthenticators = authenticators;
   }
 
   @Override
@@ -54,11 +55,11 @@ public class AuthenticationFilter implements Filter {
   public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain)
       throws IOException, ServletException {
     try {
-      Authenticator authenticator;
-      if (filterAuthenticator == null) {
-        authenticator = ServerAuthenticator.getInstance().authenticator();
+      List<Authenticator> authenticators;
+      if (filterAuthenticators == null || filterAuthenticators.isEmpty()) {
+        authenticators = ServerAuthenticator.getInstance().authenticators();
       } else {
-        authenticator = filterAuthenticator;
+        authenticators = filterAuthenticators;
       }
       HttpServletRequest req = (HttpServletRequest) request;
       Enumeration<String> headerData = 
req.getHeaders(AuthConstants.HTTP_HEADER_AUTHORIZATION);
@@ -66,10 +67,21 @@ public class AuthenticationFilter implements Filter {
       if (headerData.hasMoreElements()) {
         authData = headerData.nextElement().getBytes(StandardCharsets.UTF_8);
       }
-      if (authenticator.isDataFromToken()) {
-        Principal principal = authenticator.authenticateToken(authData);
-        
request.setAttribute(AuthConstants.AUTHENTICATED_PRINCIPAL_ATTRIBUTE_NAME, 
principal);
+
+      Principal principal = null;
+      for (Authenticator authenticator : authenticators) {
+        if (authenticator.supportsToken(authData) && 
authenticator.isDataFromToken()) {
+          principal = authenticator.authenticateToken(authData);
+          if (principal != null) {
+            
request.setAttribute(AuthConstants.AUTHENTICATED_PRINCIPAL_ATTRIBUTE_NAME, 
principal);
+            break;
+          }
+        }
       }
+      if (principal == null) {
+        throw new UnauthorizedException("The provided credentials did not 
support");
+      }
+
       chain.doFilter(request, response);
     } catch (UnauthorizedException ue) {
       HttpServletResponse resp = (HttpServletResponse) response;
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/Authenticator.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/Authenticator.java
index 57e91b07e..dff4805d4 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/Authenticator.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/Authenticator.java
@@ -55,4 +55,19 @@ public interface Authenticator {
    * @throws RuntimeException if the initialization fails
    */
   void initialize(Config config) throws RuntimeException;
+
+  /**
+   * Determines if the provided token data is supported by this authenticator
+   *
+   * <p>This method checks if the given token data can be processed by this 
authenticator.
+   * Implementations should override this method to provide specific logic for 
determining if the
+   * token data format or content is recognized and can be authenticated.
+   *
+   * @param tokenData The byte array containing the token data to be checked.
+   * @return true if the token data is supported and can be authenticated by 
this authenticator;
+   *     false otherwise.
+   */
+  default boolean supportsToken(byte[] tokenData) {
+    return false;
+  }
 }
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticatorFactory.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticatorFactory.java
index 3eef4831d..31d67cfac 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticatorFactory.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticatorFactory.java
@@ -20,6 +20,8 @@
 package org.apache.gravitino.server.authentication;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import java.util.List;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.Configs;
 import org.apache.gravitino.auth.AuthenticatorType;
@@ -42,15 +44,21 @@ public class AuthenticatorFactory {
 
   private AuthenticatorFactory() {}
 
-  public static Authenticator createAuthenticator(Config config) {
-    String name = config.get(Configs.AUTHENTICATOR);
-    String className = AUTHENTICATORS.getOrDefault(name, name);
+  public static List<Authenticator> createAuthenticators(Config config) {
+    List<String> authenticatorNames = config.get(Configs.AUTHENTICATORS);
 
-    try {
-      return (Authenticator) 
Class.forName(className).getDeclaredConstructor().newInstance();
-    } catch (Exception e) {
-      LOG.error("Failed to create and initialize Authenticator by name {}.", 
name, e);
-      throw new RuntimeException("Failed to create and initialize 
Authenticator: " + name, e);
+    List<Authenticator> authenticators = Lists.newArrayList();
+    for (String name : authenticatorNames) {
+      String className = AUTHENTICATORS.getOrDefault(name, name);
+      try {
+        Authenticator authenticator =
+            (Authenticator) 
Class.forName(className).getDeclaredConstructor().newInstance();
+        authenticators.add(authenticator);
+      } catch (Exception e) {
+        LOG.error("Failed to create and initialize Authenticator by name {}.", 
name, e);
+        throw new RuntimeException("Failed to create and initialize 
Authenticator: " + name, e);
+      }
     }
+    return authenticators;
   }
 }
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/KerberosAuthenticator.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/KerberosAuthenticator.java
index 47507bee2..c5aff415c 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/KerberosAuthenticator.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/KerberosAuthenticator.java
@@ -133,6 +133,13 @@ public class KerberosAuthenticator implements 
Authenticator {
     }
   }
 
+  @Override
+  public boolean supportsToken(byte[] tokenData) {
+    return tokenData != null
+        && new String(tokenData, StandardCharsets.UTF_8)
+            .startsWith(AuthConstants.AUTHORIZATION_NEGOTIATE_HEADER);
+  }
+
   private Principal retrievePrincipalFromToken(String serverPrincipal, byte[] 
clientToken)
       throws GSSException {
     GSSContext gssContext = null;
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/OAuth2TokenAuthenticator.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/OAuth2TokenAuthenticator.java
index ea457bc9a..c3433cf58 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/OAuth2TokenAuthenticator.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/OAuth2TokenAuthenticator.java
@@ -126,6 +126,13 @@ class OAuth2TokenAuthenticator implements Authenticator {
     this.defaultSigningKey = 
decodeSignKey(Base64.getDecoder().decode(configuredSignKey), algType);
   }
 
+  @Override
+  public boolean supportsToken(byte[] tokenData) {
+    return tokenData != null
+        && new String(tokenData, StandardCharsets.UTF_8)
+            .startsWith(AuthConstants.AUTHORIZATION_BEARER_HEADER);
+  }
+
   private static Key decodeSignKey(byte[] key, String algType) {
     try {
       SignatureAlgorithmFamilyType algFamilyType =
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/ServerAuthenticator.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/ServerAuthenticator.java
index 170cc566a..ce112b63a 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/ServerAuthenticator.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/ServerAuthenticator.java
@@ -19,11 +19,12 @@
 
 package org.apache.gravitino.server.authentication;
 
+import java.util.List;
 import org.apache.gravitino.Config;
 
 public class ServerAuthenticator {
 
-  private Authenticator authenticator;
+  private List<Authenticator> authenticators;
 
   private ServerAuthenticator() {}
 
@@ -47,11 +48,13 @@ public class ServerAuthenticator {
    */
   public void initialize(Config config) {
     // Create and initialize Authenticator related modules
-    this.authenticator = AuthenticatorFactory.createAuthenticator(config);
-    this.authenticator.initialize(config);
+    this.authenticators = AuthenticatorFactory.createAuthenticators(config);
+    for (Authenticator authenticator : authenticators) {
+      authenticator.initialize(config);
+    }
   }
 
-  public Authenticator authenticator() {
-    return authenticator;
+  public List<Authenticator> authenticators() {
+    return authenticators;
   }
 }
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/SimpleAuthenticator.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/SimpleAuthenticator.java
index 8763381e4..88ecebd91 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authentication/SimpleAuthenticator.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authentication/SimpleAuthenticator.java
@@ -72,4 +72,11 @@ class SimpleAuthenticator implements Authenticator {
   public void initialize(Config config) throws RuntimeException {
     // no op
   }
+
+  @Override
+  public boolean supportsToken(byte[] tokenData) {
+    return tokenData == null
+        || new String(tokenData, StandardCharsets.UTF_8)
+            .startsWith(AuthConstants.AUTHORIZATION_BASIC_HEADER);
+  }
 }
diff --git 
a/server-common/src/test/java/org/apache/gravitino/server/authentication/TestAuthenticationFilter.java
 
b/server-common/src/test/java/org/apache/gravitino/server/authentication/TestAuthenticationFilter.java
index a3540d8bc..0ef48c922 100644
--- 
a/server-common/src/test/java/org/apache/gravitino/server/authentication/TestAuthenticationFilter.java
+++ 
b/server-common/src/test/java/org/apache/gravitino/server/authentication/TestAuthenticationFilter.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.google.common.collect.Lists;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.Vector;
@@ -45,12 +46,13 @@ public class TestAuthenticationFilter {
   public void testDoFilterNormal() throws ServletException, IOException {
 
     Authenticator authenticator = mock(Authenticator.class);
-    AuthenticationFilter filter = new AuthenticationFilter(authenticator);
+    AuthenticationFilter filter = new 
AuthenticationFilter(Lists.newArrayList(authenticator));
     FilterChain mockChain = mock(FilterChain.class);
     HttpServletRequest mockRequest = mock(HttpServletRequest.class);
     HttpServletResponse mockResponse = mock(HttpServletResponse.class);
     when(mockRequest.getHeaders(AuthConstants.HTTP_HEADER_AUTHORIZATION))
         .thenReturn(new 
Vector<>(Collections.singletonList("user")).elements());
+    when(authenticator.supportsToken(any())).thenReturn(true);
     when(authenticator.isDataFromToken()).thenReturn(true);
     when(authenticator.authenticateToken(any())).thenReturn(new 
UserPrincipal("user"));
     filter.doFilter(mockRequest, mockResponse, mockChain);
@@ -60,16 +62,64 @@ public class TestAuthenticationFilter {
   @Test
   public void testDoFilterWithException() throws ServletException, IOException 
{
     Authenticator authenticator = mock(Authenticator.class);
-    AuthenticationFilter filter = new AuthenticationFilter(authenticator);
+    AuthenticationFilter filter = new 
AuthenticationFilter(Lists.newArrayList(authenticator));
     FilterChain mockChain = mock(FilterChain.class);
     HttpServletRequest mockRequest = mock(HttpServletRequest.class);
     HttpServletResponse mockResponse = mock(HttpServletResponse.class);
     when(mockRequest.getHeaders(AuthConstants.HTTP_HEADER_AUTHORIZATION))
         .thenReturn(new 
Vector<>(Collections.singletonList("user")).elements());
+    when(authenticator.supportsToken(any())).thenReturn(true);
     when(authenticator.isDataFromToken()).thenReturn(true);
     when(authenticator.authenticateToken(any()))
         .thenThrow(new UnauthorizedException("UNAUTHORIZED"));
     filter.doFilter(mockRequest, mockResponse, mockChain);
     verify(mockResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED, 
"UNAUTHORIZED");
   }
+
+  @Test
+  public void testMultiFilterNormal() throws ServletException, IOException {
+
+    Authenticator authenticator1 = mock(Authenticator.class);
+    Authenticator authenticator2 = mock(Authenticator.class);
+    AuthenticationFilter filter =
+        new AuthenticationFilter(Lists.newArrayList(authenticator1, 
authenticator2));
+    FilterChain mockChain = mock(FilterChain.class);
+    HttpServletRequest mockRequest = mock(HttpServletRequest.class);
+    HttpServletResponse mockResponse = mock(HttpServletResponse.class);
+    when(mockRequest.getHeaders(AuthConstants.HTTP_HEADER_AUTHORIZATION))
+        .thenReturn(new 
Vector<>(Collections.singletonList("user")).elements());
+    when(authenticator1.supportsToken(any())).thenReturn(false);
+    when(authenticator1.isDataFromToken()).thenReturn(true);
+    when(authenticator1.authenticateToken(any())).thenReturn(new 
UserPrincipal("user"));
+    when(authenticator2.supportsToken(any())).thenReturn(true);
+    when(authenticator2.isDataFromToken()).thenReturn(true);
+    when(authenticator2.authenticateToken(any())).thenReturn(new 
UserPrincipal("user"));
+
+    filter.doFilter(mockRequest, mockResponse, mockChain);
+    verify(mockResponse, never()).sendError(anyInt(), anyString());
+  }
+
+  @Test
+  public void testMultiFilterWithException() throws ServletException, 
IOException {
+
+    Authenticator authenticator1 = mock(Authenticator.class);
+    Authenticator authenticator2 = mock(Authenticator.class);
+    AuthenticationFilter filter =
+        new AuthenticationFilter(Lists.newArrayList(authenticator1, 
authenticator2));
+    FilterChain mockChain = mock(FilterChain.class);
+    HttpServletRequest mockRequest = mock(HttpServletRequest.class);
+    HttpServletResponse mockResponse = mock(HttpServletResponse.class);
+    when(mockRequest.getHeaders(AuthConstants.HTTP_HEADER_AUTHORIZATION))
+        .thenReturn(new 
Vector<>(Collections.singletonList("user")).elements());
+    when(authenticator1.supportsToken(any())).thenReturn(false);
+    when(authenticator1.isDataFromToken()).thenReturn(true);
+    when(authenticator1.authenticateToken(any())).thenReturn(new 
UserPrincipal("user"));
+    when(authenticator2.supportsToken(any())).thenReturn(true);
+    when(authenticator2.isDataFromToken()).thenReturn(true);
+    when(authenticator2.authenticateToken(any()))
+        .thenThrow(new UnauthorizedException("UNAUTHORIZED"));
+
+    filter.doFilter(mockRequest, mockResponse, mockChain);
+    verify(mockResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED, 
"UNAUTHORIZED");
+  }
 }
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/ConfigServlet.java 
b/server/src/main/java/org/apache/gravitino/server/web/ConfigServlet.java
index c040a301f..cdce0a0b1 100644
--- a/server/src/main/java/org/apache/gravitino/server/web/ConfigServlet.java
+++ b/server/src/main/java/org/apache/gravitino/server/web/ConfigServlet.java
@@ -42,7 +42,7 @@ public class ConfigServlet extends HttpServlet {
       ImmutableSet.of(OAuthConfig.DEFAULT_SERVER_URI, 
OAuthConfig.DEFAULT_TOKEN_PATH);
 
   private static final ImmutableSet<ConfigEntry<?>> basicConfigEntries =
-      ImmutableSet.of(Configs.AUTHENTICATOR);
+      ImmutableSet.of(Configs.AUTHENTICATORS);
 
   private final Map<String, String> configs = Maps.newHashMap();
 
@@ -51,7 +51,10 @@ public class ConfigServlet extends HttpServlet {
       String config = String.valueOf(serverConfig.get(key));
       configs.put(key.getKey(), config);
     }
-    if 
(serverConfig.get(Configs.AUTHENTICATOR).equalsIgnoreCase(AuthenticatorType.OAUTH.name()))
 {
+
+    if (serverConfig
+        .get(Configs.AUTHENTICATORS)
+        .contains(AuthenticatorType.OAUTH.name().toLowerCase())) {
       for (ConfigEntry<?> key : oauthConfigEntries) {
         String config = String.valueOf(serverConfig.get(key));
         configs.put(key.getKey(), config);
diff --git a/web/src/lib/store/auth/index.js b/web/src/lib/store/auth/index.js
index c8d77dbb9..83d58394c 100644
--- a/web/src/lib/store/auth/index.js
+++ b/web/src/lib/store/auth/index.js
@@ -38,7 +38,9 @@ export const getAuthConfigs = 
createAsyncThunk('auth/getAuthConfigs', async () =
   }
 
   oauthUrl = 
`${res['gravitino.authenticator.oauth.serverUri']}${res['gravitino.authenticator.oauth.tokenPath']}`
-  authType = res['gravitino.authenticator']
+
+  // ** get the first authenticator from the response. response example: 
"[simple, oauth]"
+  authType = res['gravitino.authenticators'].slice(1, -1).split(',')[0].trim()
 
   localStorage.setItem('oauthUrl', oauthUrl)
 

Reply via email to