provide a simple login module (#521)

diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/annotation/Authority.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/annotation/Authority.java
new file mode 100644
index 0000000..1e353ef
--- /dev/null
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/annotation/Authority.java
@@ -0,0 +1,32 @@
+/*
+ * 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.dubbo.admin.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Inherited
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface Authority {
+
+    boolean needLogin() default false;
+}
\ No newline at end of file
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/config/WebMvcConfiguration.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/config/WebMvcConfiguration.java
new file mode 100644
index 0000000..cacadda
--- /dev/null
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/config/WebMvcConfiguration.java
@@ -0,0 +1,34 @@
+/*
+ * 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.dubbo.admin.config;
+
+import org.apache.dubbo.admin.interceptor.AuthInterceptor;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebMvcConfiguration implements WebMvcConfigurer {
+    @Autowired
+    private AuthInterceptor interceptor;
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(interceptor).addPathPatterns("/**");
+    }
+}
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/AccessesController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/AccessesController.java
index 3510e54..00e7e7d 100644
--- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/AccessesController.java
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/AccessesController.java
@@ -16,7 +16,7 @@
  */
 package org.apache.dubbo.admin.controller;
 
-import org.apache.commons.lang3.StringUtils;
+import org.apache.dubbo.admin.annotation.Authority;
 import org.apache.dubbo.admin.common.exception.ParamValidationException;
 import org.apache.dubbo.admin.common.exception.ResourceNotFoundException;
 import org.apache.dubbo.admin.common.exception.VersionValidationException;
@@ -27,6 +27,8 @@
 import org.apache.dubbo.admin.service.RouteService;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
+
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -41,6 +43,7 @@
 import java.util.List;
 import java.util.Objects;
 
+@Authority(needLogin = true)
 @RestController
 @RequestMapping("/api/{env}/rules/access")
 public class AccessesController {
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ConditionRoutesController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ConditionRoutesController.java
index 8486d3e..7f5d8af 100644
--- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ConditionRoutesController.java
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ConditionRoutesController.java
@@ -18,6 +18,8 @@
 package org.apache.dubbo.admin.controller;
 
 import org.apache.commons.lang3.StringUtils;
+
+import org.apache.dubbo.admin.annotation.Authority;
 import org.apache.dubbo.admin.common.exception.ParamValidationException;
 import org.apache.dubbo.admin.common.exception.ResourceNotFoundException;
 import org.apache.dubbo.admin.common.exception.VersionValidationException;
@@ -37,7 +39,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
-
+@Authority(needLogin = true)
 @RestController
 @RequestMapping("/api/{env}/rules/route/condition")
 public class ConditionRoutesController {
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/LoadBalanceController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/LoadBalanceController.java
index 3daad1f..d0f525e 100644
--- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/LoadBalanceController.java
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/LoadBalanceController.java
@@ -18,6 +18,8 @@
 package org.apache.dubbo.admin.controller;
 
 import org.apache.commons.lang3.StringUtils;
+
+import org.apache.dubbo.admin.annotation.Authority;
 import org.apache.dubbo.admin.common.exception.ParamValidationException;
 import org.apache.dubbo.admin.common.exception.ResourceNotFoundException;
 import org.apache.dubbo.admin.common.exception.VersionValidationException;
@@ -38,7 +40,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-
+@Authority(needLogin = true)
 @RestController
 @RequestMapping("/api/{env}/rules/balancing")
 public class LoadBalanceController {
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ManagementController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ManagementController.java
index b12c689..896dae7 100644
--- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ManagementController.java
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ManagementController.java
@@ -17,6 +17,7 @@
 
 package org.apache.dubbo.admin.controller;
 
+import org.apache.dubbo.admin.annotation.Authority;
 import org.apache.dubbo.admin.common.exception.ParamValidationException;
 import org.apache.dubbo.admin.common.exception.ResourceNotFoundException;
 import org.apache.dubbo.admin.common.util.Constants;
@@ -37,7 +38,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.regex.Pattern;
-
+@Authority(needLogin = true)
 @RestController
 @RequestMapping("/api/{env}/manage")
 public class ManagementController {
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java
index 9c061a2..6acc0af 100644
--- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java
@@ -19,6 +19,8 @@
 
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
+
+import org.apache.dubbo.admin.annotation.Authority;
 import org.apache.dubbo.admin.common.util.Constants;
 import org.apache.dubbo.admin.common.util.Tool;
 import org.apache.dubbo.admin.model.domain.Consumer;
@@ -42,7 +44,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
+@Authority(needLogin = true)
 @RestController
 @RequestMapping("/api/{env}/metrics")
 public class MetricsCollectController {
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/OverridesController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/OverridesController.java
index c50ae01..39667d9 100644
--- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/OverridesController.java
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/OverridesController.java
@@ -18,6 +18,8 @@
 package org.apache.dubbo.admin.controller;
 
 import org.apache.commons.lang3.StringUtils;
+
+import org.apache.dubbo.admin.annotation.Authority;
 import org.apache.dubbo.admin.common.exception.ParamValidationException;
 import org.apache.dubbo.admin.common.exception.ResourceNotFoundException;
 import org.apache.dubbo.admin.common.exception.VersionValidationException;
@@ -37,7 +39,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
-
+@Authority(needLogin = true)
 @RestController
 @RequestMapping("/api/{env}/rules/override")
 public class OverridesController {
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java
index 66a05b8..64f19a3 100644
--- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java
@@ -18,6 +18,8 @@
 package org.apache.dubbo.admin.controller;
 
 import com.google.gson.Gson;
+
+import org.apache.dubbo.admin.annotation.Authority;
 import org.apache.dubbo.admin.common.util.Constants;
 import org.apache.dubbo.admin.common.util.Tool;
 import org.apache.dubbo.admin.model.domain.Consumer;
@@ -42,7 +44,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
-
+@Authority(needLogin = true)
 @RestController
 @RequestMapping("/api/{env}")
 public class ServiceController {
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceTestController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceTestController.java
index 9fc946b..cc8b608 100644
--- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceTestController.java
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceTestController.java
@@ -19,6 +19,8 @@
 
 import com.google.gson.Gson;
 import org.apache.dubbo.admin.common.util.Constants;
+
+import org.apache.dubbo.admin.annotation.Authority;
 import org.apache.dubbo.admin.common.util.ConvertUtil;
 import org.apache.dubbo.admin.common.util.ServiceTestUtil;
 import org.apache.dubbo.admin.model.domain.MethodMetadata;
@@ -37,7 +39,7 @@
 
 import java.util.List;
 import java.util.Map;
-
+@Authority(needLogin = true)
 @RestController
 @RequestMapping("/api/{env}/test")
 public class ServiceTestController {
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/TagRoutesController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/TagRoutesController.java
index f3c88ec..e6b8318 100644
--- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/TagRoutesController.java
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/TagRoutesController.java
@@ -18,6 +18,8 @@
 package org.apache.dubbo.admin.controller;
 
 import org.apache.commons.lang3.StringUtils;
+
+import org.apache.dubbo.admin.annotation.Authority;
 import org.apache.dubbo.admin.common.exception.ParamValidationException;
 import org.apache.dubbo.admin.common.exception.ResourceNotFoundException;
 import org.apache.dubbo.admin.common.exception.VersionValidationException;
@@ -37,7 +39,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
-
+@Authority(needLogin = true)
 @RestController
 @RequestMapping("/api/{env}/rules/route/tag")
 public class TagRoutesController {
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/UserController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/UserController.java
new file mode 100644
index 0000000..4cfb3f1
--- /dev/null
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/UserController.java
@@ -0,0 +1,96 @@
+/*
+ * 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.dubbo.admin.controller;
+
+import org.apache.dubbo.admin.annotation.Authority;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+@RestController
+@RequestMapping("/api/{env}/user")
+public class UserController {
+    public static Map<String /*token*/, User /*user info*/> tokenMap = new ConcurrentHashMap<>();
+
+    @Value("${admin.root.user.name:}")
+    private String rootUserName;
+    @Value("${admin.root.user.password:}")
+    private String rootUserPassword;
+
+    @RequestMapping(value = "/login", method = RequestMethod.GET)
+    public String login(@RequestParam String userName, @RequestParam String password) {
+        if (StringUtils.isBlank(rootUserName) || (rootUserName.equals(userName) && rootUserPassword.equals(password))) {
+            UUID uuid = UUID.randomUUID();
+            String token = uuid.toString();
+            User user = new User();
+            user.setUserName(userName);
+            user.setLastUpdateTime(System.currentTimeMillis());
+            tokenMap.put(token, user);
+            return token;
+        }
+        return null;
+    }
+
+    @Authority(needLogin = true)
+    @RequestMapping(value = "/logout", method = RequestMethod.DELETE)
+    public boolean logout() {
+        HttpServletRequest request =
+                ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
+        String token = request.getHeader("Authorization");
+        return null != tokenMap.remove(token);
+    }
+
+    @Scheduled(cron= "0 5 * * * ?")
+    public void clearExpiredToken() {
+        tokenMap.entrySet().removeIf(entry -> entry.getValue() == null || System.currentTimeMillis() - entry.getValue().getLastUpdateTime() > 1000 * 60 * 15);
+    }
+
+    public static class User {
+        private String userName;
+        private long lastUpdateTime;
+
+        public String getUserName() {
+            return userName;
+        }
+
+        public void setUserName(String userName) {
+            this.userName = userName;
+        }
+
+        public long getLastUpdateTime() {
+            return lastUpdateTime;
+        }
+
+        public void setLastUpdateTime(long lastUpdateTime) {
+            this.lastUpdateTime = lastUpdateTime;
+        }
+    }
+
+}
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/WeightController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/WeightController.java
index 3333e90..d6cbc4c 100644
--- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/WeightController.java
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/WeightController.java
@@ -18,6 +18,8 @@
 package org.apache.dubbo.admin.controller;
 
 import org.apache.commons.lang3.StringUtils;
+
+import org.apache.dubbo.admin.annotation.Authority;
 import org.apache.dubbo.admin.common.exception.ParamValidationException;
 import org.apache.dubbo.admin.common.exception.ResourceNotFoundException;
 import org.apache.dubbo.admin.common.exception.VersionValidationException;
@@ -37,7 +39,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
-
+@Authority(needLogin = true)
 @RestController
 @RequestMapping("/api/{env}/rules/weight")
 public class WeightController {
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/interceptor/AuthInterceptor.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/interceptor/AuthInterceptor.java
new file mode 100644
index 0000000..ac18432
--- /dev/null
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/interceptor/AuthInterceptor.java
@@ -0,0 +1,60 @@
+/*
+ * 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.dubbo.admin.interceptor;
+
+import org.apache.dubbo.admin.annotation.Authority;
+import org.apache.dubbo.admin.controller.UserController;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+
+@Component
+public class AuthInterceptor extends HandlerInterceptorAdapter {
+    @Value("${admin.check.authority:true}")
+    private boolean checkAuthority;
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        if (!(handler instanceof HandlerMethod) || !checkAuthority) {
+            return true;
+        }
+        HandlerMethod handlerMethod = (HandlerMethod) handler;
+        Method method = handlerMethod.getMethod();
+        Authority authority = method.getDeclaredAnnotation(Authority.class);
+        if (null == authority) {
+            authority = method.getDeclaringClass().getDeclaredAnnotation(Authority.class);
+        }
+        if (null != authority && authority.needLogin()) {
+            String authorization = request.getHeader("Authorization");
+            UserController.User user = UserController.tokenMap.get(authorization);
+            if (null != user && System.currentTimeMillis() - user.getLastUpdateTime() <= 1000 * 60 * 15) {
+                user.setLastUpdateTime(System.currentTimeMillis());
+                return true;
+            }
+            response.setStatus(HttpStatus.UNAUTHORIZED.value());
+            return false;
+        } else {
+            return true;
+        }
+    }
+}
diff --git a/dubbo-admin-server/src/main/resources/application-test.properties b/dubbo-admin-server/src/main/resources/application-test.properties
index 2363252..075c357 100644
--- a/dubbo-admin-server/src/main/resources/application-test.properties
+++ b/dubbo-admin-server/src/main/resources/application-test.properties
@@ -19,3 +19,4 @@
 admin.registry.address=zookeeper://127.0.0.1:2182
 admin.config-center=zookeeper://127.0.0.1:2182
 admin.metadata.address=zookeeper://127.0.0.1:2182
+admin.check.authority=false
\ No newline at end of file
diff --git a/dubbo-admin-server/src/main/resources/application.properties b/dubbo-admin-server/src/main/resources/application.properties
index 056946d..a8a79c1 100644
--- a/dubbo-admin-server/src/main/resources/application.properties
+++ b/dubbo-admin-server/src/main/resources/application.properties
@@ -20,6 +20,8 @@
 admin.config-center=zookeeper://127.0.0.1:2181
 admin.metadata-report.address=zookeeper://127.0.0.1:2181
 
+admin.root.user.name=root
+admin.root.user.password=root
 #group
 admin.registry.group=dubbo
 admin.config-center.group=dubbo
diff --git a/dubbo-admin-ui/src/App.vue b/dubbo-admin-ui/src/App.vue
index e83bfd4..5bc1add 100644
--- a/dubbo-admin-ui/src/App.vue
+++ b/dubbo-admin-ui/src/App.vue
@@ -16,26 +16,12 @@
   -->
 
 <template>
-  <v-app :dark="dark">
-    <drawer></drawer>
-    <toolbar></toolbar>
-    <v-content>
-      <router-view/>
-    </v-content>
-    <footers></footers>
-  </v-app>
+  <div id="app">
+    <router-view/>
+  </div>
 </template>
 <script>
-  import Drawer from '@/components/public/Drawer'
-  import Toolbar from '@/components/public/Toolbar'
-  import Footers from '@/components/public/Footers'
-
   export default {
-    components: {
-      Drawer,
-      Toolbar,
-      Footers
-    },
     data () {
       return {
         dark: false
diff --git a/dubbo-admin-ui/src/Index.vue b/dubbo-admin-ui/src/Index.vue
new file mode 100644
index 0000000..d1a00ee
--- /dev/null
+++ b/dubbo-admin-ui/src/Index.vue
@@ -0,0 +1,62 @@
+<!--
+  - 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.
+  -->
+<template>
+  <v-app :dark="dark">
+    <drawer></drawer>
+    <toolbar></toolbar>
+    <v-content>
+      <router-view/>
+    </v-content>
+    <footers></footers>
+  </v-app>
+</template>
+
+<script>
+  import Drawer from '@/components/public/Drawer'
+  import Toolbar from '@/components/public/Toolbar'
+  import Footers from '@/components/public/Footers'
+  export default {
+    name: 'Index',
+    components: {
+      Drawer,
+      Toolbar,
+      Footers
+    },
+    data () {
+      return {
+        dark: false
+      }
+    },
+    created () {
+      window.getApp = this
+      window.getApp.$on('APP_LOGOUT', () => {
+        console.log('logout')
+        window.getApp.$axios.delete('/user/logout')
+          .then(response => {
+            if (response.status === 200 && response.data) {
+              localStorage.removeItem('token')
+              localStorage.removeItem('username')
+              window.getApp.$router.replace('/login')
+            }
+          })
+      })
+    }
+  }
+</script>
+
+<style scoped>
+</style>
diff --git a/dubbo-admin-ui/src/Login.vue b/dubbo-admin-ui/src/Login.vue
new file mode 100644
index 0000000..e4004c8
--- /dev/null
+++ b/dubbo-admin-ui/src/Login.vue
@@ -0,0 +1,100 @@
+<!--
+  - 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.
+  -->
+<template>
+  <v-app id="inspire">
+    <v-content>
+      <v-container fluid fill-height>
+        <v-layout align-center justify-center>
+          <v-flex xs12 sm8 md4>
+            <v-card class="elevation-12">
+              <v-toolbar dark color="primary">
+                <v-spacer></v-spacer>
+              </v-toolbar>
+              <v-card-text>
+                <v-form action='login' >
+                  <v-text-field required
+                                name="username"
+                                v-model="userName"
+                                append-icon="person"
+                                :label="$t('userName')" type="text">
+                  </v-text-field>
+                  <v-text-field
+                    name="input-10-2"
+                    :label="$t('password')"
+                    hint="At least 8 characters"
+                    min="8"
+                    :append-icon="e2 ? 'visibility' : 'visibility_off'"
+                    :append-icon-cb="() => (e2 = !e2)"
+                    v-model="password"
+                    class="input-group--focused"
+                    :type="e2 ? 'password' : 'text'"
+                  ></v-text-field>
+
+                  <v-card-actions>
+                    <v-spacer></v-spacer>
+                    <v-btn @click="login" color="primary">{{$t('login')}}<v-icon>send</v-icon></v-btn>
+                    <v-spacer></v-spacer>
+                  </v-card-actions>
+                </v-form>
+              </v-card-text>
+            </v-card>
+          </v-flex>
+        </v-layout>
+      </v-container>
+    </v-content>
+    <footers></footers>
+  </v-app>
+</template>
+
+<script>
+  import Footers from '@/components/public/Footers'
+  export default {
+    name: 'Login',
+    data: () => ({
+      userName: '',
+      password: '',
+      e2: true
+    }),
+    components: {
+      Footers
+    },
+    methods: {
+      login: function () {
+        let userName = this.userName
+        let password = this.password
+        let vm = this
+        this.$axios.get('/user/login', {
+          params: {
+            userName,
+            password
+          }
+        }).then(response => {
+          if (response.status === 200 && response.data) {
+            localStorage.setItem('token', response.data)
+            localStorage.setItem('username', userName)
+            this.$router.replace('/')
+          } else {
+            vm.$notify('Username or password error,please try again')
+          }
+        })
+      }
+    }
+  }
+</script>
+
+<style scoped>
+</style>
diff --git a/dubbo-admin-ui/src/components/http-common.js b/dubbo-admin-ui/src/components/http-common.js
index 3421e56..96890bf 100644
--- a/dubbo-admin-ui/src/components/http-common.js
+++ b/dubbo-admin-ui/src/components/http-common.js
@@ -22,13 +22,23 @@
   baseURL: '/api/dev'
 })
 
+instance.interceptors.request.use(config => {
+  let token = localStorage.getItem('token')
+  if (token) {
+    config.headers.Authorization = token
+  }
+  return config
+})
+
 instance.interceptors.response.use((response) => {
   return response
 }, (error) => {
   if (error.message.indexOf('Network Error') >= 0) {
     Vue.prototype.$notify.error('Network error, please check your network settings!')
   } else if (error.response.status === HttpStatus.UNAUTHORIZED) {
-    // TODO jump to url
+    localStorage.removeItem('token')
+    localStorage.removeItem('username')
+    Vue.prototype.$notify.error('Authorized failed,please login.')
   } else if (error.response.status >= HttpStatus.BAD_REQUEST) {
     Vue.prototype.$notify.error(error.response.data.message)
   }
diff --git a/dubbo-admin-ui/src/components/public/Toolbar.vue b/dubbo-admin-ui/src/components/public/Toolbar.vue
index 66fdd35..7c1ee22 100644
--- a/dubbo-admin-ui/src/components/public/Toolbar.vue
+++ b/dubbo-admin-ui/src/components/public/Toolbar.vue
@@ -73,7 +73,7 @@
     </v-menu>
 
     <!--login user info-->
-    <v-menu offset-y origin="center center" :nudge-bottom="10" transition="scale-transition" v-if="false">
+    <v-menu offset-y origin="center center" :nudge-bottom="10" transition="scale-transition">
       <v-btn icon large flat slot="activator">
         <v-avatar size="30px">
           <img src="@/assets/avatar.png" alt="Logined User" />
@@ -113,14 +113,6 @@
           }
         },
         {
-          icon: 'settings',
-          href: '#',
-          title: 'Settings',
-          click: (e) => {
-            console.log(e)
-          }
-        },
-        {
           icon: 'fullscreen_exit',
           href: '#',
           title: 'Logout',
@@ -167,6 +159,10 @@
       } else {
         this.selectedLang = 'English'
       }
+      let username = localStorage.getItem('username')
+      if (username) {
+        this.items[0].title = this.$t('userName') + ':' + username
+      }
     }
   }
 </script>
diff --git a/dubbo-admin-ui/src/lang/en.js b/dubbo-admin-ui/src/lang/en.js
index d8f44ef..e762f39 100644
--- a/dubbo-admin-ui/src/lang/en.js
+++ b/dubbo-admin-ui/src/lang/en.js
@@ -156,5 +156,8 @@
   methods: 'Methods',
   testModule: {
     searchServiceHint: 'Service ID, org.apache.dubbo.demo.api.DemoService, * for fuzzy search, press Enter to search'
-  }
+  },
+  userName: 'User Name',
+  password: 'Password',
+  login: 'Login'
 }
diff --git a/dubbo-admin-ui/src/lang/zh.js b/dubbo-admin-ui/src/lang/zh.js
index d13f7b6..cf74e33 100644
--- a/dubbo-admin-ui/src/lang/zh.js
+++ b/dubbo-admin-ui/src/lang/zh.js
@@ -156,5 +156,8 @@
   methods: '方法列表',
   testModule: {
     searchServiceHint: '服务ID, org.apache.dubbo.demo.api.DemoService, 使用 * 进行模糊查找, 按回车键查询'
-  }
+  },
+  userName: '用户名',
+  password: '密码',
+  login: '登录'
 }
diff --git a/dubbo-admin-ui/src/main.js b/dubbo-admin-ui/src/main.js
index b161680..908cb98 100644
--- a/dubbo-admin-ui/src/main.js
+++ b/dubbo-admin-ui/src/main.js
@@ -56,3 +56,18 @@
   components: { App },
   template: '<App/>'
 })
+
+router.beforeEach((to, from, next) => {
+  if (to.matched.some(record => record.meta.requireLogin)) {
+    if (localStorage.getItem('token')) {
+      next()
+    } else {
+      next({
+        path: '/login',
+        query: {redirect: to.fullPath}
+      })
+    }
+  } else {
+    next()
+  }
+})
diff --git a/dubbo-admin-ui/src/router/index.js b/dubbo-admin-ui/src/router/index.js
index 4b1d318..e61ffe2 100644
--- a/dubbo-admin-ui/src/router/index.js
+++ b/dubbo-admin-ui/src/router/index.js
@@ -31,79 +31,140 @@
 import ServiceMetrics from '@/components/metrics/ServiceMetrics'
 import ServiceRelation from '@/components/metrics/ServiceRelation'
 import Management from '@/components/Management'
+import Index from '@/Index'
+import Login from '@/Login'
 
 Vue.use(Router)
 
 export default new Router({
   routes: [
     {
-      path: '/service',
-      name: 'ServiceSearch',
-      component: ServiceSearch
-    },
-    {
-      path: '/serviceDetail',
-      name: 'ServiceDetail',
-      component: ServiceDetail
-    },
-    {
-      path: '/testMethod',
-      name: 'TestMethod',
-      component: TestMethod
-    },
-    {
-      path: '/governance/routingRule',
-      name: 'RoutingRule',
-      component: RoutingRule
-    },
-    {
-      path: '/governance/tagRule',
-      name: 'TagRule',
-      component: TagRule
-    },
-    {
-      path: '/governance/access',
-      name: 'AccessControl',
-      component: AccessControl
-    },
-    {
-      path: '/governance/loadbalance',
-      name: 'LoadBalance',
-      component: LoadBalance
-    },
-    { path: '/governance/weight',
-      name: 'WeightAdjust',
-      component: WeightAdjust
-    },
-    {
-      path: '/governance/config',
-      name: 'Overrides',
-      component: Overrides
-    },
-    {
-      path: '/test',
-      name: 'ServiceTest',
-      component: ServiceTest
-    },
-    {
-      path: '/mock',
-      name: 'ServiceMock',
-      component: ServiceMock
-    },
-    {
-      path: '/metrics/index',
-      name: 'ServiceMetrics',
-      component: ServiceMetrics
-    },
-    {
-      path: '/metrics/relation',
-      name: 'ServiceRelation',
-      component: ServiceRelation
-    },
-    {
-      path: '/management',
-      name: 'Management',
-      component: Management
+      path: '/',
+      name: 'Index',
+      component: Index,
+      meta: {
+        requireLogin: true
+      },
+      children: [
+        {
+          path: '/service',
+          name: 'ServiceSearch',
+          component: ServiceSearch,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/serviceDetail',
+          name: 'ServiceDetail',
+          component: ServiceDetail,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/testMethod',
+          name: 'TestMethod',
+          component: TestMethod,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/governance/routingRule',
+          name: 'RoutingRule',
+          component: RoutingRule,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/governance/tagRule',
+          name: 'TagRule',
+          component: TagRule,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/governance/access',
+          name: 'AccessControl',
+          component: AccessControl,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/governance/loadbalance',
+          name: 'LoadBalance',
+          component: LoadBalance,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/governance/weight',
+          name: 'WeightAdjust',
+          component: WeightAdjust,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/governance/config',
+          name: 'Overrides',
+          component: Overrides,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/test',
+          name: 'ServiceTest',
+          component: ServiceTest,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/mock',
+          name: 'ServiceMock',
+          component: ServiceMock,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/metrics/index',
+          name: 'ServiceMetrics',
+          component: ServiceMetrics,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/metrics/relation',
+          name: 'ServiceRelation',
+          component: ServiceRelation,
+          meta: {
+            requireLogin: true
+          }
+        },
+        {
+          path: '/management',
+          name: 'Management',
+          component: Management,
+          meta: {
+            requireLogin: true
+          }
+        }]
+    }, {
+      path: '/login',
+      name: 'Login',
+      component: Login,
+      meta: {
+        requireLogin: false
+      }
     }
 
   ]