Added JWT Token Support (#203)

* Added JWT Token Authentication Support Upgraded Spring and gradle to 4.10.2

* Added pulsarJwtToken to Test Cases

* Send token only when configured

* Fixed License header formatting

Signed-off-by: Yuvaraj Loganathan <uvaraj6@gmail.com>

* Reverted Spring & gradle upgrade
Will raise an separate pull request for the upgrade

Signed-off-by: Yuvaraj Loganathan <uvaraj6@gmail.com>

* Reverted Gradle Version

Signed-off-by: Yuvaraj Loganathan <uvaraj6@gmail.com>
diff --git a/.gitignore b/.gitignore
index a892a4f..5b37fc9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,3 +45,5 @@
 front-end/data/
 *.log
 dbdata
+src/main/resources/local.properties
+
diff --git a/front-end/src/utils/request.js b/front-end/src/utils/request.js
index df7d81f..dcbe922 100644
--- a/front-end/src/utils/request.js
+++ b/front-end/src/utils/request.js
@@ -22,7 +22,7 @@
 // create an axios instance
 const service = axios.create({
   baseURL: process.env.BASE_API, // api 的 base_url
-  timeout: 5000 // request timeout
+  timeout: 60000 // request timeout
 })
 
 // request interceptor
diff --git a/src/main/java/io/streamnative/pulsar/manager/controller/EnvironmentsController.java b/src/main/java/io/streamnative/pulsar/manager/controller/EnvironmentsController.java
index 49d92e5..d7dea7f 100644
--- a/src/main/java/io/streamnative/pulsar/manager/controller/EnvironmentsController.java
+++ b/src/main/java/io/streamnative/pulsar/manager/controller/EnvironmentsController.java
@@ -24,8 +24,10 @@
 import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
+import org.apache.commons.lang3.StringUtils;
 import org.hibernate.validator.constraints.Range;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.ResponseEntity;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -50,6 +52,9 @@
     @Autowired
     private EnvironmentsRepository environmentsRepository;
 
+    @Value("${backend.jwt.token}")
+    private String pulsarJwtToken;
+
     @Autowired
     private EnvironmentCacheService environmentCacheService;
 
@@ -102,6 +107,9 @@
         }
         Map<String, String> header = Maps.newHashMap();
         header.put("Content-Type", "application/json");
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         String httpTestResult = HttpUtil.doGet(environmentEntity.getBroker() + "/metrics", header);
         if (httpTestResult == null) {
             result.put("error", "This environment is error. Please check it");
@@ -129,6 +137,9 @@
         }
         Map<String, String> header = Maps.newHashMap();
         header.put("Content-Type", "application/json");
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         String httpTestResult = HttpUtil.doGet(environmentEntity.getBroker() + "/metrics", header);
         if (httpTestResult == null) {
             result.put("error", "This environment is error. Please check it");
diff --git a/src/main/java/io/streamnative/pulsar/manager/service/impl/BookiesServiceImpl.java b/src/main/java/io/streamnative/pulsar/manager/service/impl/BookiesServiceImpl.java
index 690f916..49e195c 100644
--- a/src/main/java/io/streamnative/pulsar/manager/service/impl/BookiesServiceImpl.java
+++ b/src/main/java/io/streamnative/pulsar/manager/service/impl/BookiesServiceImpl.java
@@ -18,6 +18,7 @@
 import com.google.gson.Gson;
 import io.streamnative.pulsar.manager.service.BookiesService;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
@@ -44,8 +45,12 @@
     @Value("${bookie.enable}")
     private Boolean bookieEnable;
 
+    @Value("${backend.jwt.token}")
+    private static String pulsarJwtToken;
+
     private static final Map<String, String> header = new HashMap<String, String>(){{
         put("Content-Type","application/json");
+        put("Authorization", String.format("Bearer %s", pulsarJwtToken));
     }};
 
     private final Pattern pattern = Pattern.compile(" \\d+");;
@@ -57,6 +62,9 @@
             Gson gson = new Gson();
             Map<String, String> header = Maps.newHashMap();
             header.put("Content-Type", "application/json");
+            if (StringUtils.isNotBlank(pulsarJwtToken)) {
+                header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+            }
             String rwBookieList = HttpUtil.doGet(
                     bookieHost + "/api/v1/bookie/list_bookies?type=rw&print_hostnames=true", header);
             Map<String, String> rwBookies = gson.fromJson(
diff --git a/src/main/java/io/streamnative/pulsar/manager/service/impl/BrokerStatsServiceImpl.java b/src/main/java/io/streamnative/pulsar/manager/service/impl/BrokerStatsServiceImpl.java
index 10ac073..c328e4f 100644
--- a/src/main/java/io/streamnative/pulsar/manager/service/impl/BrokerStatsServiceImpl.java
+++ b/src/main/java/io/streamnative/pulsar/manager/service/impl/BrokerStatsServiceImpl.java
@@ -38,6 +38,8 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -61,6 +63,9 @@
     @Value("${backend.directRequestHost}")
     private String directRequestHost;
 
+    @Value("${backend.jwt.token}")
+    private String pulsarJwtToken;
+
     @Value("${clear.stats.interval}")
     private Long clearStatsInterval;
 
@@ -96,13 +101,18 @@
     }};
 
     public String forwarBrokerStatsMetrics(String broker, String requestHost) {
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
 
         broker = checkServiceUrl(broker, requestHost);
         return HttpUtil.doGet(broker + "/admin/v2/broker-stats/metrics", header);
     }
 
     public String forwardBrokerStatsTopics(String broker, String requestHost) {
-
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         broker = checkServiceUrl(broker, requestHost);
         return HttpUtil.doGet(broker + "/admin/v2/broker-stats/topics", header);
     }
@@ -134,6 +144,9 @@
     }
 
     public void collectStatsToDB(long unixTime, String env, String cluster, String serviceUrl) {
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         Map<String, Object> brokerObject = brokersService.getBrokersList(0, 0, cluster, serviceUrl);
         List<HashMap<String, Object>> brokerLists = (List<HashMap<String, Object>>) brokerObject.get("data");
         brokerLists.forEach((brokerMap) -> {
diff --git a/src/main/java/io/streamnative/pulsar/manager/service/impl/BrokersServiceImpl.java b/src/main/java/io/streamnative/pulsar/manager/service/impl/BrokersServiceImpl.java
index e8a2cc6..cd6496f 100644
--- a/src/main/java/io/streamnative/pulsar/manager/service/impl/BrokersServiceImpl.java
+++ b/src/main/java/io/streamnative/pulsar/manager/service/impl/BrokersServiceImpl.java
@@ -18,6 +18,7 @@
 import com.google.gson.Gson;
 import io.streamnative.pulsar.manager.service.BrokersService;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
@@ -31,6 +32,9 @@
     @Value("${backend.directRequestBroker}")
     private boolean directRequestBroker;
 
+    @Value("${backend.jwt.token}")
+    private String pulsarJwtToken;
+
 
     public Map<String, Object> getBrokersList(Integer pageNum, Integer pageSize, String cluster, String requestHost) {
         Map<String, Object> brokersMap = Maps.newHashMap();
@@ -39,6 +43,9 @@
             Gson gson = new Gson();
             Map<String, String> header = Maps.newHashMap();
             header.put("Content-Type", "application/json");
+            if (StringUtils.isNotBlank(pulsarJwtToken)) {
+                header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+            }
             String failureDomainsResult = HttpUtil.doGet(
                     requestHost + "/admin/v2/clusters/" + cluster + "/failureDomains", header);
             Map<String, Map<String, List<String>>> failureDomains = gson.fromJson(
diff --git a/src/main/java/io/streamnative/pulsar/manager/service/impl/ClustersServiceImpl.java b/src/main/java/io/streamnative/pulsar/manager/service/impl/ClustersServiceImpl.java
index 27b160d..92820a3 100644
--- a/src/main/java/io/streamnative/pulsar/manager/service/impl/ClustersServiceImpl.java
+++ b/src/main/java/io/streamnative/pulsar/manager/service/impl/ClustersServiceImpl.java
@@ -20,6 +20,8 @@
 import io.streamnative.pulsar.manager.service.ClustersService;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
 import java.util.function.Function;
+
+import org.apache.commons.lang3.StringUtils;
 import org.apache.pulsar.common.policies.data.ClusterData;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -35,6 +37,9 @@
     @Value("${backend.directRequestBroker}")
     private boolean directRequestBroker;
 
+    @Value("${backend.jwt.token}")
+    private String pulsarJwtToken;
+
     @Autowired
     private BrokersService brokersService;
 
@@ -49,6 +54,9 @@
             Gson gson = new Gson();
             Map<String, String> header = Maps.newHashMap();
             header.put("Content-Type", "application/json");
+            if (StringUtils.isNotBlank(pulsarJwtToken)) {
+                header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+            }
             String result = HttpUtil.doGet(envServiceUrl + "/admin/v2/clusters", header);
             List<String> clustersList = gson.fromJson(result, new TypeToken<List<String>>(){}.getType());
             for (String cluster: clustersList) {
diff --git a/src/main/java/io/streamnative/pulsar/manager/service/impl/EnvironmentCacheServiceImpl.java b/src/main/java/io/streamnative/pulsar/manager/service/impl/EnvironmentCacheServiceImpl.java
index 94c6d91..7a8dd8a 100644
--- a/src/main/java/io/streamnative/pulsar/manager/service/impl/EnvironmentCacheServiceImpl.java
+++ b/src/main/java/io/streamnative/pulsar/manager/service/impl/EnvironmentCacheServiceImpl.java
@@ -33,6 +33,7 @@
 import org.apache.commons.lang3.StringUtils;
 import org.apache.pulsar.common.policies.data.ClusterData;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
@@ -46,6 +47,9 @@
     @Autowired
     private EnvironmentsRepository environmentsRepository;
 
+    @Value("${backend.jwt.token}")
+    private String pulsarJwtToken;
+
     private final Map<String, Map<String, ClusterData>> environments;
 
     public EnvironmentCacheServiceImpl() {
@@ -99,6 +103,9 @@
 
     private Map<String, String> jsonHeader() {
         Map<String, String> header = Maps.newHashMap();
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         header.put("Content-Type", "application/json");
         return header;
     }
diff --git a/src/main/java/io/streamnative/pulsar/manager/service/impl/NamespacesServiceImpl.java b/src/main/java/io/streamnative/pulsar/manager/service/impl/NamespacesServiceImpl.java
index 3649652..e1e19e2 100644
--- a/src/main/java/io/streamnative/pulsar/manager/service/impl/NamespacesServiceImpl.java
+++ b/src/main/java/io/streamnative/pulsar/manager/service/impl/NamespacesServiceImpl.java
@@ -22,6 +22,7 @@
 import io.streamnative.pulsar.manager.service.NamespacesService;
 import io.streamnative.pulsar.manager.service.TopicsService;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
@@ -34,6 +35,9 @@
     @Value("${backend.directRequestBroker}")
     private boolean directRequestBroker;
 
+    @Value("${backend.jwt.token}")
+    private String pulsarJwtToken;
+
     @Autowired
     private TopicsStatsRepository topicsStatsRepository;
 
@@ -47,6 +51,9 @@
             Gson gson = new Gson();
             Map<String, String> header = Maps.newHashMap();
             header.put("Content-Type", "application/json");
+            if (StringUtils.isNotBlank(pulsarJwtToken)) {
+                header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+            }
             String result = HttpUtil.doGet(requestHost + "/admin/v2/namespaces/" + tenant, header);
             if (result != null) {
                 List<String> namespacesList = gson.fromJson(result, new TypeToken<List<String>>(){}.getType());
diff --git a/src/main/java/io/streamnative/pulsar/manager/service/impl/TenantsServiceImpl.java b/src/main/java/io/streamnative/pulsar/manager/service/impl/TenantsServiceImpl.java
index 4224398..5017e7d 100644
--- a/src/main/java/io/streamnative/pulsar/manager/service/impl/TenantsServiceImpl.java
+++ b/src/main/java/io/streamnative/pulsar/manager/service/impl/TenantsServiceImpl.java
@@ -18,6 +18,7 @@
 import com.google.gson.Gson;
 import io.streamnative.pulsar.manager.service.TenantsService;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.pulsar.common.policies.data.TenantInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -37,6 +38,9 @@
     @Value("${backend.directRequestBroker}")
     private boolean directRequestBroker;
 
+    @Value("${backend.jwt.token}")
+    private String pulsarJwtToken;
+
     public Map<String, Object> getTenantsList(Integer pageNum, Integer pageSize, String requestHost) {
         Map<String, Object> tenantsMap = Maps.newHashMap();
         List<Map<String, Object>> tenantsArray = new ArrayList<>();
@@ -44,6 +48,9 @@
             Gson gson = new Gson();
             Map<String, String> header = Maps.newHashMap();
             header.put("Content-Type", "application/json");
+            if (StringUtils.isNotBlank(pulsarJwtToken)) {
+                header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+            }
             String result = HttpUtil.doGet( requestHost + "/admin/v2/tenants", header);
             if (result != null) {
                 List<String> tenantsList = gson.fromJson(result, new TypeToken<List<String>>(){}.getType());
diff --git a/src/main/java/io/streamnative/pulsar/manager/service/impl/TopicsServiceImpl.java b/src/main/java/io/streamnative/pulsar/manager/service/impl/TopicsServiceImpl.java
index dd41382..ff25b2b 100644
--- a/src/main/java/io/streamnative/pulsar/manager/service/impl/TopicsServiceImpl.java
+++ b/src/main/java/io/streamnative/pulsar/manager/service/impl/TopicsServiceImpl.java
@@ -22,6 +22,7 @@
 import io.streamnative.pulsar.manager.entity.TopicsStatsRepository;
 import io.streamnative.pulsar.manager.service.TopicsService;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -39,6 +40,9 @@
     @Value("${backend.directRequestBroker}")
     private boolean directRequestBroker;
 
+    @Value("${backend.jwt.token}")
+    private String pulsarJwtToken;
+
     @Autowired
     private TopicsStatsRepository topicsStatsRepository;
 
@@ -192,6 +196,9 @@
             String tenant, String namespace, String persistent, String requestHost) {
         List<Map<String, String>> topicsArray = new ArrayList<>();
         Map<String, String> header = Maps.newHashMap();
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         header.put("Content-Type", "application/json");
         String prefix = "/admin/v2/" + persistent + "/" + tenant + "/" + namespace;
         Gson gson = new Gson();
diff --git a/src/main/java/io/streamnative/pulsar/manager/zuul/EnvironmentForward.java b/src/main/java/io/streamnative/pulsar/manager/zuul/EnvironmentForward.java
index 5657977..96dfd77 100644
--- a/src/main/java/io/streamnative/pulsar/manager/zuul/EnvironmentForward.java
+++ b/src/main/java/io/streamnative/pulsar/manager/zuul/EnvironmentForward.java
@@ -20,6 +20,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
 import org.springframework.stereotype.Component;
 
@@ -40,6 +41,8 @@
 
     @Autowired
     private EnvironmentCacheService environmentCacheService;
+    @Value("${backend.jwt.token}")
+    private String pulsarJwtToken;
 
     @Override
     public String filterType() {
@@ -89,12 +92,13 @@
     private Object forwardRequest(RequestContext ctx, HttpServletRequest request, String serviceUrl) {
         ctx.put(REQUEST_URI_KEY, request.getRequestURI());
         try {
+            ctx.addZuulRequestHeader("Authorization", String.format("Bearer %s", pulsarJwtToken));
             ctx.setRouteHost(new URL(serviceUrl));
             log.info("Forward request to {} @ path {}",
-                serviceUrl, request.getRequestURI());
+                    serviceUrl, request.getRequestURI());
         } catch (MalformedURLException e) {
             log.error("Route forward to {} path {} error: {}",
-                serviceUrl, request.getRequestURI(), e.getMessage());
+                    serviceUrl, request.getRequestURI(), e.getMessage());
         }
         return null;
     }
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index f92afb6..5e3fc7e 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -57,11 +57,14 @@
 #spring.datasource.password=postgres
 
 # zuul config
+# https://cloud.spring.io/spring-cloud-static/Dalston.SR5/multi/multi__router_and_filter_zuul.html
+# By Default Zuul adds  Authorization to be dropped headers list. Below we are manually setting it
+zuul.sensitive-headers=Cookie,Set-Cookie
 zuul.routes.admin.path=/admin/**
 zuul.routes.admin.url=http://localhost:8080/admin/
 zuul.routes.lookup.path=/lookup/**
 zuul.routes.lookup.url=http://localhost:8080/lookup/
-        
+
 # pagehelper plugin
 #pagehelper.helperDialect=sqlite
 # force 'mysql' for HerdDB, comment out for postgresql
@@ -69,6 +72,7 @@
 
 backend.directRequestBroker=true
 backend.directRequestHost=http://localhost:8080
+backend.jwt.token=
 
 jwt.secret=dab1c8ba-b01b-11e9-b384-186590e06885
 jwt.sessionTime=2592000
diff --git a/src/test/java/io/streamnative/pulsar/manager/service/BookiesServiceImplTest.java b/src/test/java/io/streamnative/pulsar/manager/service/BookiesServiceImplTest.java
index 2f556b3..e04aa06 100644
--- a/src/test/java/io/streamnative/pulsar/manager/service/BookiesServiceImplTest.java
+++ b/src/test/java/io/streamnative/pulsar/manager/service/BookiesServiceImplTest.java
@@ -17,6 +17,7 @@
 import io.streamnative.pulsar.manager.PulsarManagerApplication;
 import io.streamnative.pulsar.manager.profiles.HerdDBTestProfile;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -26,6 +27,7 @@
 import org.powermock.modules.junit4.PowerMockRunner;
 import org.powermock.modules.junit4.PowerMockRunnerDelegate;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.TestPropertySource;
@@ -50,11 +52,17 @@
     @Autowired
     private BookiesService bookiesService;
 
+    @Value("${backend.jwt.token}")
+    private static String pulsarJwtToken;
+
     @Test
     public void bookiesServiceTest() {
         PowerMockito.mockStatic(HttpUtil.class);
         Map<String, String> header = Maps.newHashMap();
         header.put("Content-Type", "application/json");
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         PowerMockito.when(HttpUtil.doGet("http://localhost:8050/api/v1/bookie/list_bookies?type=rw&print_hostnames=true", header))
                 .thenReturn("{\"192.168.2.116:3181\" : \"192.168.2.116\"}");
         PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/brokers/standalone", header))
diff --git a/src/test/java/io/streamnative/pulsar/manager/service/BrokerStatsServiceImplTest.java b/src/test/java/io/streamnative/pulsar/manager/service/BrokerStatsServiceImplTest.java
index 62b8336..0abe3df 100644
--- a/src/test/java/io/streamnative/pulsar/manager/service/BrokerStatsServiceImplTest.java
+++ b/src/test/java/io/streamnative/pulsar/manager/service/BrokerStatsServiceImplTest.java
@@ -28,6 +28,7 @@
 import io.streamnative.pulsar.manager.entity.TopicStatsEntity;
 import io.streamnative.pulsar.manager.entity.TopicsStatsRepository;
 import io.streamnative.pulsar.manager.profiles.HerdDBTestProfile;
+import org.apache.commons.lang3.StringUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,6 +38,7 @@
 import org.powermock.modules.junit4.PowerMockRunner;
 import org.powermock.modules.junit4.PowerMockRunnerDelegate;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit4.SpringRunner;
@@ -60,6 +62,9 @@
     @Autowired
     private BrokerStatsService brokerStatsService;
 
+    @Value("${backend.jwt.token}")
+    private static String pulsarJwtToken;
+
     @Autowired
     private TopicsStatsRepository topicsStatsRepository;
 
@@ -217,6 +222,9 @@
         PowerMockito.mockStatic(HttpUtil.class);
         Map<String, String> header = Maps.newHashMap();
         header.put("Content-Type", "application/json");
+        if (StringUtils.isNotBlank(pulsarJwtToken)){
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/clusters", header))
                 .thenReturn("[\"standalone\"]");
         PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/clusters/standalone", header))
diff --git a/src/test/java/io/streamnative/pulsar/manager/service/BrokersServiceImplTest.java b/src/test/java/io/streamnative/pulsar/manager/service/BrokersServiceImplTest.java
index 825e021..a369f02 100644
--- a/src/test/java/io/streamnative/pulsar/manager/service/BrokersServiceImplTest.java
+++ b/src/test/java/io/streamnative/pulsar/manager/service/BrokersServiceImplTest.java
@@ -19,6 +19,8 @@
 import io.streamnative.pulsar.manager.profiles.HerdDBTestProfile;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
 import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -28,6 +30,7 @@
 import org.powermock.modules.junit4.PowerMockRunner;
 import org.powermock.modules.junit4.PowerMockRunnerDelegate;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit4.SpringRunner;
@@ -45,6 +48,9 @@
 @ActiveProfiles("test")
 public class BrokersServiceImplTest {
 
+    @Value("${backend.jwt.token}")
+    private static String pulsarJwtToken;
+
     @Autowired
     private BrokersService brokersService;
 
@@ -53,6 +59,10 @@
         PowerMockito.mockStatic(HttpUtil.class);
         Map<String, String> header = Maps.newHashMap();
         header.put("Content-Type", "application/json");
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
+
         PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/clusters/standalone/failureDomains", header))
                 .thenReturn("{\"test\":{\"brokers\":[\"tengdeMBP:8080\"]}}");
 
diff --git a/src/test/java/io/streamnative/pulsar/manager/service/ClustersServiceImplTest.java b/src/test/java/io/streamnative/pulsar/manager/service/ClustersServiceImplTest.java
index 3c3a04d..51948e5 100644
--- a/src/test/java/io/streamnative/pulsar/manager/service/ClustersServiceImplTest.java
+++ b/src/test/java/io/streamnative/pulsar/manager/service/ClustersServiceImplTest.java
@@ -17,6 +17,7 @@
 import io.streamnative.pulsar.manager.PulsarManagerApplication;
 import io.streamnative.pulsar.manager.profiles.HerdDBTestProfile;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -26,6 +27,7 @@
 import org.powermock.modules.junit4.PowerMockRunner;
 import org.powermock.modules.junit4.PowerMockRunnerDelegate;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit4.SpringRunner;
@@ -48,11 +50,17 @@
     @Autowired
     private ClustersService clustersService;
 
+    @Value("${backend.jwt.token}")
+    private static String pulsarJwtToken;
+
     @Test
     public void clusterServiceImplTest() {
         PowerMockito.mockStatic(HttpUtil.class);
         Map<String, String> header = Maps.newHashMap();
         header.put("Content-Type", "application/json");
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/clusters", header))
                 .thenReturn("[\"standalone\"]");
 
diff --git a/src/test/java/io/streamnative/pulsar/manager/service/NamespacesServiceImplTest.java b/src/test/java/io/streamnative/pulsar/manager/service/NamespacesServiceImplTest.java
index 1710dde..e36b625 100644
--- a/src/test/java/io/streamnative/pulsar/manager/service/NamespacesServiceImplTest.java
+++ b/src/test/java/io/streamnative/pulsar/manager/service/NamespacesServiceImplTest.java
@@ -17,6 +17,7 @@
 import io.streamnative.pulsar.manager.PulsarManagerApplication;
 import io.streamnative.pulsar.manager.profiles.HerdDBTestProfile;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -26,6 +27,7 @@
 import org.powermock.modules.junit4.PowerMockRunner;
 import org.powermock.modules.junit4.PowerMockRunnerDelegate;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit4.SpringRunner;
@@ -51,11 +53,17 @@
     @Autowired
     private BrokerStatsService brokerStatsService;
 
+    @Value("${backend.jwt.token}")
+    private static String pulsarJwtToken;
+
     @Test
     public void namespaceServiceImplTest() {
         PowerMockito.mockStatic(HttpUtil.class);
         Map<String, String> header = Maps.newHashMap();
         header.put("Content-Type", "application/json");
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/namespaces/public", header))
                 .thenReturn("[\"public/default\"]");
 
@@ -75,6 +83,9 @@
         PowerMockito.mockStatic(HttpUtil.class);
         Map<String, String> header = Maps.newHashMap();
         header.put("Content-Type", "application/json");
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/clusters", header))
                 .thenReturn("[\"standalone\"]");
 
diff --git a/src/test/java/io/streamnative/pulsar/manager/service/TenantsServiceImplTest.java b/src/test/java/io/streamnative/pulsar/manager/service/TenantsServiceImplTest.java
index 973f914..7c06822 100644
--- a/src/test/java/io/streamnative/pulsar/manager/service/TenantsServiceImplTest.java
+++ b/src/test/java/io/streamnative/pulsar/manager/service/TenantsServiceImplTest.java
@@ -17,6 +17,7 @@
 import io.streamnative.pulsar.manager.PulsarManagerApplication;
 import io.streamnative.pulsar.manager.profiles.HerdDBTestProfile;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -26,6 +27,7 @@
 import org.powermock.modules.junit4.PowerMockRunner;
 import org.powermock.modules.junit4.PowerMockRunnerDelegate;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit4.SpringRunner;
@@ -51,11 +53,17 @@
     @Autowired
     private TenantsService tenantsService;
 
+    @Value("${backend.jwt.token}")
+    private static String pulsarJwtToken;
+
     @Test
     public void tenantsServiceImplTest() {
         PowerMockito.mockStatic(HttpUtil.class);
         Map<String, String> header = Maps.newHashMap();
         header.put("Content-Type", "application/json");
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/tenants", header)).thenReturn("[\"public\"]");
         PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/tenants/public", header))
                 .thenReturn("{\"adminRoles\": [\"admin\"], \"allowedClusters\": [\"standalone\"]}");
diff --git a/src/test/java/io/streamnative/pulsar/manager/service/TopicsServiceImplTest.java b/src/test/java/io/streamnative/pulsar/manager/service/TopicsServiceImplTest.java
index 233b563..2ec31de 100644
--- a/src/test/java/io/streamnative/pulsar/manager/service/TopicsServiceImplTest.java
+++ b/src/test/java/io/streamnative/pulsar/manager/service/TopicsServiceImplTest.java
@@ -17,6 +17,7 @@
 import io.streamnative.pulsar.manager.PulsarManagerApplication;
 import io.streamnative.pulsar.manager.profiles.HerdDBTestProfile;
 import io.streamnative.pulsar.manager.utils.HttpUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -26,6 +27,7 @@
 import org.powermock.modules.junit4.PowerMockRunner;
 import org.powermock.modules.junit4.PowerMockRunnerDelegate;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit4.SpringRunner;
@@ -53,6 +55,9 @@
     @Autowired
     private BrokerStatsService brokerStatsService;
 
+    @Value("${backend.jwt.token}")
+    private static String pulsarJwtToken;
+
     private final String topics = "[" +
             "\"persistent://public/default/test789\"," +
             "\"persistent://public/default/test900-partition-0\"," +
@@ -66,6 +71,9 @@
         PowerMockito.mockStatic(HttpUtil.class);
         Map<String, String> header = Maps.newHashMap();
         header.put("Content-Type", "application/json");
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/persistent/public/default", header))
                 .thenReturn(topics);
         PowerMockito.when(HttpUtil.doGet(
@@ -87,6 +95,9 @@
         PowerMockito.mockStatic(HttpUtil.class);
         Map<String, String> header = Maps.newHashMap();
         header.put("Content-Type", "application/json");
+        if (StringUtils.isNotBlank(pulsarJwtToken)) {
+            header.put("Authorization", String.format("Bearer %s", pulsarJwtToken));
+        }
         PowerMockito.when(HttpUtil.doGet("http://localhost:8080/admin/v2/clusters", header))
                 .thenReturn("[\"standalone\"]");