You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ma...@apache.org on 2019/08/08 01:30:10 UTC

[servicecomb-samples] 31/43: first commit for login and gateway microservice

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

mabin pushed a commit to branch houserush-sample
in repository https://gitbox.apache.org/repos/asf/servicecomb-samples.git

commit fceb59e5bc3f068eb7fd2587db004c53f4ebe41c
Author: linzb0123 <li...@qq.com>
AuthorDate: Fri Aug 2 11:48:03 2019 +0800

    first commit for login and gateway microservice
---
 houserush/gateway/README.md                        |  53 +++++++++
 houserush/gateway/pom.xml                          |   5 -
 .../houserush/gateway/GatewayApplication.java      |  43 ++++++++
 .../practise/houserush/gateway/GatewayConfig.java  |  25 +++++
 .../houserush/gateway/config/LoginUrlConfig.java   |  42 +++++++
 .../houserush/gateway/filter/AuthorizeFilter.java  | 122 +++++++++++++++++++++
 .../practise/houserush/gateway/rpc/UserApi.java    |  26 +++++
 .../practise/houserush/gateway/rpc/po/User.java    |  28 +++++
 .../gateway/src/main/resources/application.yaml    |  39 +++++++
 .../gateway/src/main/resources/microservice.yaml   |  66 +++++++++++
 houserush/login/README.md                          |  89 +++++++++++++++
 houserush/login/pom.xml                            |   5 +
 .../practise/houserush/login/LoginApplication.java |  44 ++++++++
 .../practise/houserush/login/LoginConfig.java      |  26 +++++
 .../practise/houserush/login/aggregate/User.java   | 117 ++++++++++++++++++++
 .../practise/houserush/login/api/UserApi.java      |  34 ++++++
 .../houserush/login/api/UserApiRestImpl.java       |  61 +++++++++++
 .../practise/houserush/login/dao/UserDao.java      |  25 +++++
 .../houserush/login/service/UserService.java       |  36 ++++++
 .../houserush/login/service/UserServiceImpl.java   |  93 ++++++++++++++++
 .../login/src/main/resources/microservice.yaml     |  43 ++++++++
 21 files changed, 1017 insertions(+), 5 deletions(-)

diff --git a/houserush/gateway/README.md b/houserush/gateway/README.md
new file mode 100755
index 0000000..3756401
--- /dev/null
+++ b/houserush/gateway/README.md
@@ -0,0 +1,53 @@
+## 微服务 gateway
+
+该为微服务为API网关,作为对外的唯一入口,主要负责路由转发和鉴权。
+
+### 主要功能
+
+- API入口
+- 动态路由
+- 鉴权
+- 。。。
+
+### 设计原理
+- 使用[zuul](https://github.com/Netflix/zuul/wiki)来设计实现API网关功能
+
+![API gateway工作流程](https://raw.githubusercontent.com/linzb0123/images/master/servicecomb-samples-houserush/gateway1.png)
+
+### 实现
+
+- 路由转发配置
+
+- 鉴权
+
+  - 自定义AuthorizeFilter
+
+    ```java
+    @Component
+    public class AuthorizeFilter extends ZuulFilter {
+        @Override
+      public String filterType() {
+        return "pre";
+      }
+    
+      @Override
+      public int filterOrder() {
+        return 0;
+      }
+      @Override
+      public boolean shouldFilter() {
+        return true;
+      }
+      @Override
+      public Object run() {
+          //根据uri判断是否需要鉴权
+          //向认证中心登录获取token
+          //提取request的header中的Authorization字段的token
+          //向认证中心检验Authorization是否有效
+          //...
+      }
+        
+        
+    }
+    ```
+    [使用zuul做边缘服务](https://docs.servicecomb.io/java-chassis/zh_CN/edge/zuul.html)
\ No newline at end of file
diff --git a/houserush/gateway/pom.xml b/houserush/gateway/pom.xml
index a0c2a7c..9e256a8 100644
--- a/houserush/gateway/pom.xml
+++ b/houserush/gateway/pom.xml
@@ -54,11 +54,6 @@
       <artifactId>spring-cloud-zuul-zipkin</artifactId>
     </dependency>
     <dependency>
-      <groupId>com.auth0</groupId>
-      <artifactId>java-jwt</artifactId>
-      <version>3.8.1</version>
-    </dependency>
-    <dependency>
       <groupId>org.json</groupId>
       <artifactId>json</artifactId>
       <version>20180813</version>
diff --git a/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/GatewayApplication.java b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/GatewayApplication.java
new file mode 100755
index 0000000..c128606
--- /dev/null
+++ b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/GatewayApplication.java
@@ -0,0 +1,43 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.gateway;
+
+import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory;
+import org.apache.servicecomb.springboot.starter.provider.EnableServiceComb;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
+
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+@SpringBootApplication
+@EnableZuulProxy
+@EnableServiceComb
+public class GatewayApplication {
+  public static void main(String[] args) {
+    configBeforeBoot();
+    SpringApplication.run(GatewayApplication.class, args);
+  }
+
+  private static void configBeforeBoot() {
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+    simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
+    RestObjectMapperFactory.getRestObjectMapper().setDateFormat(simpleDateFormat);
+  }
+}
diff --git a/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/GatewayConfig.java b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/GatewayConfig.java
new file mode 100755
index 0000000..87dd5e5
--- /dev/null
+++ b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/GatewayConfig.java
@@ -0,0 +1,25 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.gateway;
+
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class GatewayConfig {
+
+}
diff --git a/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/config/LoginUrlConfig.java b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/config/LoginUrlConfig.java
new file mode 100755
index 0000000..c738e5f
--- /dev/null
+++ b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/config/LoginUrlConfig.java
@@ -0,0 +1,42 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.gateway.config;
+
+import com.netflix.config.DynamicPropertyFactory;
+import com.netflix.config.DynamicStringProperty;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class LoginUrlConfig {
+  private DynamicStringProperty loginUrls = DynamicPropertyFactory.getInstance()
+      .getStringProperty("gateway.loginUrls", "");
+
+  private DynamicStringProperty nologinUrls = DynamicPropertyFactory.getInstance()
+      .getStringProperty("gateway.noLoginUrls", "");
+
+  public Set<String> loginUrlsSet = new HashSet<>(Arrays.asList(loginUrls.get().split(",")));
+
+  public Set<String> nologinUrlsSet = new HashSet<>(Arrays.asList(nologinUrls.get().split(",")));
+
+  public LoginUrlConfig() {
+    //TODO runtime change set
+
+  }
+}
diff --git a/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/filter/AuthorizeFilter.java b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/filter/AuthorizeFilter.java
new file mode 100755
index 0000000..390051e
--- /dev/null
+++ b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/filter/AuthorizeFilter.java
@@ -0,0 +1,122 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.gateway.filter;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import org.apache.commons.lang.StringUtils;
+import org.apache.servicecomb.provider.pojo.RpcReference;
+import org.apache.servicecomb.samples.practise.houserush.gateway.config.LoginUrlConfig;
+import org.apache.servicecomb.samples.practise.houserush.gateway.rpc.UserApi;
+import org.apache.servicecomb.samples.practise.houserush.gateway.rpc.po.User;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+
+@Component
+public class AuthorizeFilter extends ZuulFilter {
+
+  private static final String SECRET = "231sdfqwer21313123cafkhioerutieweirqwuqbjffbqwrwr3";
+
+  private static LoginUrlConfig loginUrlConfig = new LoginUrlConfig();
+  private static Logger log = LoggerFactory.getLogger(AuthorizeFilter.class);
+
+  @RpcReference(microserviceName = "login", schemaId = "userApiRest")
+  private UserApi userApi;
+
+  @Override
+  public String filterType() {
+    return "pre";
+  }
+
+  @Override
+  public int filterOrder() {
+    return 0;
+  }
+
+  @Override
+  public boolean shouldFilter() {
+    return true;
+  }
+
+  private RequestContext ctx;
+
+  @Override
+  public Object run() {
+    ctx = RequestContext.getCurrentContext();
+    HttpServletRequest request = ctx.getRequest();
+    String method = request.getMethod();
+    String requestUri = request.getRequestURI();
+    log.info(String.format("%s -> %s", method, requestUri));
+    requestUri = requestUri.replaceAll("\\d+", "{id}");
+
+    String key = method + " " + requestUri;
+    if (loginUrlConfig.loginUrlsSet.contains(key)) {
+      String token = request.getHeader("Authorization");
+      if (token != null && StringUtils.isNotBlank(token)) {
+        User user = userApi.verifyToken(token);
+        if (user != null) {
+          ctx.addZuulRequestHeader("customerId", String.valueOf(user.getId()));
+          ctx.addZuulResponseHeader("newAuthorization", user.getToken());
+          return null;
+        }
+      }
+      sendResponse(403, "need login!");
+    } else if (loginUrlConfig.nologinUrlsSet.contains(key)) {
+      if ("/login/signin".equals(requestUri)) {
+        try {
+          ObjectMapper mapper = new ObjectMapper();
+          User user = mapper.readValue(request.getInputStream(), User.class);
+          String username = user.getUsername();
+          String password = user.getPassword();
+          User resultUser = userApi.signin(user);
+          if (resultUser != null && resultUser.getToken() != null) {
+            sendResponse(200, "{\"token\": \"" + resultUser.getToken() + "\"}");
+          } else {
+            sendResponse(401, "cannot sign in!");
+          }
+        } catch (IOException e) {
+          e.printStackTrace();
+          sendResponse(401, e.getMessage());
+        }
+      }
+      return null;
+    } else {
+      sendResponse(401, "the request url is not validate");
+    }
+    return null;
+
+  }
+
+  private void sendResponse(int code, String message) {
+    ctx.setSendZuulResponse(false);
+    ctx.setResponseStatusCode(code);
+    try {
+      ctx.getResponse().getWriter().write(message);
+    } catch (Exception e) {
+      log.warn(e.getMessage());
+    }
+  }
+
+
+}
diff --git a/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/rpc/UserApi.java b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/rpc/UserApi.java
new file mode 100755
index 0000000..3a82676
--- /dev/null
+++ b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/rpc/UserApi.java
@@ -0,0 +1,26 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.gateway.rpc;
+
+import org.apache.servicecomb.samples.practise.houserush.gateway.rpc.po.User;
+
+public interface UserApi {
+  User signin(User user);
+
+  User verifyToken(String token);
+}
diff --git a/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/rpc/po/User.java b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/rpc/po/User.java
new file mode 100755
index 0000000..5b37356
--- /dev/null
+++ b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/rpc/po/User.java
@@ -0,0 +1,28 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.gateway.rpc.po;
+
+import lombok.Data;
+
+@Data
+public class User {
+  private int id;
+  private String username;
+  private String password;
+  private String token;
+}
diff --git a/houserush/gateway/src/main/resources/application.yaml b/houserush/gateway/src/main/resources/application.yaml
new file mode 100755
index 0000000..c46bf3e
--- /dev/null
+++ b/houserush/gateway/src/main/resources/application.yaml
@@ -0,0 +1,39 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+zuul:
+  routes:
+    realestate: /realestate/**
+    customer-manage: /customer-manage/**
+    house-order: /house-order/**
+    login: /login/**
+    user-center: /user-center/**
+
+
+
+# disable netflix eurkea since it's not used for service discovery
+ribbon:
+  eureka:
+    enabled: false
+
+server:
+  port: 8889
+
+servicecomb:
+  tracing:
+    enabled: false
+
diff --git a/houserush/gateway/src/main/resources/microservice.yaml b/houserush/gateway/src/main/resources/microservice.yaml
new file mode 100755
index 0000000..2f44af2
--- /dev/null
+++ b/houserush/gateway/src/main/resources/microservice.yaml
@@ -0,0 +1,66 @@
+#
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+# all interconnected microservices must belong to an application wth the same ID
+APPLICATION_ID: houserush
+service_description:
+  # name of the declaring microservice
+  name: gateway
+  version: 0.0.4
+servicecomb:
+  service:
+    registry:
+      address: http://127.0.0.1:30100
+
+gateway:
+  loginUrls:
+    - POST /realestate/realestates
+    - GET /realestate/realestates/{id}
+    - PUT /realestate/realestates/{id}
+    - DELETE /realestate/realestates/{id}
+    - GET /realestate/realestates
+    - POST /realestate/{id}/buildings
+    - GET /realestate/buildings/{id}
+    - PUT /realestate/buildings/{id}
+    - DELETE /realestate/buildings/{id}
+    - GET /realestate/realestates/{id}/buildings
+    - POST /realestate/buildings/{buildingId}/houses
+    - GET /realestate/houses/{id}
+    - PUT /realestate/houses/{id}
+    - DELETE /realestate/houses/{id}
+    - GET /realestate/buildings/{id}/houses
+    - PUT /realestate/houses/lock_houses_for_sale
+    - POST /customer-manage/customers
+    - GET /customer-manage/customers/{id}
+    - PUT /customer-manage/customers/{id}
+    - DELETE /customer-manage/customers/{id}
+    - GET /customer-manage/customers
+    - PUT /customer-manage/customers/{id}/update_qualifications
+    - POST /house-order/sales/{id}/house_orders
+    - PUT /house-order/house_orders/{id}
+    - POST /house-order/sales
+    - GET /house-order/sales/{id}
+    - PUT /house-order/sales/{id}
+    - DELETE /house-order/sales/{id}
+    - GET /house-order/sales
+
+
+  noLoginUrls:
+    - POST /login/users
+    - PUT /login/signin
+
diff --git a/houserush/login/README.md b/houserush/login/README.md
new file mode 100755
index 0000000..efbe99d
--- /dev/null
+++ b/houserush/login/README.md
@@ -0,0 +1,89 @@
+## 微服务 login
+
+该微服务作为认证中心,主要负责token的生成与校验。
+
+### 主要功能
+
+- 登录账号的维护
+
+- 生成token
+
+- 校验token
+
+### 设计原理
+
+基于[JWT](https://jwt.io/introduction/)实现易于横向拓展的分布式认证中心。
+
+JWT标准格式
+
+```json
+{
+  //Header
+  "alg": "HS256",
+  "typ": "JWT",
+  //Payload
+  "sub": "1234567890",
+  "name": "John Doe",
+  "admin": true,
+  //Signature
+  HMACSHA256(
+  base64UrlEncode(header) + "." +
+  base64UrlEncode(payload),
+  secret)
+}
+```
+
+[JWT的一种实现](https://github.com/auth0/java-jwt)
+
+
+
+### 数据库设计
+
+users表
+
+| id              | int          | 主键id   |
+| :-------------- | ------------ | -------- |
+| username        | varchar(255) | 用户名   |
+| hashed_password | varchar(255) | 密码hash |
+| deleted_at      | timestamp    | 删除时间 |
+| created_at      | timestamp    | 创建时间 |
+| update_at       | timestamp    | 更新时间 |
+
+##### 聚合
+
+user
+
+```java
+class User{
+    
+    //对密码hash加密
+    String makeHashedPassword(){};
+    
+    //使用auth0的java-jwt库来生成jwt
+    String generateToken() {};
+    
+    //解密token获取Id
+    int verifyTokenGetUserId(){};
+}
+```
+
+
+
+### 接口设计
+
+```java
+  User createUser(User user);
+
+  User findUser(int id);
+
+  void removeUser(int id);
+
+  boolean updatePassword(int id,String oldPassword,String newPassword)
+
+  //登录,签发token
+  User signin(User user);
+
+  //校验token
+  User verifyToken(String token);
+```
+
diff --git a/houserush/login/pom.xml b/houserush/login/pom.xml
index a1965ab..5d32776 100644
--- a/houserush/login/pom.xml
+++ b/houserush/login/pom.xml
@@ -61,6 +61,11 @@
       <groupId>org.postgresql</groupId>
       <artifactId>postgresql</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.auth0</groupId>
+      <artifactId>java-jwt</artifactId>
+      <version>3.8.1</version>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/LoginApplication.java b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/LoginApplication.java
new file mode 100755
index 0000000..be12850
--- /dev/null
+++ b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/LoginApplication.java
@@ -0,0 +1,44 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.login;
+
+import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory;
+import org.apache.servicecomb.springboot.starter.provider.EnableServiceComb;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+@SpringBootApplication
+@EnableServiceComb
+@EnableJpaAuditing
+public class LoginApplication {
+
+  public static void main(String[] args) {
+    configBeforeBoot();
+    SpringApplication.run(LoginApplication.class, args);
+  }
+
+  private static void configBeforeBoot() {
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+    simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
+    RestObjectMapperFactory.getRestObjectMapper().setDateFormat(simpleDateFormat);
+  }
+}
diff --git a/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/LoginConfig.java b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/LoginConfig.java
new file mode 100755
index 0000000..03bc4a7
--- /dev/null
+++ b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/LoginConfig.java
@@ -0,0 +1,26 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.login;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+@Configuration
+@EnableJpaRepositories(basePackages = "org.apache.servicecomb.samples.practise.houserush")
+public class LoginConfig {
+}
diff --git a/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/aggregate/User.java b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/aggregate/User.java
new file mode 100755
index 0000000..c36db7c
--- /dev/null
+++ b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/aggregate/User.java
@@ -0,0 +1,117 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.login.aggregate;
+
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.JWTVerifier;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+import org.apache.commons.lang.StringUtils;
+import org.hibernate.annotations.SQLDelete;
+import org.hibernate.annotations.Where;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.persistence.*;
+import java.util.Base64;
+import java.util.Calendar;
+import java.util.Date;
+
+@Data
+@Entity
+@Table(name = "users")
+@SQLDelete(sql = "update users set deleted_at = now() where id = ?")
+@Where(clause = "deleted_at is null")
+@EntityListeners(AuditingEntityListener.class)
+public class User {
+  private final static String USER_SECRET = "231sdfqwer21313123cafkhioerutieweirqwuqbjffbqwrwr3";
+  private final static String HASH_TYPE = "HmacSHA256";
+  @Id
+  @GeneratedValue(strategy = GenerationType.AUTO)
+  private Integer id;
+
+  private String username;
+
+  @Transient
+  private String password;
+
+  @Transient
+  private String oldPassword;
+
+  @JsonIgnore
+  private String hashedPassword;
+
+  @Temporal(TemporalType.TIMESTAMP)
+  private Date deletedAt;
+
+  @CreatedDate
+  @Temporal(TemporalType.TIMESTAMP)
+  private Date createdAt;
+
+  @LastModifiedDate
+  @Temporal(TemporalType.TIMESTAMP)
+  private Date updatedAt;
+
+  @Transient
+  private String token;
+
+
+  public String makeHashedPassword(String password) {
+    try {
+      String data = username + password;
+      SecretKey secretKey = new SecretKeySpec(USER_SECRET.getBytes(), HASH_TYPE);
+      Mac mac = Mac.getInstance(HASH_TYPE);
+      mac.init(secretKey);
+      byte[] bytes = mac.doFinal(data.getBytes());
+      return new String(Base64.getEncoder().encode(bytes));
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public String generateToken() {
+    Calendar calendar = Calendar.getInstance();
+    calendar.add(Calendar.MINUTE, 30);
+    Algorithm algorithm = Algorithm.HMAC256(USER_SECRET);
+    token = JWT.create().withSubject(String.valueOf(id)).withExpiresAt(calendar.getTime()).sign(algorithm);
+    return token;
+  }
+
+  private static Algorithm algorithm = null;
+  private static JWTVerifier verifier = null;
+
+  {
+    algorithm = Algorithm.HMAC256(USER_SECRET);
+    verifier = JWT.require(algorithm)
+        .build();
+  }
+
+  public static int verifyTokenGetUserId(String token) {
+    String sub = verifier.verify(token).getSubject();
+    if (StringUtils.isNotBlank(sub)) {
+      return Integer.parseInt(sub);
+    }
+    throw new RuntimeException("verify the token fails");
+  }
+}
diff --git a/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/api/UserApi.java b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/api/UserApi.java
new file mode 100755
index 0000000..c64d0f9
--- /dev/null
+++ b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/api/UserApi.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.servicecomb.samples.practise.houserush.login.api;
+
+import org.apache.servicecomb.samples.practise.houserush.login.aggregate.User;
+
+public interface UserApi {
+  User createUser(User user);
+
+  User findUser(int id);
+
+  void removeUser(int id);
+
+  User signin(User user);
+
+  User verifyToken(String token);
+
+  boolean updatePassword(int customerId, User user);
+}
diff --git a/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/api/UserApiRestImpl.java b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/api/UserApiRestImpl.java
new file mode 100755
index 0000000..25203d0
--- /dev/null
+++ b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/api/UserApiRestImpl.java
@@ -0,0 +1,61 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.login.api;
+
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.apache.servicecomb.samples.practise.houserush.login.aggregate.User;
+import org.apache.servicecomb.samples.practise.houserush.login.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestSchema(schemaId = "userApiRest")
+@RequestMapping("/")
+public class UserApiRestImpl implements UserApi {
+  @Autowired
+  UserService userService;
+
+  @PostMapping("users")
+  public User createUser(@RequestBody User user) {
+    return userService.createUser(user);
+  }
+
+  @GetMapping("users/{id}")
+  public User findUser(@PathVariable int id) {
+    return userService.findUser(id);
+  }
+
+  @DeleteMapping("users/{id}")
+  public void removeUser(@PathVariable int id) {
+    userService.removeUser(id);
+  }
+
+  @PutMapping("users/signin")
+  public User signin(@RequestBody User user) {
+    return userService.signin(user);
+  }
+
+  @PutMapping("users/verify_token")
+  public User verifyToken(@RequestParam String token) {
+    return userService.verifyToken(token);
+  }
+
+  @PutMapping("users/password")
+  public boolean updatePassword(@RequestHeader int customerId, @RequestBody User user) {
+    return userService.updatePassword(customerId, user.getOldPassword(), user.getPassword());
+  }
+}
diff --git a/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/dao/UserDao.java b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/dao/UserDao.java
new file mode 100755
index 0000000..a13807e
--- /dev/null
+++ b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/dao/UserDao.java
@@ -0,0 +1,25 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.login.dao;
+
+import org.apache.servicecomb.samples.practise.houserush.login.aggregate.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface UserDao extends JpaRepository<User, Integer> {
+  User findByUsername(String username);
+}
diff --git a/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/service/UserService.java b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/service/UserService.java
new file mode 100755
index 0000000..cec944c
--- /dev/null
+++ b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/service/UserService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.login.service;
+
+import org.apache.servicecomb.samples.practise.houserush.login.aggregate.User;
+
+public interface UserService {
+  User createUser(User user);
+
+  User findUser(int id);
+
+  User updateUser(User user);
+
+  void removeUser(int id);
+
+  User signin(User user);
+
+  User verifyToken(String token);
+
+  boolean updatePassword(int id, String oldPassword, String newPassword);
+}
diff --git a/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/service/UserServiceImpl.java b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/service/UserServiceImpl.java
new file mode 100755
index 0000000..409fe94
--- /dev/null
+++ b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/service/UserServiceImpl.java
@@ -0,0 +1,93 @@
+/*
+ * 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.servicecomb.samples.practise.houserush.login.service;
+
+import org.apache.servicecomb.samples.practise.houserush.login.aggregate.User;
+import org.apache.servicecomb.samples.practise.houserush.login.dao.UserDao;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataRetrievalFailureException;
+import org.springframework.stereotype.Service;
+
+@Service
+public class UserServiceImpl implements UserService {
+
+  @Autowired
+  UserDao userDao;
+
+  public User createUser(User user) {
+    if (userDao.findByUsername(user.getUsername()) != null) {
+      throw new InvocationException(400, "", "用户名已存在");
+    }
+    String hashedPassword = user.makeHashedPassword(user.getPassword());
+    user.setHashedPassword(hashedPassword);
+    return userDao.save(user);
+  }
+
+
+  public User findUser(int id) {
+    return userDao.findOne(id);
+  }
+
+  public User updateUser(User user) {
+    int id = user.getId();
+    if (userDao.exists(id)) {
+      return userDao.save(user);
+    } else {
+      throw new DataRetrievalFailureException("cannot update non-existed user");
+    }
+  }
+
+  public void removeUser(int id) {
+    userDao.delete(id);
+  }
+
+  public User signin(User user) {
+    String username = user.getUsername();
+    String password = user.getPassword();
+    user = userDao.findByUsername(username);
+    if (user != null && password != null) {
+      if (user.getHashedPassword().equals(user.makeHashedPassword(password))) {
+        user.generateToken();
+        return user;
+      }
+    }
+    return null;
+  }
+
+  public User verifyToken(String token) {
+    int userId = User.verifyTokenGetUserId(token);
+    User user = userDao.findOne(userId);
+    user.generateToken();
+    return user;
+  }
+
+  @Override
+  public boolean updatePassword(int id, String oldPassword, String newPassword) {
+    User user = userDao.findOne(id);
+    if (user == null) {
+      throw new InvocationException(400, "", "user not existed");
+    }
+    if (!user.getHashedPassword().equals(user.makeHashedPassword(oldPassword))) {
+      throw new InvocationException(400, "", "The password is incorrect");
+    }
+    user.setHashedPassword(user.makeHashedPassword(newPassword));
+    userDao.save(user);
+    return true;
+  }
+}
diff --git a/houserush/login/src/main/resources/microservice.yaml b/houserush/login/src/main/resources/microservice.yaml
new file mode 100755
index 0000000..e8e6cd2
--- /dev/null
+++ b/houserush/login/src/main/resources/microservice.yaml
@@ -0,0 +1,43 @@
+#
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+# all interconnected microservices must belong to an application wth the same ID
+APPLICATION_ID: houserush
+service_description:
+  # name of the declaring microservice
+  name: login
+  version: 0.0.10
+servicecomb:
+  service:
+    registry:
+      address: http://127.0.0.1:30100
+  rest:
+    address: 0.0.0.0:6777
+  handler:
+    chain:
+      Provider:
+        default: bizkeeper-provider
+spring:
+  datasource:
+    url: jdbc:mysql://127.0.0.1:3306/login?characterEncoding=utf8&useSSL=false
+    username: root
+    password: root
+  jpa:
+    properties:
+      hibernate:
+        enable_lazy_load_no_trans: true
\ No newline at end of file