Merge pull request #14 from linzb0123/houserush-sample
Add the two of 6 micro-services: login and gateway
diff --git a/houserush/doc/images/ApiGateway.png b/houserush/doc/images/ApiGateway.png
new file mode 100755
index 0000000..9db08d1
--- /dev/null
+++ b/houserush/doc/images/ApiGateway.png
Binary files differ
diff --git a/houserush/gateway/README.md b/houserush/gateway/README.md
new file mode 100755
index 0000000..66c595f
--- /dev/null
+++ b/houserush/gateway/README.md
@@ -0,0 +1,51 @@
+## 微服务 gateway
+
+HouseRush应用网关,作为前端与后端通信的统一入口,为房源管理、订单中心等微服务提供路由和认证鉴权的功能
+
+### 主要功能
+
+- API入口
+- 动态路由
+- 鉴权
+
+### 设计原理
+- 使用[zuul](https://github.com/Netflix/zuul/wiki)来设计实现API网关功能
+- [使用zuul做边缘服务](https://docs.servicecomb.io/java-chassis/zh_CN/edge/zuul.html)
+![API gateway工作流程](../doc/images/ApiGateway.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是否有效
+ //...
+ }
+
+
+ }
+ ```
\ 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..f2230e2
--- /dev/null
+++ b/houserush/gateway/src/main/java/org/apache/servicecomb/samples/practise/houserush/gateway/filter/AuthorizeFilter.java
@@ -0,0 +1,119 @@
+/*
+ * 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.http.HttpStatus;
+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 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(HttpStatus.SC_FORBIDDEN, "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);
+ User resultUser = userApi.signin(user);
+ if (resultUser != null && resultUser.getToken() != null) {
+ sendResponse(HttpStatus.SC_OK, "{\"token\": \"" + resultUser.getToken() + "\"}");
+ } else {
+ sendResponse(HttpStatus.SC_UNAUTHORIZED, "cannot sign in!");
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ sendResponse(HttpStatus.SC_UNAUTHORIZED, e.getMessage());
+ }
+ }
+ return null;
+ } else {
+ sendResponse(HttpStatus.SC_UNAUTHORIZED, "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..3416bb3 100644
--- a/houserush/login/pom.xml
+++ b/houserush/login/pom.xml
@@ -61,6 +61,16 @@
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.auth0</groupId>
+ <artifactId>java-jwt</artifactId>
+ <version>3.8.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </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..8b2793b
--- /dev/null
+++ b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/aggregate/User.java
@@ -0,0 +1,118 @@
+/*
+ * 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 {
+ // this is secret key,you can changed it to what you want
+ 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..79c0fb2
--- /dev/null
+++ b/houserush/login/src/main/java/org/apache/servicecomb/samples/practise/houserush/login/service/UserServiceImpl.java
@@ -0,0 +1,102 @@
+/*
+ * 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 com.auth0.jwt.exceptions.TokenExpiredException;
+import org.apache.http.HttpStatus;
+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(HttpStatus.SC_BAD_REQUEST, "", "user already exists");
+ }
+ 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;
+ try {
+ userId = User.verifyTokenGetUserId(token);
+ } catch (TokenExpiredException e) {
+ throw new InvocationException(HttpStatus.SC_BAD_REQUEST, "", "token has expired");
+ } catch (Exception e) {
+ throw new InvocationException(HttpStatus.SC_BAD_REQUEST, "", "decode token fail");
+ }
+ 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(HttpStatus.SC_BAD_REQUEST, "", "user not existed");
+ }
+ if (!user.getHashedPassword().equals(user.makeHashedPassword(oldPassword))) {
+ throw new InvocationException(HttpStatus.SC_BAD_REQUEST, "", "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
diff --git a/houserush/login/src/test/java/org/apache/servicecomb/samples/practise/houserush/login/service/UserServiceTest.java b/houserush/login/src/test/java/org/apache/servicecomb/samples/practise/houserush/login/service/UserServiceTest.java
new file mode 100644
index 0000000..8703ae7
--- /dev/null
+++ b/houserush/login/src/test/java/org/apache/servicecomb/samples/practise/houserush/login/service/UserServiceTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+@SpringBootTest
+public class UserServiceTest {
+
+ @InjectMocks
+ private UserService userService = new UserServiceImpl();
+
+ @Mock
+ private UserDao userDao;
+
+ @Test
+ public void testCreate() {
+ //create a user
+ User user = new User();
+ user.setUsername("root");
+ user.setPassword("123456");
+ when(userDao.save(any(User.class))).then(invocation -> {
+ User user0 = (User) invocation.getArguments()[0];
+ user0.setId(10);
+ return user0;
+ });
+ when(userDao.findByUsername("root")).thenReturn(null).thenReturn(user);
+
+ //first create
+ User newUser = new User();
+ newUser.setUsername("root");
+ newUser.setPassword("123456");
+ user = userService.createUser(newUser);
+ assertNotNull(user);
+
+ //repeat create
+ User newUser2 = new User();
+ newUser2.setUsername("root");
+ newUser2.setPassword("123456");
+ try {
+ userService.createUser(newUser2);
+ fail("expect to occur an InvocationException but not");
+ } catch (InvocationException e) {
+ assertEquals("InvocationException: code=400;msg=user already exists", e.getMessage());
+ }
+
+ }
+
+ @Test
+ public void testUpdatePassword() {
+ User user = new User();
+ user.setUsername("root");
+ user.setHashedPassword(user.makeHashedPassword("123456"));
+ user.setId(10);
+ when(userDao.findOne(10)).thenReturn(null).thenReturn(user);
+
+ //user not existed
+ try {
+ userService.updatePassword(10, "123456", "123456789");
+ fail("expect to occur an InvocationException but not");
+ } catch (InvocationException e) {
+ assertEquals("InvocationException: code=400;msg=user not existed", e.getMessage());
+ }
+ //password is incorrect
+ try {
+ userService.updatePassword(10, "12345", "123456789");
+ fail("expect to occur an InvocationException but not");
+ } catch (InvocationException e) {
+ assertEquals("InvocationException: code=400;msg=The password is incorrect", e.getMessage());
+ }
+ boolean success = userService.updatePassword(10, "123456", "123456789");
+ assertTrue(success);
+
+ }
+
+ @Test
+ public void testSigninAndVerifyToken() {
+ //create a user
+ User user = new User();
+ user.setUsername("root");
+ user.setId(10);
+ user.setHashedPassword(user.makeHashedPassword("root"));
+ when(userDao.findByUsername("root")).thenReturn(user);
+ when(userDao.findOne(10)).thenReturn(user);
+
+ //login success
+ User user1 = new User();
+ user1.setUsername("root");
+ user1.setPassword("root");
+ user = userService.signin(user1);
+ assertNotNull(user);
+ assertNotNull(user.getToken());
+ //verify success
+ String token = user.getToken();
+ user = userService.verifyToken(token);
+ assertThat(user.getId(), is(10));
+ //token expired
+ try {
+ user = userService.verifyToken("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI3NSIsImV4cCI6MTU2NTE2MzU4MH0.y1vz0mus91c9fje6GHzyzWQxlUA3h8eCg7za_kiATdg");
+ fail("expect to occur an InvocationException but not");
+ } catch (Exception e) {
+ assertEquals("InvocationException: code=400;msg=token has expired", e.getMessage());
+ }
+ //invalid token expired
+ try {
+ user = userService.verifyToken("invalid token");
+ fail("expect to occur an InvocationException but not");
+ } catch (Exception e) {
+ assertEquals("InvocationException: code=400;msg=decode token fail", e.getMessage());
+ }
+ //login fail password incorrect
+ User user2 = new User();
+ user2.setUsername("root");
+ user2.setPassword("root2");
+ user = userService.signin(user2);
+ assertNull(user);
+
+ //login fail username not existed
+ User user3 = new User();
+ user3.setUsername("root3");
+ user3.setPassword("root");
+ user = userService.signin(user2);
+ assertNull(user);
+ }
+
+}
\ No newline at end of file