You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@skywalking.apache.org by ha...@apache.org on 2018/06/20 02:30:29 UTC

[incubator-skywalking] 01/01: Add login filter

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

hanahmily pushed a commit to branch webapp/login
in repository https://gitbox.apache.org/repos/asf/incubator-skywalking.git

commit 821010ad59bf7e9625f896e61b618faa3462c6ff
Author: gaohongtao <ha...@gmail.com>
AuthorDate: Wed Jun 20 10:29:03 2018 +0800

    Add login filter
    
    Username and password are stored in application.yml with
    prefix "security.user"
---
 .../skywalking/apm/webapp/ApplicationStartUp.java  |  14 +--
 .../skywalking/apm/webapp/config/UIConfig.java     |  36 -------
 .../{tools => proxy}/CollectorServerList.java      |   2 +-
 .../webapp/{tools => proxy}/HttpClientTools.java   |   2 +-
 .../webapp/{tools => proxy}/RewritePathFilter.java |  20 ++--
 .../skywalking/apm/webapp/security/Account.java    |  13 +++
 .../apm/webapp/security/LoginFilter.java           |  78 ++++++++++++++
 .../apm/webapp/security/ReaderAccount.java         |  32 ++++++
 .../apm/webapp/security/UserChecker.java           |  41 +++++++
 apm-webapp/src/main/resources/application.yml      |   7 ++
 .../{tools => proxy}/RewritePathFilterTest.java    | 118 ++++++++++-----------
 .../apm/webapp/security/LoginFilterTest.java       |  52 +++++++++
 .../skywalking/apm/webapp/security/LoginTest.java  |  72 +++++++++++++
 .../apm/webapp/security/ReaderAccountTest.java     |  19 ++++
 .../apm/webapp/security/UserCheckerTest.java       |  78 ++++++++++++++
 15 files changed, 470 insertions(+), 114 deletions(-)

diff --git a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/ApplicationStartUp.java b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/ApplicationStartUp.java
index 5f3fe61..f8f3dde 100644
--- a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/ApplicationStartUp.java
+++ b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/ApplicationStartUp.java
@@ -18,25 +18,17 @@
 
 package org.apache.skywalking.apm.webapp;
 
-import org.apache.skywalking.apm.webapp.config.UIConfig;
-import org.apache.skywalking.apm.webapp.tools.RewritePathFilter;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.support.SpringBootServletInitializer;
 import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.annotation.Bean;
 
 @SpringBootApplication
 @EnableZuulProxy
 public class ApplicationStartUp extends SpringBootServletInitializer {
 
-    public static void main(String[] args) throws Exception {
-        ApplicationContext applicationContext = SpringApplication.run(ApplicationStartUp.class, args);
-    }
-
-    @Bean
-    public RewritePathFilter addWritePathFilter(UIConfig uiConfig) {
-        return new RewritePathFilter(uiConfig.getRewritePath());
+    public static void main(String[] args) {
+        SpringApplication.run(ApplicationStartUp.class, args);
     }
+    
 }
diff --git a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/config/UIConfig.java b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/config/UIConfig.java
deleted file mode 100644
index c5a1f29..0000000
--- a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/config/UIConfig.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.skywalking.apm.webapp.config;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * @author peng-yongsheng
- */
-@Configuration
-public class UIConfig {
-    
-    @Value("${collector.path}")
-    private String rewritePath;
-
-    public String getRewritePath() {
-        return rewritePath;
-    }
-}
diff --git a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/tools/CollectorServerList.java b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/proxy/CollectorServerList.java
similarity index 98%
rename from apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/tools/CollectorServerList.java
rename to apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/proxy/CollectorServerList.java
index 95f3c2e..2df2b0d 100644
--- a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/tools/CollectorServerList.java
+++ b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/proxy/CollectorServerList.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.skywalking.apm.webapp.tools;
+package org.apache.skywalking.apm.webapp.proxy;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
diff --git a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/tools/HttpClientTools.java b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/proxy/HttpClientTools.java
similarity index 98%
rename from apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/tools/HttpClientTools.java
rename to apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/proxy/HttpClientTools.java
index da162a8..8fcc974 100644
--- a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/tools/HttpClientTools.java
+++ b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/proxy/HttpClientTools.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.skywalking.apm.webapp.tools;
+package org.apache.skywalking.apm.webapp.proxy;
 
 import org.apache.http.HttpEntity;
 import org.apache.http.NameValuePair;
diff --git a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/tools/RewritePathFilter.java b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/proxy/RewritePathFilter.java
similarity index 80%
rename from apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/tools/RewritePathFilter.java
rename to apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/proxy/RewritePathFilter.java
index e3303f8..83450aa 100644
--- a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/tools/RewritePathFilter.java
+++ b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/proxy/RewritePathFilter.java
@@ -16,10 +16,12 @@
  *
  */
 
-package org.apache.skywalking.apm.webapp.tools;
+package org.apache.skywalking.apm.webapp.proxy;
 
 import com.netflix.zuul.ZuulFilter;
 import com.netflix.zuul.context.RequestContext;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
 
 import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
 
@@ -28,17 +30,23 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst
  *
  * @author gaohongtao
  */
+@Component
+@ConfigurationProperties(prefix = "collector")
 public class RewritePathFilter extends ZuulFilter {
 
     private static final String REQUEST_URI = "requestURI";
 
-    private static final int ORDER = PRE_DECORATION_FILTER_ORDER + 1;
+    private static final int ORDER = PRE_DECORATION_FILTER_ORDER + 2;
+    
+    private String path;
 
-    public RewritePathFilter(String rewritePath) {
-        this.rewritePath = rewritePath;
+    public String getPath() {
+        return path;
     }
 
-    private final String rewritePath;
+    public void setPath(String path) {
+        this.path = path;
+    }
 
     @Override
     public int filterOrder() {
@@ -59,7 +67,7 @@ public class RewritePathFilter extends ZuulFilter {
     @Override
     public Object run() {
         RequestContext ctx = RequestContext.getCurrentContext();
-        ctx.set(REQUEST_URI, rewritePath);
+        ctx.set(REQUEST_URI, path);
         return null;
     }
 }
diff --git a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/Account.java b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/Account.java
new file mode 100644
index 0000000..fd070ad
--- /dev/null
+++ b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/Account.java
@@ -0,0 +1,13 @@
+package org.apache.skywalking.apm.webapp.security;
+
+/**
+ * Account of Login.
+ * 
+ * @author gaohongtao
+ */
+public interface Account {
+
+    String userName();
+
+    String password();
+}
diff --git a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/LoginFilter.java b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/LoginFilter.java
new file mode 100644
index 0000000..fc6b584
--- /dev/null
+++ b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/LoginFilter.java
@@ -0,0 +1,78 @@
+package org.apache.skywalking.apm.webapp.security;
+
+import com.google.gson.Gson;
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import java.io.IOException;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ReflectionUtils;
+
+import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
+
+/**
+ * Filter login request.
+ * 
+ * @author gaohongtao
+ */
+@Component
+public class LoginFilter extends ZuulFilter {
+
+    private static final String REQUEST_URI = "requestURI";
+    
+    private static final String LOGIN_URI = "/login/account";
+
+    private static final int ORDER = PRE_DECORATION_FILTER_ORDER + 1;
+    
+    private final UserChecker checker;
+    
+    public LoginFilter(final UserChecker checker) {
+        this.checker = checker;
+    }
+
+    @Override public String filterType() {
+        return "pre";
+    }
+
+    @Override public int filterOrder() {
+        return ORDER;
+    }
+
+    @Override public boolean shouldFilter() {
+        RequestContext ctx = RequestContext.getCurrentContext();
+        return ctx.get(REQUEST_URI).equals(LOGIN_URI);
+    }
+
+    @Override public Object run() {
+        RequestContext ctx = RequestContext.getCurrentContext();
+        Account loginAccount = null;
+        try {
+            loginAccount = ReaderAccount.newReaderAccount(ctx.getRequest().getReader());
+        } catch (IOException e) {
+            ReflectionUtils.rethrowRuntimeException(e);
+        }
+        Gson gson = new Gson();
+        String resStr;
+        if (checker.check(loginAccount)) {
+            resStr = gson.toJson(new ResponseData("ok", "admin"));
+        } else {
+            resStr = gson.toJson(new ResponseData("error", "guest"));
+        }
+        HttpServletResponse response = ctx.getResponse();
+        response.setContentType("application/json");
+        response.setCharacterEncoding("UTF-8");
+        ctx.setResponseStatusCode(HttpServletResponse.SC_OK);
+        ctx.setResponseBody(resStr);
+        ctx.setSendZuulResponse(false);
+        return null;
+    }
+    
+    private static class ResponseData {
+        private final String status;
+        private final String currentAuthority;
+        ResponseData(final String status, final String currentAuthority) {
+            this.status = status;
+            this.currentAuthority = currentAuthority;
+        }
+    }
+}
diff --git a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/ReaderAccount.java b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/ReaderAccount.java
new file mode 100644
index 0000000..8cd27c5
--- /dev/null
+++ b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/ReaderAccount.java
@@ -0,0 +1,32 @@
+package org.apache.skywalking.apm.webapp.security;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.BufferedReader;
+
+/**
+ * A container of login information.
+ * 
+ * @author gaohongtao
+ */
+class ReaderAccount implements Account {
+
+    private final static Gson GSON = new GsonBuilder().disableHtmlEscaping()
+        .setLenient().create();
+    
+    private String username;
+    
+    private String password;
+    
+    static ReaderAccount newReaderAccount(final BufferedReader accountReader) {
+        return GSON.fromJson(accountReader, ReaderAccount.class);
+    }
+    
+    public String userName() {
+        return username;
+    }
+    
+    public String password() {
+        return password;
+    }
+}
diff --git a/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/UserChecker.java b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/UserChecker.java
new file mode 100644
index 0000000..ef26962
--- /dev/null
+++ b/apm-webapp/src/main/java/org/apache/skywalking/apm/webapp/security/UserChecker.java
@@ -0,0 +1,41 @@
+package org.apache.skywalking.apm.webapp.security;
+
+import com.google.common.base.Strings;
+import java.util.HashMap;
+import java.util.Map;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * A Checker to check username and password.
+ * 
+ * @author gaohongtao
+ */
+@Component
+@ConfigurationProperties(prefix = "security")
+public class UserChecker {
+    
+    private Map<String, User> user = new HashMap<>();
+
+    public Map<String, User> getUser() {
+        return user;
+    }
+
+    boolean check(Account account) {
+        if (Strings.isNullOrEmpty(account.userName()) || Strings.isNullOrEmpty(account.password())) {
+            return false;
+        }
+        if (!user.containsKey(account.userName())) {
+            return false;
+        }
+        return user.get(account.userName()).password.equals(account.password());
+    }
+    
+    public static class User {
+        private String password;
+
+        public void setPassword(String password) {
+            this.password = password;
+        }
+    }
+}
diff --git a/apm-webapp/src/main/resources/application.yml b/apm-webapp/src/main/resources/application.yml
index 20d57d5..d63d17c 100644
--- a/apm-webapp/src/main/resources/application.yml
+++ b/apm-webapp/src/main/resources/application.yml
@@ -28,3 +28,10 @@ collector:
   ribbon:
     listOfServers: 127.0.0.1:10800
     NIWSServerListClassName: org.apache.skywalking.apm.webapp.tools.CollectorServerList
+
+security:
+  user:
+    admin:
+      password: admin
+    guest:
+      password:
diff --git a/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/tools/RewritePathFilterTest.java b/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/proxy/RewritePathFilterTest.java
similarity index 89%
rename from apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/tools/RewritePathFilterTest.java
rename to apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/proxy/RewritePathFilterTest.java
index b856fb6..f46ee2a 100644
--- a/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/tools/RewritePathFilterTest.java
+++ b/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/proxy/RewritePathFilterTest.java
@@ -1,60 +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.skywalking.apm.webapp.tools;
-
-import com.netflix.zuul.context.RequestContext;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.*;
-import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
-
-public class RewritePathFilterTest {
-
-    private RewritePathFilter filter;
-
-    @Before
-    public void init() {
-        filter = new RewritePathFilter("/graphql");
-    }
-
-    @Test
-    public void filterOrder() {
-        assertThat(filter.filterOrder(), is(PRE_DECORATION_FILTER_ORDER + 1));
-    }
-
-    @Test
-    public void filterType() {
-        assertThat(filter.filterType(), is("pre"));
-    }
-
-    @Test
-    public void shouldFilter() {
-        assertFalse(filter.shouldFilter());
-        RequestContext.getCurrentContext().set("requestURI");
-        assertTrue(filter.shouldFilter());
-    }
-
-    @Test
-    public void run() {
-        filter.run();
-        assertThat(RequestContext.getCurrentContext().get("requestURI"), is("/graphql"));
-    }
+/*
+ * 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.skywalking.apm.webapp.proxy;
+
+import com.netflix.zuul.context.RequestContext;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
+
+public class RewritePathFilterTest {
+
+    private RewritePathFilter filter = new RewritePathFilter();
+
+    @Before
+    public void init() {
+        filter.setPath("/graphql");
+    }
+
+    @Test
+    public void filterOrder() {
+        assertThat(filter.filterOrder(), is(PRE_DECORATION_FILTER_ORDER + 1));
+    }
+
+    @Test
+    public void filterType() {
+        assertThat(filter.filterType(), is("pre"));
+    }
+
+    @Test
+    public void shouldFilter() {
+        assertFalse(filter.shouldFilter());
+        RequestContext.getCurrentContext().set("requestURI");
+        assertTrue(filter.shouldFilter());
+    }
+
+    @Test
+    public void run() {
+        filter.run();
+        assertThat(RequestContext.getCurrentContext().get("requestURI"), is("/graphql"));
+    }
 }
\ No newline at end of file
diff --git a/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/LoginFilterTest.java b/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/LoginFilterTest.java
new file mode 100644
index 0000000..fc5ba25
--- /dev/null
+++ b/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/LoginFilterTest.java
@@ -0,0 +1,52 @@
+package org.apache.skywalking.apm.webapp.security;
+
+import com.netflix.zuul.context.RequestContext;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
+
+
+public class LoginFilterTest {
+    
+    private LoginFilter loginFilter;
+
+    @Before
+    public void setUp() {
+        UserChecker checker = new UserChecker();
+        UserChecker.User user = new UserChecker.User();
+        user.setPassword("admin");
+        checker.getUser().put("admin", user);
+        loginFilter = new LoginFilter(checker);
+    }
+
+    @After
+    public void tearDown() {
+        RequestContext.testSetCurrentContext(null);
+    }
+
+    @Test
+    public void assertFilterType() {
+        assertThat(loginFilter.filterType(), is("pre"));
+    }
+
+    @Test
+    public void assertFilterOrder() {
+        assertThat(loginFilter.filterOrder(), is(PRE_DECORATION_FILTER_ORDER + 1));
+    }
+
+    @Test
+    public void assertShouldFilter() {
+        RequestContext ctx = new RequestContext();
+        ctx.set("requestURI", "/login/account");
+        RequestContext.testSetCurrentContext(ctx);
+        assertTrue(loginFilter.shouldFilter());
+        ctx.set("requestURI", "/dashboard");
+        assertFalse(loginFilter.shouldFilter());
+    }
+}
\ No newline at end of file
diff --git a/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/LoginTest.java b/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/LoginTest.java
new file mode 100644
index 0000000..3e2dd57
--- /dev/null
+++ b/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/LoginTest.java
@@ -0,0 +1,72 @@
+package org.apache.skywalking.apm.webapp.security;
+
+import com.netflix.zuul.context.RequestContext;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.lang.reflect.UndeclaredThrowableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class LoginTest {
+
+    private LoginFilter loginFilter;
+
+    @Mock
+    private RequestContext ctx;
+
+    @Mock
+    private HttpServletRequest request;
+    
+    @Mock
+    private HttpServletResponse response;
+
+    @Before
+    public void setUp() {
+        UserChecker checker = new UserChecker();
+        UserChecker.User user = new UserChecker.User();
+        user.setPassword("admin");
+        checker.getUser().put("admin", user);
+        loginFilter = new LoginFilter(checker);
+        when(ctx.getRequest()).thenReturn(request);
+        when(ctx.getResponse()).thenReturn(response);
+        RequestContext.testSetCurrentContext(ctx);
+    }
+
+    @Test
+    public void assertSuccessLogin() throws IOException {
+        when(request.getReader()).thenReturn(new BufferedReader(new StringReader("{\"username\": \"admin\", \"password\":\"admin\"}")));
+        loginFilter.run();
+        assertHeaderAndStatusCode();
+        verify(ctx).setResponseBody("{\"status\":\"ok\",\"currentAuthority\":\"admin\"}");
+    }
+    
+    @Test
+    public void assertFailLogin() throws IOException {
+        when(request.getReader()).thenReturn(new BufferedReader(new StringReader("{\"username\": \"admin\", \"password\":\"888888\"}")));
+        loginFilter.run();
+        assertHeaderAndStatusCode();
+        verify(ctx).setResponseBody("{\"status\":\"error\",\"currentAuthority\":\"guest\"}");
+    }
+
+    @Test(expected = UndeclaredThrowableException.class)
+    public void assertException() throws IOException {
+        when(request.getReader()).thenThrow(new IOException());
+        loginFilter.run();
+    }
+    
+    private void assertHeaderAndStatusCode() {
+        verify(ctx).setResponseStatusCode(HttpServletResponse.SC_OK);
+        verify(response).setContentType("application/json");
+        verify(response).setCharacterEncoding("UTF-8");
+    }
+}
diff --git a/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/ReaderAccountTest.java b/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/ReaderAccountTest.java
new file mode 100644
index 0000000..962e68b
--- /dev/null
+++ b/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/ReaderAccountTest.java
@@ -0,0 +1,19 @@
+package org.apache.skywalking.apm.webapp.security;
+
+import java.io.BufferedReader;
+import java.io.StringReader;
+import org.junit.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+
+public class ReaderAccountTest {
+
+    @Test
+    public void assertNewReaderAccount() {
+        Account account = ReaderAccount.newReaderAccount(new BufferedReader(new StringReader("{\"username\": \"admin\", \"password\":\"888888\"}")));
+        assertThat(account.userName(), is("admin"));
+        assertThat(account.password(), is("888888"));
+    }
+    
+}
\ No newline at end of file
diff --git a/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/UserCheckerTest.java b/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/UserCheckerTest.java
new file mode 100644
index 0000000..d9b3afb
--- /dev/null
+++ b/apm-webapp/src/test/java/org/apache/skywalking/apm/webapp/security/UserCheckerTest.java
@@ -0,0 +1,78 @@
+package org.apache.skywalking.apm.webapp.security;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class UserCheckerTest {
+
+    @Test
+    public void assertCheckSuccess() {
+        UserChecker checker = new UserChecker();
+        UserChecker.User user = new UserChecker.User();
+        user.setPassword("888888");
+        checker.getUser().put("admin", user);
+        assertTrue(checker.check(new Account() {
+            @Override public String userName() {
+                return "admin";
+            }
+
+            @Override public String password() {
+                return "888888";
+            }
+        }));
+    }
+
+    @Test
+    public void assertCheckFail() {
+        UserChecker checker = new UserChecker();
+        UserChecker.User user = new UserChecker.User();
+        user.setPassword("123456");
+        checker.getUser().put("guest", user);
+        assertFalse(checker.check(new Account() {
+            @Override public String userName() {
+                return "admin";
+            }
+
+            @Override public String password() {
+                return "888888";
+            }
+        }));
+        assertFalse(checker.check(new Account() {
+            @Override public String userName() {
+                return "guest";
+            }
+
+            @Override public String password() {
+                return "888888";
+            }
+        }));
+        assertFalse(checker.check(new Account() {
+            @Override public String userName() {
+                return "admin";
+            }
+
+            @Override public String password() {
+                return "123456";
+            }
+        }));
+        assertFalse(checker.check(new Account() {
+            @Override public String userName() {
+                return "";
+            }
+
+            @Override public String password() {
+                return "123456";
+            }
+        }));
+        assertFalse(checker.check(new Account() {
+            @Override public String userName() {
+                return "admin";
+            }
+
+            @Override public String password() {
+                return "";
+            }
+        }));
+    }
+}
\ No newline at end of file