├── README.md ├── TEST_MSA_no_configuration.md ├── actuator-demo ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── demo │ │ │ ├── DemoApplication.java │ │ │ └── DemoController.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── demo │ └── DemoApplicationTests.java ├── apigateway-service-no-config ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── apigatewayservice │ │ │ ├── ApigatewayServiceApplication.java │ │ │ ├── config │ │ │ └── FilterConfig.java │ │ │ └── filter │ │ │ ├── AuthorizationHeaderFilter.java │ │ │ ├── CustomFilter.java │ │ │ ├── GlobalFilter.java │ │ │ └── LoggingFilter.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── apigatewayservice │ └── ApigatewayServiceApplicationTests.java ├── apigateway-service ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── apigatewayservice │ │ │ ├── ApigatewayServiceApplication.java │ │ │ ├── config │ │ │ └── FilterConfig.java │ │ │ └── filter │ │ │ ├── AuthorizationHeaderFilter.java │ │ │ ├── CustomFilter.java │ │ │ ├── GlobalFilter.java │ │ │ └── LoggingFilter.java │ └── resources │ │ ├── application.yml │ │ └── bootstrap.yml │ └── test │ └── java │ └── com │ └── example │ └── apigatewayservice │ └── ApigatewayServiceApplicationTests.java ├── catalog-service ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── catalogservice │ │ │ ├── CatalogServiceApplication.java │ │ │ ├── controller │ │ │ └── CatalogController.java │ │ │ ├── dto │ │ │ └── CatalogDto.java │ │ │ ├── jpa │ │ │ ├── CatalogEntity.java │ │ │ └── CatalogRepository.java │ │ │ ├── messagequeue │ │ │ ├── KafkaConsumer.java │ │ │ └── KafkaConsumerConfig.java │ │ │ ├── service │ │ │ ├── CatalogService.java │ │ │ └── CatalogServiceImpl.java │ │ │ └── vo │ │ │ └── ResponseCatalog.java │ └── resources │ │ ├── application.yml │ │ └── data.sql │ └── test │ └── java │ └── com │ └── example │ └── catalogservice │ └── CatalogServiceApplicationTests.java ├── config-service ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── apiEncryptionKey.jks ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── configservice │ │ │ └── ConfigServiceApplication.java │ └── resources │ │ ├── application.yml │ │ └── bootstrap.yml │ └── test │ └── java │ └── com │ └── example │ └── configservice │ └── ConfigServiceApplicationTests.java ├── cookie-demo ├── index.js └── package.json ├── discoveryservice ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── discoveryservice │ │ │ └── DiscoveryserviceApplication.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── discoveryservice │ └── DiscoveryserviceApplicationTests.java ├── docker-files ├── Dockerfile ├── docker-compose.yml └── mysql_data │ └── mysql │ └── mydb │ ├── db.opt │ ├── hibernate_sequence.frm │ ├── hibernate_sequence.ibd │ ├── orders.frm │ └── orders.ibd ├── eclipse_lombok.pdf ├── first-service ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── first-service-demo.zip ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── firstservice │ │ │ ├── CustomizationPort.java │ │ │ ├── FirstServiceApplication.java │ │ │ └── FirstServiceController.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── firstservice │ └── FirstServiceApplicationTests.java ├── order-service ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── mem │ ├── testdb.h2.db │ └── testdb.trace.db ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── orderservice │ │ │ ├── OrderServiceApplication.java │ │ │ ├── controller │ │ │ └── OrderController.java │ │ │ ├── dto │ │ │ ├── Field.java │ │ │ ├── KafkaOrderDto.java │ │ │ ├── OrderDto.java │ │ │ ├── Payload.java │ │ │ └── Schema.java │ │ │ ├── jpa │ │ │ ├── OrderEntity.java │ │ │ └── OrderRepository.java │ │ │ ├── messagequeue │ │ │ ├── KafkaProducer.java │ │ │ ├── KafkaProducerConfig.java │ │ │ └── OrderProducer.java │ │ │ ├── service │ │ │ ├── OrderService.java │ │ │ └── OrderServiceImpl.java │ │ │ └── vo │ │ │ ├── RequestOrder.java │ │ │ └── ResponseOrder.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── orderservice │ └── OrderServiceApplicationTests.java ├── pdf ├── Section 0. Microservice와 Spring Cloud 소개.pdf ├── Section 1. Service Discovery.pdf ├── Section 2. API Gateway Service.pdf ├── Section 3. E-commerce 애플리케이션.pdf ├── Section 4. Users Microservice-part1.pdf ├── Section 5. Catalogs, Orders Microservice.pdf ├── Section 6. Users Microservice-part2.pdf ├── Section 7. Spring Config Server.pdf ├── Section 8. Spring Cloud Bus.pdf ├── Section 9. Encryption and Decryption.pdf ├── Section10. 마이크로서비스간 통신.pdf ├── Section11. Kafka를 이용한 데이터동기화-part1.pdf ├── Section12. Kafka를 이용한 데이터동기화-part2.pdf ├── Section13. Resilience4J_Trace.pdf ├── Section14. Monitoring.pdf ├── Section15. Docker.pdf └── Section16. Deployment.pdf ├── second-service ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── secondservice │ │ │ ├── SecondServiceApplication.java │ │ │ └── SecondServiceController.java │ └── resources │ │ ├── application.properties │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── secondservice │ └── SecondServiceApplicationTests.java ├── user-service-no-config ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── userservice │ │ │ ├── EncryptDemo.java │ │ │ ├── UserServiceApplication.java │ │ │ ├── client │ │ │ └── OrderServiceClient.java │ │ │ ├── config │ │ │ ├── CustomContainer.java │ │ │ └── Resilience4JConfig.java │ │ │ ├── controller │ │ │ └── UserController.java │ │ │ ├── dto │ │ │ └── UserDto.java │ │ │ ├── error │ │ │ └── FeignErrorDecoder.java │ │ │ ├── jpa │ │ │ ├── UserEntity.java │ │ │ └── UserRepository.java │ │ │ ├── security │ │ │ ├── AuthenticationFilter.java │ │ │ └── WebSecurity.java │ │ │ ├── service │ │ │ ├── UserService.java │ │ │ └── UserServiceImpl.java │ │ │ └── vo │ │ │ ├── Greeting.java │ │ │ ├── RequestLogin.java │ │ │ ├── RequestUser.java │ │ │ ├── ResponseOrder.java │ │ │ └── ResponseUser.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── userservice │ └── UserServiceApplicationTests.java ├── user-service-no-security ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── userservice │ │ │ ├── UserServiceApplication.java │ │ │ ├── controller │ │ │ └── UserController.java │ │ │ ├── dto │ │ │ └── UserDto.java │ │ │ ├── jpa │ │ │ ├── UserEntity.java │ │ │ └── UserRepository.java │ │ │ ├── security │ │ │ └── WebSecurity.java │ │ │ ├── service │ │ │ ├── UserService.java │ │ │ └── UserServiceImpl.java │ │ │ └── vo │ │ │ ├── Greeting.java │ │ │ ├── RequestLogin.java │ │ │ ├── RequestUser.java │ │ │ ├── ResponseOrder.java │ │ │ └── ResponseUser.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── userservice │ └── UserServiceApplicationTests.java ├── user-service ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── Dockerfile ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── userservice │ │ │ ├── EncryptDemo.java │ │ │ ├── UserServiceApplication.java │ │ │ ├── client │ │ │ └── OrderServiceClient.java │ │ │ ├── config │ │ │ ├── CustomContainer.java │ │ │ └── Resilience4JConfig.java │ │ │ ├── controller │ │ │ └── UserController.java │ │ │ ├── dto │ │ │ └── UserDto.java │ │ │ ├── error │ │ │ └── FeignErrorDecoder.java │ │ │ ├── jpa │ │ │ ├── UserEntity.java │ │ │ └── UserRepository.java │ │ │ ├── security │ │ │ ├── AuthenticationFilter.java │ │ │ └── WebSecurity.java │ │ │ ├── service │ │ │ ├── UserService.java │ │ │ └── UserServiceImpl.java │ │ │ └── vo │ │ │ ├── Greeting.java │ │ │ ├── RequestLogin.java │ │ │ ├── RequestUser.java │ │ │ ├── ResponseOrder.java │ │ │ └── ResponseUser.java │ └── resources │ │ ├── application.yml │ │ └── bootstrap.yml │ └── test │ └── java │ └── com │ └── example │ └── userservice │ └── UserServiceApplicationTests.java └── zuul-service ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── example │ │ └── zuulservice │ │ ├── ZuulServiceApplication.java │ │ └── filter │ │ └── ZuulLoggingFilter.java └── resources │ └── application.yml └── test └── java └── com └── example └── zuulservice └── ZuulServiceApplicationTests.java /README.md: -------------------------------------------------------------------------------- 1 | # msa_with_spring_cloud 2 | 3 | ### Configuration + Service discovery + Api gateway 4 | 1. config-service - Configuration service 5 | 2. discoveryservice - Service discovery 6 | 3. apigateway-service - Spring Cloud Gateway 7 | 4. zuul-service - Spring Cloud Netflix Zuul 8 | 9 | ### Sample codes 10 | 5. first-service - example for apigateway service 11 | 6. second-service - example for apigateway service 12 | 13 | ### E-commerce codes 14 | 7. catalog-service 15 | 8. order-service 16 | 9. user-service 17 | 18 | ### Docker files 19 | 10. docker-files 20 | -------------------------------------------------------------------------------- /TEST_MSA_no_configuration.md: -------------------------------------------------------------------------------- 1 | ### Service 기동 2 | 1. eurkea server 3 | 2. apigateway 4 | - spring cloud bus 비활성 5 | - bootstrap.yml 삭제 (나중에 추가) 6 | - routes 확인 7 | 3. user-service 8 | - spring cloud bus 비활성 9 | - bootstrap.yml 삭제 (나중에 추가) 10 | - datasource 주석 해제 11 | - application.yml 파일에 12 | - gateway.ip 등록 13 | 4. order-service 14 | 5. catalog-service 15 | 16 | ### API TEST 17 | 1. 회원가입 18 | ``` 19 | POST http://ip_address:8000/user-service/users 20 | { 21 | "email": "edowon0622@gmail.com", 22 | "name": "Dowon Lee", 23 | "pwd": "1234" 24 | } 25 | ``` 26 | 2. 로그인 27 | ``` 28 | POST http://ip_address:8000/user-service/login 29 | { 30 | "email": "edowon0622@gmail.com", 31 | "password": "1234" 32 | } 33 | ``` 34 | 3. 상품목록 확인 35 | ``` 36 | GET http://ip_address:8000/catalog-service/catalogs 37 | ``` 38 | 4. 상품 주문 39 | ``` 40 | POST http://ip_address:8000/uorderser-service/orders 41 | { 42 | "productId": "CATALOG-001", 43 | "qty": 10, 44 | "unitPrice": 1500 45 | } 46 | ``` 47 | 5. 사용자 전체 목록 확인 (token 필요) 48 | ``` 49 | POSTMAN -> Authorization -> Bearer Token 50 | GET http://ip_address:8000/user-service/users 51 | ``` 52 | 6. 사용자 상세 보기 (token 필요) 53 | ``` 54 | POSTMAN -> Authorization -> Bearer Token 55 | GET http://ip_address:8000/user-service/users/[userId] 56 | ``` 57 | -------------------------------------------------------------------------------- /actuator-demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /actuator-demo/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/actuator-demo/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /actuator-demo/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /actuator-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.4 9 | 10 | 11 | com.example 12 | demo 13 | 0.0.1-SNAPSHOT 14 | demo 15 | Demo project for Spring Boot 16 | 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-tomcat 28 | provided 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-actuator 39 | 2.4.4 40 | 41 | 42 | 43 | org.springdoc 44 | springdoc-openapi-ui 45 | 1.5.6 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-maven-plugin 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /actuator-demo/src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /actuator-demo/src/main/java/com/example/demo/DemoController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.web.bind.annotation.RequestMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class DemoController { 8 | @RequestMapping("/") 9 | public String home() { 10 | return "Hello World!"; 11 | } 12 | } -------------------------------------------------------------------------------- /actuator-demo/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8088 3 | 4 | spring: 5 | application: 6 | name: demo-app 7 | 8 | management: 9 | endpoints: 10 | web: 11 | exposure: 12 | include: health,metrics 13 | -------------------------------------------------------------------------------- /actuator-demo/src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /apigateway-service-no-config/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /apigateway-service-no-config/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/apigateway-service-no-config/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /apigateway-service-no-config/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /apigateway-service-no-config/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17-ea-11-jdk-slim 2 | VOLUME /tmp 3 | COPY target/apigateway-service-1.0.jar ApigatewayService.jar 4 | ENTRYPOINT ["java", "-jar", "ApigatewayService.jar"] -------------------------------------------------------------------------------- /apigateway-service-no-config/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.2 9 | 10 | 11 | com.example 12 | apigateway-service 13 | 1.0 14 | apigateway-service 15 | Demo project for Spring Boot 16 | 17 | 11 18 | 2020.0.0 19 | 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-starter-gateway 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-starter-netflix-eureka-client 28 | 29 | 30 | 31 | org.projectlombok 32 | lombok 33 | true 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | test 39 | 40 | 41 | io.jsonwebtoken 42 | jjwt 43 | 0.9.1 44 | 45 | 46 | javax.xml.bind 47 | jaxb-api 48 | 49 | 50 | 51 | org.springframework.cloud 52 | spring-cloud-starter-config 53 | 54 | 55 | 56 | org.springframework.cloud 57 | spring-cloud-starter-bootstrap 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-actuator 63 | 64 | 65 | 66 | 71 | 72 | 73 | 74 | 75 | io.micrometer 76 | micrometer-registry-prometheus 77 | 78 | 79 | 80 | 81 | 82 | org.springframework.cloud 83 | spring-cloud-dependencies 84 | ${spring-cloud.version} 85 | pom 86 | import 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | org.springframework.boot 95 | spring-boot-maven-plugin 96 | 97 | 98 | 99 | org.projectlombok 100 | lombok 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | spring-milestones 110 | Spring Milestones 111 | https://repo.spring.io/milestone 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /apigateway-service-no-config/src/main/java/com/example/apigatewayservice/ApigatewayServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.actuate.trace.http.HttpTraceRepository; 5 | import org.springframework.boot.actuate.trace.http.InMemoryHttpTraceRepository; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | @SpringBootApplication 10 | public class ApigatewayServiceApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(ApigatewayServiceApplication.class, args); 14 | } 15 | 16 | @Bean 17 | public HttpTraceRepository httpTraceRepository() { 18 | return new InMemoryHttpTraceRepository(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apigateway-service-no-config/src/main/java/com/example/apigatewayservice/config/FilterConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice.config; 2 | 3 | import org.springframework.cloud.gateway.route.RouteLocator; 4 | import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | //@Configuration 9 | public class FilterConfig { 10 | // @Bean 11 | public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) { 12 | return builder.routes() 13 | .route(r -> r.path("/first-service/**") 14 | .filters(f -> f.addRequestHeader("first-request", "first-request-header") 15 | .addResponseHeader("first-response", "first-response-header")) 16 | .uri("http://localhost:8081")) 17 | .route(r -> r.path("/second-service/**") 18 | .filters(f -> f.addRequestHeader("second-request", "second-request-header") 19 | .addResponseHeader("second-response", "second-response-header")) 20 | .uri("http://localhost:8082")) 21 | .build(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apigateway-service-no-config/src/main/java/com/example/apigatewayservice/filter/AuthorizationHeaderFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice.filter; 2 | 3 | import com.google.common.net.HttpHeaders; 4 | import io.jsonwebtoken.Jwts; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.boot.web.servlet.server.Session; 7 | import org.springframework.cloud.gateway.filter.GatewayFilter; 8 | import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; 9 | import org.springframework.core.env.Environment; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseCookie; 12 | import org.springframework.http.server.reactive.ServerHttpRequest; 13 | import org.springframework.http.server.reactive.ServerHttpResponse; 14 | import org.springframework.stereotype.Component; 15 | import org.springframework.web.server.ServerWebExchange; 16 | import reactor.core.publisher.Mono; 17 | 18 | @Component 19 | @Slf4j 20 | public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory { 21 | Environment env; 22 | 23 | public AuthorizationHeaderFilter(Environment env) { 24 | super(Config.class); 25 | this.env = env; 26 | } 27 | 28 | public static class Config { 29 | // Put configuration properties here 30 | } 31 | 32 | @Override 33 | public GatewayFilter apply(Config config) { 34 | return (exchange, chain) -> { 35 | ServerHttpRequest request = exchange.getRequest(); 36 | 37 | if (!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)) { 38 | return onError(exchange, "No authorization header", HttpStatus.UNAUTHORIZED); 39 | } 40 | 41 | String authorizationHeader = request.getHeaders().get(HttpHeaders.AUTHORIZATION).get(0); 42 | String jwt = authorizationHeader.replace("Bearer", ""); 43 | 44 | // Create a cookie object 45 | // ServerHttpResponse response = exchange.getResponse(); 46 | // ResponseCookie c1 = ResponseCookie.from("my_token", "test1234").maxAge(60 * 60 * 24).build(); 47 | // response.addCookie(c1); 48 | 49 | if (!isJwtValid(jwt)) { 50 | return onError(exchange, "JWT token is not valid", HttpStatus.UNAUTHORIZED); 51 | } 52 | 53 | return chain.filter(exchange); 54 | }; 55 | } 56 | 57 | private Mono onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) { 58 | ServerHttpResponse response = exchange.getResponse(); 59 | response.setStatusCode(httpStatus); 60 | 61 | log.error(err); 62 | return response.setComplete(); 63 | } 64 | 65 | private boolean isJwtValid(String jwt) { 66 | boolean returnValue = true; 67 | 68 | String subject = null; 69 | 70 | try { 71 | subject = Jwts.parser().setSigningKey(env.getProperty("token.secret")) 72 | .parseClaimsJws(jwt).getBody() 73 | .getSubject(); 74 | } catch (Exception ex) { 75 | returnValue = false; 76 | } 77 | 78 | if (subject == null || subject.isEmpty()) { 79 | returnValue = false; 80 | } 81 | 82 | return returnValue; 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /apigateway-service-no-config/src/main/java/com/example/apigatewayservice/filter/CustomFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice.filter; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.cloud.gateway.filter.GatewayFilter; 5 | import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; 6 | import org.springframework.http.server.reactive.ServerHttpRequest; 7 | import org.springframework.http.server.reactive.ServerHttpResponse; 8 | import org.springframework.stereotype.Component; 9 | import reactor.core.publisher.Mono; 10 | 11 | @Component 12 | @Slf4j 13 | public class CustomFilter extends AbstractGatewayFilterFactory { 14 | public CustomFilter() { 15 | super(Config.class); 16 | } 17 | 18 | @Override 19 | public GatewayFilter apply(Config config) { 20 | // Custom Pre Filter 21 | return (exchange, chain) -> { 22 | ServerHttpRequest request = exchange.getRequest(); 23 | ServerHttpResponse response = exchange.getResponse(); 24 | 25 | log.info("Custom PRE filter: request id -> {}", request.getId()); 26 | 27 | // Custom Post Filter 28 | return chain.filter(exchange).then(Mono.fromRunnable(() -> { 29 | log.info("Custom POST filter: response code -> {}", response.getStatusCode()); 30 | })); 31 | }; 32 | } 33 | 34 | public static class Config { 35 | // Put the configuration properties 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /apigateway-service-no-config/src/main/java/com/example/apigatewayservice/filter/GlobalFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice.filter; 2 | 3 | import lombok.Data; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.cloud.gateway.filter.GatewayFilter; 6 | import org.springframework.cloud.gateway.filter.GatewayFilterChain; 7 | import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; 8 | import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; 9 | import org.springframework.cloud.gateway.support.ipresolver.RemoteAddressResolver; 10 | import org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver; 11 | import org.springframework.core.Ordered; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.server.reactive.ServerHttpRequest; 14 | import org.springframework.http.server.reactive.ServerHttpResponse; 15 | import org.springframework.stereotype.Component; 16 | import org.springframework.web.server.ServerWebExchange; 17 | import reactor.core.publisher.Mono; 18 | 19 | @Component 20 | @Slf4j 21 | public class GlobalFilter extends AbstractGatewayFilterFactory { 22 | public GlobalFilter() { 23 | super(Config.class); 24 | } 25 | 26 | @Override 27 | public GatewayFilter apply(Config config) { 28 | return ((exchange, chain) -> { 29 | ServerHttpRequest request = exchange.getRequest(); 30 | ServerHttpResponse response = exchange.getResponse(); 31 | 32 | log.info("Global Filter baseMessage: {}, {}", config.getBaseMessage(), request.getRemoteAddress()); 33 | if (config.isPreLogger()) { 34 | log.info("Global Filter Start: request id -> {}", request.getId()); 35 | } 36 | return chain.filter(exchange).then(Mono.fromRunnable(()->{ 37 | if (config.isPostLogger()) { 38 | log.info("Global Filter End: response code -> {}", response.getStatusCode()); 39 | } 40 | })); 41 | }); 42 | } 43 | 44 | @Data 45 | public static class Config { 46 | private String baseMessage; 47 | private boolean preLogger; 48 | private boolean postLogger; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /apigateway-service-no-config/src/main/java/com/example/apigatewayservice/filter/LoggingFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice.filter; 2 | 3 | import lombok.Data; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.cloud.gateway.filter.GatewayFilter; 6 | import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; 7 | import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; 8 | import org.springframework.core.Ordered; 9 | import org.springframework.http.server.reactive.ServerHttpRequest; 10 | import org.springframework.http.server.reactive.ServerHttpResponse; 11 | import org.springframework.stereotype.Component; 12 | import reactor.core.publisher.Mono; 13 | 14 | @Component 15 | @Slf4j 16 | public class LoggingFilter extends AbstractGatewayFilterFactory { 17 | public LoggingFilter() { 18 | super(Config.class); 19 | } 20 | 21 | @Override 22 | public GatewayFilter apply(Config config) { 23 | // return ((exchange, chain) -> { 24 | // ServerHttpRequest request = exchange.getRequest(); 25 | // ServerHttpResponse response = exchange.getResponse(); 26 | // 27 | // log.info("Global Filter baseMessage: {}", config.getBaseMessage()); 28 | // if (config.isPreLogger()) { 29 | // log.info("Global Filter Start: request id -> {}", request.getId()); 30 | // } 31 | // return chain.filter(exchange).then(Mono.fromRunnable(()->{ 32 | // if (config.isPostLogger()) { 33 | // log.info("Global Filter End: response code -> {}", response.getStatusCode()); 34 | // } 35 | // })); 36 | // }); 37 | GatewayFilter filter = new OrderedGatewayFilter((exchange, chain) -> { 38 | ServerHttpRequest request = exchange.getRequest(); 39 | ServerHttpResponse response = exchange.getResponse(); 40 | 41 | log.info("Logging Filter baseMessage: {}", config.getBaseMessage()); 42 | if (config.isPreLogger()) { 43 | log.info("Logging PRE Filter: request id -> {}", request.getId()); 44 | } 45 | return chain.filter(exchange).then(Mono.fromRunnable(()->{ 46 | if (config.isPostLogger()) { 47 | log.info("Logging POST Filter: response code -> {}", response.getStatusCode()); 48 | } 49 | })); 50 | }, Ordered.LOWEST_PRECEDENCE); 51 | 52 | return filter; 53 | } 54 | 55 | @Data 56 | public static class Config { 57 | private String baseMessage; 58 | private boolean preLogger; 59 | private boolean postLogger; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /apigateway-service-no-config/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8000 3 | 4 | eureka: 5 | client: 6 | register-with-eureka: true 7 | fetch-registry: true 8 | service-url: 9 | defaultZone: http://localhost:8761/eureka 10 | 11 | spring: 12 | application: 13 | name: apigateway-service 14 | 15 | cloud: 16 | gateway: 17 | default-filters: 18 | - name: GlobalFilter 19 | args: 20 | baseMessage: Spring Cloud Gateway Global Filter 21 | preLogger: true 22 | postLogger: true 23 | routes: 24 | # - id: user-service 25 | # uri: lb://USER-SERVICE 26 | # predicates: 27 | # - Path=/user-service/** 28 | - id: user-service 29 | uri: lb://USER-SERVICE 30 | predicates: 31 | - Path=/user-service/login 32 | - Method=POST 33 | filters: 34 | - RemoveRequestHeader=Cookie 35 | - RewritePath=/user-service/(?.*), /$\{segment} 36 | - id: user-service 37 | uri: lb://USER-SERVICE 38 | predicates: 39 | - Path=/user-service/users 40 | - Method=POST 41 | filters: 42 | - RemoveRequestHeader=Cookie 43 | - RewritePath=/user-service/(?.*), /$\{segment} 44 | - id: user-service 45 | uri: lb://USER-SERVICE 46 | predicates: 47 | - Path=/user-service/actuator/** 48 | - Method=GET,POST 49 | filters: 50 | - RemoveRequestHeader=Cookie 51 | - RewritePath=/user-service/(?.*), /$\{segment} 52 | - id: user-service 53 | uri: lb://USER-SERVICE 54 | predicates: 55 | - Path=/user-service/** 56 | - Method=GET 57 | filters: 58 | - RemoveRequestHeader=Cookie 59 | - RewritePath=/user-service/(?.*), /$\{segment} 60 | - AuthorizationHeaderFilter 61 | - id: catalog-service 62 | uri: lb://CATALOG-SERVICE 63 | predicates: 64 | - Path=/catalog-service/** 65 | - id: order-service 66 | uri: lb://ORDER-SERVICE 67 | predicates: 68 | - Path=/order-service/actuator/** 69 | - Method=GET,POST 70 | filters: 71 | - RemoveRequestHeader=Cookie 72 | - RewritePath=/order-service/(?.*), /$\{segment} 73 | - id: order-service 74 | uri: lb://ORDER-SERVICE 75 | predicates: 76 | - Path=/order-service/** 77 | - id: first-service 78 | uri: lb://MY-FIRST-SERVICE 79 | predicates: 80 | - Path=/first-service/** 81 | filters: 82 | - AddRequestHeader=first-request, first-request-header2 83 | - AddResponseHeader=first-response, first-response-header2 84 | # - CustomFilter 85 | - id: second-service 86 | uri: lb://MY-SECOND-SERVICE 87 | predicates: 88 | - Path=/second-service/** 89 | filters: 90 | - AddRequestHeader=second-request, second-request-header2 91 | - AddResponseHeader=second-response, second-response-header2 92 | # - name: CustomFilter 93 | - name: LoggingFilter 94 | args: 95 | baseMessage: Hi, there. 96 | preLogger: true 97 | postLogger: true 98 | 99 | token: 100 | expiration_time: 86400000 101 | secret: user_token 102 | 103 | management: 104 | endpoints: 105 | web: 106 | exposure: 107 | include: refresh, health, beans, httptrace, busrefresh, info, metrics, prometheus -------------------------------------------------------------------------------- /apigateway-service-no-config/src/test/java/com/example/apigatewayservice/ApigatewayServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ApigatewayServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /apigateway-service/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /apigateway-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/apigateway-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /apigateway-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /apigateway-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17-ea-11-jdk-slim 2 | VOLUME /tmp 3 | COPY target/apigateway-service-1.0.jar ApigatewayService.jar 4 | ENTRYPOINT ["java", "-jar", "ApigatewayService.jar"] -------------------------------------------------------------------------------- /apigateway-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.2 9 | 10 | 11 | com.example 12 | apigateway-service 13 | 1.0 14 | apigateway-service 15 | Demo project for Spring Boot 16 | 17 | 11 18 | 2020.0.0 19 | 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-starter-gateway 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-starter-netflix-eureka-client 28 | 29 | 30 | 31 | org.projectlombok 32 | lombok 33 | true 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | test 39 | 40 | 41 | io.jsonwebtoken 42 | jjwt 43 | 0.9.1 44 | 45 | 46 | javax.xml.bind 47 | jaxb-api 48 | 49 | 50 | 51 | org.springframework.cloud 52 | spring-cloud-starter-config 53 | 54 | 55 | 56 | org.springframework.cloud 57 | spring-cloud-starter-bootstrap 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-actuator 63 | 64 | 65 | 66 | org.springframework.cloud 67 | spring-cloud-starter-bus-amqp 68 | 69 | 70 | 71 | io.micrometer 72 | micrometer-registry-prometheus 73 | 74 | 75 | 76 | 77 | 78 | org.springframework.cloud 79 | spring-cloud-dependencies 80 | ${spring-cloud.version} 81 | pom 82 | import 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | org.springframework.boot 91 | spring-boot-maven-plugin 92 | 93 | 94 | 95 | org.projectlombok 96 | lombok 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | spring-milestones 106 | Spring Milestones 107 | https://repo.spring.io/milestone 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /apigateway-service/src/main/java/com/example/apigatewayservice/ApigatewayServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.actuate.trace.http.HttpTraceRepository; 5 | import org.springframework.boot.actuate.trace.http.InMemoryHttpTraceRepository; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | @SpringBootApplication 10 | public class ApigatewayServiceApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(ApigatewayServiceApplication.class, args); 14 | } 15 | 16 | @Bean 17 | public HttpTraceRepository httpTraceRepository() { 18 | return new InMemoryHttpTraceRepository(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apigateway-service/src/main/java/com/example/apigatewayservice/config/FilterConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice.config; 2 | 3 | import org.springframework.cloud.gateway.route.RouteLocator; 4 | import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | //@Configuration 9 | public class FilterConfig { 10 | // @Bean 11 | public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) { 12 | return builder.routes() 13 | .route(r -> r.path("/first-service/**") 14 | .filters(f -> f.addRequestHeader("first-request", "first-request-header") 15 | .addResponseHeader("first-response", "first-response-header")) 16 | .uri("http://localhost:8081")) 17 | .route(r -> r.path("/second-service/**") 18 | .filters(f -> f.addRequestHeader("second-request", "second-request-header") 19 | .addResponseHeader("second-response", "second-response-header")) 20 | .uri("http://localhost:8082")) 21 | .build(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apigateway-service/src/main/java/com/example/apigatewayservice/filter/AuthorizationHeaderFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice.filter; 2 | 3 | import com.google.common.net.HttpHeaders; 4 | import io.jsonwebtoken.Jwts; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.boot.web.servlet.server.Session; 7 | import org.springframework.cloud.gateway.filter.GatewayFilter; 8 | import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; 9 | import org.springframework.core.env.Environment; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseCookie; 12 | import org.springframework.http.server.reactive.ServerHttpRequest; 13 | import org.springframework.http.server.reactive.ServerHttpResponse; 14 | import org.springframework.stereotype.Component; 15 | import org.springframework.web.server.ServerWebExchange; 16 | import reactor.core.publisher.Mono; 17 | 18 | @Component 19 | @Slf4j 20 | public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory { 21 | Environment env; 22 | 23 | public AuthorizationHeaderFilter(Environment env) { 24 | super(Config.class); 25 | this.env = env; 26 | } 27 | 28 | public static class Config { 29 | // Put configuration properties here 30 | } 31 | 32 | @Override 33 | public GatewayFilter apply(Config config) { 34 | return (exchange, chain) -> { 35 | ServerHttpRequest request = exchange.getRequest(); 36 | 37 | if (!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)) { 38 | return onError(exchange, "No authorization header", HttpStatus.UNAUTHORIZED); 39 | } 40 | 41 | String authorizationHeader = request.getHeaders().get(HttpHeaders.AUTHORIZATION).get(0); 42 | String jwt = authorizationHeader.replace("Bearer", ""); 43 | 44 | // Create a cookie object 45 | // ServerHttpResponse response = exchange.getResponse(); 46 | // ResponseCookie c1 = ResponseCookie.from("my_token", "test1234").maxAge(60 * 60 * 24).build(); 47 | // response.addCookie(c1); 48 | 49 | if (!isJwtValid(jwt)) { 50 | return onError(exchange, "JWT token is not valid", HttpStatus.UNAUTHORIZED); 51 | } 52 | 53 | return chain.filter(exchange); 54 | }; 55 | } 56 | 57 | private Mono onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) { 58 | ServerHttpResponse response = exchange.getResponse(); 59 | response.setStatusCode(httpStatus); 60 | 61 | log.error(err); 62 | return response.setComplete(); 63 | } 64 | 65 | private boolean isJwtValid(String jwt) { 66 | boolean returnValue = true; 67 | 68 | String subject = null; 69 | 70 | try { 71 | subject = Jwts.parser().setSigningKey(env.getProperty("token.secret")) 72 | .parseClaimsJws(jwt).getBody() 73 | .getSubject(); 74 | } catch (Exception ex) { 75 | returnValue = false; 76 | } 77 | 78 | if (subject == null || subject.isEmpty()) { 79 | returnValue = false; 80 | } 81 | 82 | return returnValue; 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /apigateway-service/src/main/java/com/example/apigatewayservice/filter/CustomFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice.filter; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.cloud.gateway.filter.GatewayFilter; 5 | import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; 6 | import org.springframework.http.server.reactive.ServerHttpRequest; 7 | import org.springframework.http.server.reactive.ServerHttpResponse; 8 | import org.springframework.stereotype.Component; 9 | import reactor.core.publisher.Mono; 10 | 11 | @Component 12 | @Slf4j 13 | public class CustomFilter extends AbstractGatewayFilterFactory { 14 | public CustomFilter() { 15 | super(Config.class); 16 | } 17 | 18 | @Override 19 | public GatewayFilter apply(Config config) { 20 | // Custom Pre Filter 21 | return (exchange, chain) -> { 22 | ServerHttpRequest request = exchange.getRequest(); 23 | ServerHttpResponse response = exchange.getResponse(); 24 | 25 | log.info("Custom PRE filter: request id -> {}", request.getId()); 26 | 27 | // Custom Post Filter 28 | return chain.filter(exchange).then(Mono.fromRunnable(() -> { 29 | log.info("Custom POST filter: response code -> {}", response.getStatusCode()); 30 | })); 31 | }; 32 | } 33 | 34 | public static class Config { 35 | // Put the configuration properties 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /apigateway-service/src/main/java/com/example/apigatewayservice/filter/GlobalFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice.filter; 2 | 3 | import lombok.Data; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.cloud.gateway.filter.GatewayFilter; 6 | import org.springframework.cloud.gateway.filter.GatewayFilterChain; 7 | import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; 8 | import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; 9 | import org.springframework.cloud.gateway.support.ipresolver.RemoteAddressResolver; 10 | import org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver; 11 | import org.springframework.core.Ordered; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.server.reactive.ServerHttpRequest; 14 | import org.springframework.http.server.reactive.ServerHttpResponse; 15 | import org.springframework.stereotype.Component; 16 | import org.springframework.web.server.ServerWebExchange; 17 | import reactor.core.publisher.Mono; 18 | 19 | @Component 20 | @Slf4j 21 | public class GlobalFilter extends AbstractGatewayFilterFactory { 22 | public GlobalFilter() { 23 | super(Config.class); 24 | } 25 | 26 | @Override 27 | public GatewayFilter apply(Config config) { 28 | return ((exchange, chain) -> { 29 | ServerHttpRequest request = exchange.getRequest(); 30 | ServerHttpResponse response = exchange.getResponse(); 31 | 32 | log.info("Global Filter baseMessage: {}, {}", config.getBaseMessage(), request.getRemoteAddress()); 33 | if (config.isPreLogger()) { 34 | log.info("Global Filter Start: request id -> {}", request.getId()); 35 | } 36 | return chain.filter(exchange).then(Mono.fromRunnable(()->{ 37 | if (config.isPostLogger()) { 38 | log.info("Global Filter End: response code -> {}", response.getStatusCode()); 39 | } 40 | })); 41 | }); 42 | } 43 | 44 | @Data 45 | public static class Config { 46 | private String baseMessage; 47 | private boolean preLogger; 48 | private boolean postLogger; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /apigateway-service/src/main/java/com/example/apigatewayservice/filter/LoggingFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice.filter; 2 | 3 | import lombok.Data; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.cloud.gateway.filter.GatewayFilter; 6 | import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; 7 | import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; 8 | import org.springframework.core.Ordered; 9 | import org.springframework.http.server.reactive.ServerHttpRequest; 10 | import org.springframework.http.server.reactive.ServerHttpResponse; 11 | import org.springframework.stereotype.Component; 12 | import reactor.core.publisher.Mono; 13 | 14 | @Component 15 | @Slf4j 16 | public class LoggingFilter extends AbstractGatewayFilterFactory { 17 | public LoggingFilter() { 18 | super(Config.class); 19 | } 20 | 21 | @Override 22 | public GatewayFilter apply(Config config) { 23 | // return ((exchange, chain) -> { 24 | // ServerHttpRequest request = exchange.getRequest(); 25 | // ServerHttpResponse response = exchange.getResponse(); 26 | // 27 | // log.info("Global Filter baseMessage: {}", config.getBaseMessage()); 28 | // if (config.isPreLogger()) { 29 | // log.info("Global Filter Start: request id -> {}", request.getId()); 30 | // } 31 | // return chain.filter(exchange).then(Mono.fromRunnable(()->{ 32 | // if (config.isPostLogger()) { 33 | // log.info("Global Filter End: response code -> {}", response.getStatusCode()); 34 | // } 35 | // })); 36 | // }); 37 | GatewayFilter filter = new OrderedGatewayFilter((exchange, chain) -> { 38 | ServerHttpRequest request = exchange.getRequest(); 39 | ServerHttpResponse response = exchange.getResponse(); 40 | 41 | log.info("Logging Filter baseMessage: {}", config.getBaseMessage()); 42 | if (config.isPreLogger()) { 43 | log.info("Logging PRE Filter: request id -> {}", request.getId()); 44 | } 45 | return chain.filter(exchange).then(Mono.fromRunnable(()->{ 46 | if (config.isPostLogger()) { 47 | log.info("Logging POST Filter: response code -> {}", response.getStatusCode()); 48 | } 49 | })); 50 | }, Ordered.LOWEST_PRECEDENCE); 51 | 52 | return filter; 53 | } 54 | 55 | @Data 56 | public static class Config { 57 | private String baseMessage; 58 | private boolean preLogger; 59 | private boolean postLogger; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /apigateway-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8000 3 | 4 | eureka: 5 | client: 6 | register-with-eureka: true 7 | fetch-registry: true 8 | service-url: 9 | defaultZone: http://localhost:8761/eureka 10 | 11 | spring: 12 | application: 13 | name: apigateway-service 14 | rabbitmq: 15 | host: 127.0.0.1 16 | port: 5672 17 | username: guest 18 | password: guest 19 | cloud: 20 | gateway: 21 | default-filters: 22 | - name: GlobalFilter 23 | args: 24 | baseMessage: Spring Cloud Gateway Global Filter 25 | preLogger: true 26 | postLogger: true 27 | routes: 28 | # - id: user-service 29 | # uri: lb://USER-SERVICE 30 | # predicates: 31 | # - Path=/user-service/** 32 | - id: user-service 33 | uri: lb://USER-SERVICE 34 | predicates: 35 | - Path=/user-service/login 36 | - Method=POST 37 | filters: 38 | - RemoveRequestHeader=Cookie 39 | - RewritePath=/user-service/(?.*), /$\{segment} 40 | - id: user-service 41 | uri: lb://USER-SERVICE 42 | predicates: 43 | - Path=/user-service/users 44 | - Method=POST 45 | filters: 46 | - RemoveRequestHeader=Cookie 47 | - RewritePath=/user-service/(?.*), /$\{segment} 48 | - id: user-service 49 | uri: lb://USER-SERVICE 50 | predicates: 51 | - Path=/user-service/actuator/** 52 | - Method=GET,POST 53 | filters: 54 | - RemoveRequestHeader=Cookie 55 | - RewritePath=/user-service/(?.*), /$\{segment} 56 | - id: user-service 57 | uri: lb://USER-SERVICE 58 | predicates: 59 | - Path=/user-service/** 60 | - Method=GET 61 | filters: 62 | - RemoveRequestHeader=Cookie 63 | - RewritePath=/user-service/(?.*), /$\{segment} 64 | - AuthorizationHeaderFilter 65 | - id: catalog-service 66 | uri: lb://CATALOG-SERVICE 67 | predicates: 68 | - Path=/catalog-service/** 69 | - id: order-service 70 | uri: lb://ORDER-SERVICE 71 | predicates: 72 | - Path=/order-service/actuator/** 73 | - Method=GET,POST 74 | filters: 75 | - RemoveRequestHeader=Cookie 76 | - RewritePath=/order-service/(?.*), /$\{segment} 77 | - id: order-service 78 | uri: lb://ORDER-SERVICE 79 | predicates: 80 | - Path=/order-service/** 81 | - id: first-service 82 | uri: lb://MY-FIRST-SERVICE 83 | predicates: 84 | - Path=/first-service/** 85 | filters: 86 | - AddRequestHeader=first-request, first-request-header2 87 | - AddResponseHeader=first-response, first-response-header2 88 | # - CustomFilter 89 | - id: second-service 90 | uri: lb://MY-SECOND-SERVICE 91 | predicates: 92 | - Path=/second-service/** 93 | filters: 94 | - AddRequestHeader=second-request, second-request-header2 95 | - AddResponseHeader=second-response, second-response-header2 96 | # - name: CustomFilter 97 | - name: LoggingFilter 98 | args: 99 | baseMessage: Hi, there. 100 | preLogger: true 101 | postLogger: true 102 | 103 | #token: 104 | # secret: user_token 105 | 106 | management: 107 | endpoints: 108 | web: 109 | exposure: 110 | include: refresh, health, beans, httptrace, busrefresh, info, metrics, prometheus -------------------------------------------------------------------------------- /apigateway-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | uri: http://127.0.0.1:8888 5 | name: user-service 6 | # profiles: 7 | # active: dev -------------------------------------------------------------------------------- /apigateway-service/src/test/java/com/example/apigatewayservice/ApigatewayServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.apigatewayservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ApigatewayServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /catalog-service/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /catalog-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/catalog-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /catalog-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /catalog-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17-ea-11-jdk-slim 2 | VOLUME /tmp 3 | COPY target/catalog-service-1.0.jar CatalogService.jar 4 | ENTRYPOINT ["java", "-jar", "CatalogService.jar"] -------------------------------------------------------------------------------- /catalog-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.2 9 | 10 | 11 | com.example 12 | catalog-service 13 | 1.0 14 | catalog-service 15 | Demo project for Spring Boot 16 | 17 | 11 18 | 2020.0.0 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-jpa 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-starter-netflix-eureka-client 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-devtools 37 | runtime 38 | true 39 | 40 | 41 | com.h2database 42 | h2 43 | 1.3.176 44 | runtime 45 | 46 | 47 | 48 | org.modelmapper 49 | modelmapper 50 | 2.3.8 51 | 52 | 53 | 54 | org.projectlombok 55 | lombok 56 | true 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-test 61 | test 62 | 63 | 64 | 65 | 66 | org.springframework.kafka 67 | spring-kafka 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.cloud 74 | spring-cloud-dependencies 75 | ${spring-cloud.version} 76 | pom 77 | import 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-maven-plugin 87 | 88 | 89 | 90 | org.projectlombok 91 | lombok 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | spring-milestones 101 | Spring Milestones 102 | https://repo.spring.io/milestone 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/CatalogServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.catalogservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CatalogServiceApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CatalogServiceApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/controller/CatalogController.java: -------------------------------------------------------------------------------- 1 | package com.example.catalogservice.controller; 2 | 3 | import com.example.catalogservice.jpa.CatalogEntity; 4 | import com.example.catalogservice.service.CatalogService; 5 | import com.example.catalogservice.vo.ResponseCatalog; 6 | import org.modelmapper.ModelMapper; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.core.env.Environment; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | @RestController 19 | @RequestMapping("/catalog-service") 20 | public class CatalogController { 21 | Environment env; 22 | CatalogService catalogService; 23 | 24 | @Autowired 25 | public CatalogController(Environment env, CatalogService catalogService) { 26 | this.env = env; 27 | this.catalogService = catalogService; 28 | } 29 | 30 | @GetMapping("/health_check") 31 | public String status() { 32 | return String.format("It's Working in Catalog Service on PORT %s", 33 | env.getProperty("local.server.port")); 34 | } 35 | 36 | @GetMapping("/catalogs") 37 | public ResponseEntity> getCatalogs() { 38 | Iterable catalogList = catalogService.getAllCatalogs(); 39 | 40 | List result = new ArrayList<>(); 41 | catalogList.forEach(v -> { 42 | result.add(new ModelMapper().map(v, ResponseCatalog.class)); 43 | }); 44 | 45 | return ResponseEntity.status(HttpStatus.OK).body(result); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/dto/CatalogDto.java: -------------------------------------------------------------------------------- 1 | package com.example.catalogservice.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class CatalogDto implements Serializable { 9 | private String productId; 10 | private Integer qty; 11 | private Integer unitPrice; 12 | private Integer totalPrice; 13 | 14 | private String orderId; 15 | private String userId; 16 | } 17 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/jpa/CatalogEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.catalogservice.jpa; 2 | 3 | import lombok.Data; 4 | import org.hibernate.annotations.ColumnDefault; 5 | 6 | import javax.persistence.*; 7 | import java.io.Serializable; 8 | import java.util.Date; 9 | 10 | @Data 11 | @Entity 12 | @Table(name = "catalog") 13 | public class CatalogEntity implements Serializable { 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.IDENTITY) 16 | private Long id; 17 | 18 | @Column(nullable = false, length = 120, unique = true) 19 | private String productId; 20 | @Column(nullable = false) 21 | private String productName; 22 | @Column(nullable = false) 23 | private Integer stock; 24 | @Column(nullable = false) 25 | private Integer unitPrice; 26 | 27 | @Column(nullable = false, updatable = false, insertable = false) 28 | @ColumnDefault(value = "CURRENT_TIMESTAMP") 29 | private Date createdAt; 30 | } 31 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/jpa/CatalogRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.catalogservice.jpa; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface CatalogRepository extends CrudRepository { 6 | CatalogEntity findByProductId(String productId); 7 | } 8 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/messagequeue/KafkaConsumer.java: -------------------------------------------------------------------------------- 1 | package com.example.catalogservice.messagequeue; 2 | 3 | import com.example.catalogservice.jpa.CatalogEntity; 4 | import com.example.catalogservice.jpa.CatalogRepository; 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.core.type.TypeReference; 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.kafka.annotation.KafkaListener; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | @Service 17 | @Slf4j 18 | public class KafkaConsumer { 19 | CatalogRepository repository; 20 | 21 | @Autowired 22 | public KafkaConsumer(CatalogRepository repository) { 23 | this.repository = repository; 24 | } 25 | 26 | @KafkaListener(topics = "example-catalog-topic") 27 | public void updateQty(String kafkaMessage) { 28 | log.info("Kafka Message: ->" + kafkaMessage); 29 | 30 | Map map = new HashMap<>(); 31 | ObjectMapper mapper = new ObjectMapper(); 32 | try { 33 | map = mapper.readValue(kafkaMessage, new TypeReference>() {}); 34 | } catch (JsonProcessingException ex) { 35 | ex.printStackTrace(); 36 | } 37 | 38 | CatalogEntity entity = repository.findByProductId((String)map.get("productId")); 39 | if (entity != null) { 40 | entity.setStock(entity.getStock() - (Integer)map.get("qty")); 41 | repository.save(entity); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/messagequeue/KafkaConsumerConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.catalogservice.messagequeue; 2 | 3 | import org.apache.kafka.clients.consumer.ConsumerConfig; 4 | import org.apache.kafka.common.serialization.StringDeserializer; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.kafka.annotation.EnableKafka; 8 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; 9 | import org.springframework.kafka.core.ConsumerFactory; 10 | import org.springframework.kafka.core.DefaultKafkaConsumerFactory; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | @EnableKafka 16 | @Configuration 17 | public class KafkaConsumerConfig { 18 | @Bean 19 | public ConsumerFactory consumerFactory() { 20 | Map properties = new HashMap<>(); 21 | properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "172.18.0.101:9092"); 22 | properties.put(ConsumerConfig.GROUP_ID_CONFIG, "consumerGroupId"); 23 | properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 24 | properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 25 | 26 | return new DefaultKafkaConsumerFactory<>(properties); 27 | } 28 | 29 | @Bean 30 | public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { 31 | ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory 32 | = new ConcurrentKafkaListenerContainerFactory<>(); 33 | kafkaListenerContainerFactory.setConsumerFactory(consumerFactory()); 34 | 35 | return kafkaListenerContainerFactory; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/service/CatalogService.java: -------------------------------------------------------------------------------- 1 | package com.example.catalogservice.service; 2 | 3 | import com.example.catalogservice.jpa.CatalogEntity; 4 | 5 | public interface CatalogService { 6 | Iterable getAllCatalogs(); 7 | } 8 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/service/CatalogServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.catalogservice.service; 2 | 3 | import com.example.catalogservice.jpa.CatalogEntity; 4 | import com.example.catalogservice.jpa.CatalogRepository; 5 | import lombok.Data; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | 10 | @Data 11 | @Slf4j 12 | @Service 13 | public class CatalogServiceImpl implements CatalogService{ 14 | CatalogRepository catalogRepository; 15 | 16 | @Autowired 17 | public CatalogServiceImpl(CatalogRepository catalogRepository) { 18 | this.catalogRepository = catalogRepository; 19 | } 20 | 21 | @Override 22 | public Iterable getAllCatalogs() { 23 | return catalogRepository.findAll(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/vo/ResponseCatalog.java: -------------------------------------------------------------------------------- 1 | package com.example.catalogservice.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | import java.util.Date; 7 | 8 | @Data 9 | @JsonInclude(JsonInclude.Include.NON_NULL) 10 | public class ResponseCatalog { 11 | private String productId; 12 | private String productName; 13 | private Integer unitPrice; 14 | private Integer stock; 15 | private Date createdAt; 16 | } 17 | -------------------------------------------------------------------------------- /catalog-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 0 3 | 4 | spring: 5 | application: 6 | name: catalog-service 7 | h2: 8 | console: 9 | enabled: true 10 | settings: 11 | web-allow-others: true 12 | path: /h2-console 13 | jpa: 14 | hibernate: 15 | ddl-auto: create-drop 16 | show-sql: true 17 | generate-ddl: true 18 | datasource: 19 | driver-class-name: org.h2.Driver 20 | url: jdbc:h2:mem:testdb 21 | # username: sa 22 | # password: 1234 23 | 24 | eureka: 25 | instance: 26 | instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}} 27 | client: 28 | register-with-eureka: true 29 | fetch-registry: true 30 | service-url: 31 | defaultZone: http://127.0.0.1:8761/eureka 32 | 33 | logging: 34 | level: 35 | com.example.catalogservice: DEBUG 36 | -------------------------------------------------------------------------------- /catalog-service/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | insert into catalog(product_id, product_name, stock, unit_price) 2 | values('CATALOG-001', 'Berlin', 100, 1500); 3 | insert into catalog(product_id, product_name, stock, unit_price) 4 | values('CATALOG-002', 'Tokyo', 110, 1000); 5 | insert into catalog(product_id, product_name, stock, unit_price) 6 | values('CATALOG-003', 'Stockholm', 120, 2000); -------------------------------------------------------------------------------- /catalog-service/src/test/java/com/example/catalogservice/CatalogServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.catalogservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class CatalogServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /config-service/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /config-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/config-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /config-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /config-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17-ea-11-jdk-slim 2 | VOLUME /tmp 3 | COPY apiEncryptionKey.jks apiEncryptionKey.jks 4 | COPY target/config-service-1.0.jar ConfigServer.jar 5 | ENTRYPOINT ["java","-jar","ConfigServer.jar"] -------------------------------------------------------------------------------- /config-service/apiEncryptionKey.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/config-service/apiEncryptionKey.jks -------------------------------------------------------------------------------- /config-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.5 9 | 10 | 11 | com.example 12 | config-service 13 | 14 | 1.0 15 | config-service 16 | Demo project for Spring Boot 17 | 18 | 11 19 | 2020.0.1 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-config-server 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-test 30 | test 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-actuator 36 | 37 | 38 | 39 | org.springframework.cloud 40 | spring-cloud-starter-bus-amqp 41 | 42 | 43 | 44 | org.springframework.cloud 45 | spring-cloud-starter-bootstrap 46 | 47 | 48 | 49 | 50 | 51 | org.springframework.cloud 52 | spring-cloud-dependencies 53 | ${spring-cloud.version} 54 | pom 55 | import 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-maven-plugin 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /config-service/src/main/java/com/example/configservice/ConfigServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.configservice; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.config.server.EnableConfigServer; 7 | 8 | import java.util.Enumeration; 9 | import java.util.Properties; 10 | 11 | @SpringBootApplication 12 | @EnableConfigServer 13 | public class ConfigServiceApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(ConfigServiceApplication.class, args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8888 3 | 4 | spring: 5 | application: 6 | name: config-service 7 | rabbitmq: 8 | host: 127.0.0.1 9 | port: 5672 10 | username: guest 11 | password: guest 12 | profiles: 13 | active: native 14 | cloud: 15 | config: 16 | server: 17 | git: 18 | # uri: file:///Users/dowonlee/Desktop/Work/git-local-repo 19 | uri: https://github.com/joneconsulting/spring-cloud-config 20 | # basedir: /Users/dowonlee/Desktop/Work/tmp/config-repo 21 | # username: [username] 22 | # password: [password] 23 | native: 24 | search-locations: file:///Users/dowonlee/Desktop/Work/native-file-repo 25 | 26 | management: 27 | endpoints: 28 | web: 29 | exposure: 30 | include: health, busrefresh 31 | -------------------------------------------------------------------------------- /config-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | encrypt: 2 | # key: abcdefghijklmnopqrstuvwxyz0123456789 3 | key-store: 4 | location: file://${user.home}/Desktop/Work/keystore/apiEncryptionKey.jks 5 | # location: file:/apiEncryptionKey.jks 6 | # location: file:///apiEncryptionKey.jks 7 | password: test1234 8 | alias: apiEncryptionKey -------------------------------------------------------------------------------- /config-service/src/test/java/com/example/configservice/ConfigServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.configservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ConfigServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /cookie-demo/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const cookieParser = require("cookie-parser"); 4 | app.use(cookieParser()); 5 | 6 | app.get("/login", (req, res) => { 7 | res 8 | .writeHead(200, { 9 | "Set-Cookie": "token=encryptedstring; HttpOnly", 10 | "Access-Control-Allow-Credentials": "true" 11 | }) 12 | .send(); 13 | }); 14 | 15 | app.get("/private", (req, res) => { 16 | if (!req.cookies.token) return res.status(401).send(); 17 | res.status(200).json({ secret: "Ginger ale is a specific Root Beer" }); 18 | }); 19 | 20 | app.listen(3001, () => { 21 | console.log("listening on port 3001..."); 22 | }); -------------------------------------------------------------------------------- /cookie-demo/package.json: -------------------------------------------------------------------------------- 1 | "scripts": { 2 | "start": "nodemon index.js" 3 | } -------------------------------------------------------------------------------- /discoveryservice/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /discoveryservice/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/discoveryservice/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /discoveryservice/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /discoveryservice/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17-ea-11-jdk-slim 2 | VOLUME /tmp 3 | COPY target/discoveryservice-1.0.jar DiscoveryService.jar 4 | ENTRYPOINT ["java", "-jar", "DiscoveryService.jar"] -------------------------------------------------------------------------------- /discoveryservice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.2 9 | 10 | 11 | com.example 12 | discoveryservice 13 | 14 | 1.0 15 | discoveryservice 16 | Demo project for Spring Boot 17 | 18 | 11 19 | 2020.0.0 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-netflix-eureka-server 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-test 30 | test 31 | 32 | 33 | 34 | 35 | 36 | org.springframework.cloud 37 | spring-cloud-dependencies 38 | ${spring-cloud.version} 39 | pom 40 | import 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-maven-plugin 50 | 51 | 52 | 53 | 54 | 55 | spring-milestones 56 | Spring Milestones 57 | https://repo.spring.io/milestone 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /discoveryservice/src/main/java/com/example/discoveryservice/DiscoveryserviceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.discoveryservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @SpringBootApplication 8 | @EnableEurekaServer 9 | public class DiscoveryserviceApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(DiscoveryserviceApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /discoveryservice/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8761 3 | 4 | spring: 5 | application: 6 | name: discoveryservice 7 | cloud: 8 | config: 9 | uri: http://127.0.0.1:8888 10 | name: ecommerce 11 | 12 | eureka: 13 | client: 14 | register-with-eureka: false 15 | fetch-registry: false 16 | -------------------------------------------------------------------------------- /discoveryservice/src/test/java/com/example/discoveryservice/DiscoveryserviceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.discoveryservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DiscoveryserviceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /docker-files/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mariadb:10.3 2 | # ENV MYSQL_ROOT_PASSWORD test1357 3 | ENV MYSQL_ALLOW_EMPTY_PASSWORD true 4 | ENV MYSQL_DATABASE mydb 5 | # COPY ./mysql_data/mysql /var/lib/mysql 6 | EXPOSE 3306 7 | # ENTRYPOINT ["mysqld", "--user=root"] 8 | -------------------------------------------------------------------------------- /docker-files/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | zookeeper: 4 | image: wurstmeister/zookeeper 5 | ports: 6 | - "2181:2181" 7 | networks: 8 | my-network: 9 | ipv4_address: 172.18.0.100 10 | kafka: 11 | # build: . 12 | image: wurstmeister/kafka 13 | ports: 14 | - "9092:9092" 15 | environment: 16 | KAFKA_ADVERTISED_HOST_NAME: 172.18.0.101 17 | KAFKA_CREATE_TOPICS: "test:1:1" 18 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 19 | volumes: 20 | - /var/run/docker.sock:/var/run/docker.sock 21 | depends_on: 22 | - zookeeper 23 | networks: 24 | my-network: 25 | ipv4_address: 172.18.0.101 26 | 27 | rabbitmq: 28 | image: rabbitmq:management 29 | ports: 30 | - "15671:15671" 31 | - "15672:15672" 32 | - "5671:5671" 33 | - "5672:5672" 34 | - "4369:4369" 35 | environment: 36 | RABBITMQ_DEFAULT_USER: guest 37 | RABBITMQ_DEFAULT_PASS: guest 38 | networks: 39 | my-network: 40 | 41 | config-service: 42 | image: edowon0623/config-service:1.0 43 | ports: 44 | - "8888:8888" 45 | environment: 46 | spring.rabbitmq.host: rabbitmq 47 | spring.profiles.active: default 48 | depends_on: 49 | - rabbitmq 50 | networks: 51 | my-network: 52 | 53 | discovery-service: 54 | image: edowon0623/discovery-service:1.0 55 | ports: 56 | - "8761:8761" 57 | environment: 58 | spring.cloud.config.uri: http://config-service:8888 59 | depends_on: 60 | - config-service 61 | networks: 62 | my-network: 63 | 64 | apigateway-service: 65 | image: edowon0623/apigateway-service:1.0 66 | ports: 67 | - "8000:8000" 68 | environment: 69 | spring.cloud.config.uri: http://config-service:8888 70 | spring.rabbitmq.host: rabbitmq 71 | eureka.client.serviceUrl.defaultZone: http://discovery-service:8761/eureka/ 72 | depends_on: 73 | - discovery-service 74 | networks: 75 | my-network: 76 | 77 | mariadb: 78 | image: edowon0623/my-mariadb:1.0 79 | ports: 80 | - "3306:3306" 81 | networks: 82 | my-network: 83 | 84 | zipkin: 85 | image: openzipkin/zipkin 86 | ports: 87 | - "9411:9411" 88 | networks: 89 | my-network: 90 | 91 | prometheus: 92 | image: prom/prometheus 93 | ports: 94 | - "9090:9090" 95 | volumes: 96 | - /Users/dowonlee/Desktop/Work/springcloud/prometheus-2.25.0.darwin-amd64/prometheus.yml:/etc/prometheus/prometheus.yml 97 | networks: 98 | my-network: 99 | 100 | grafana: 101 | image: grafana/grafana 102 | ports: 103 | - "3000:3000" 104 | networks: 105 | my-network: 106 | 107 | user-service: 108 | image: edowon0623/user-service:1.0 109 | environment: 110 | spring.cloud.config.uri: http://config-service:8888 111 | spring.rabbitmq.host: rabbitmq 112 | spring.zipkin.base-url: http://zipkin:9411 113 | eureka.client.serviceUrl.defaultZone: http://discovery-service:8761/eureka/ 114 | logging.file: /api-logs/users-ws.log 115 | depends_on: 116 | - apigateway-service 117 | networks: 118 | my-network: 119 | ipv4_address: 172.18.0.12 120 | 121 | order-service: 122 | image: edowon0623/order-service:1.0 123 | environment: 124 | spring.cloud.config.uri: http://config-service:8888 125 | spring.rabbitmq.host: rabbitmq 126 | spring.zipkin.base-url: http://zipkin:9411 127 | eureka.client.serviceUrl.defaultZone: http://discovery-service:8761/eureka/ 128 | spring.datasource.url: jdbc:mariadb://mariadb:3306/mydb 129 | logging.file: /api-logs/orders-ws.log 130 | depends_on: 131 | - apigateway-service 132 | networks: 133 | my-network: 134 | ipv4_address: 172.18.0.13 135 | 136 | catalog-service: 137 | image: edowon0623/catalog-service:1.0 138 | environment: 139 | spring.cloud.config.uri: http://config-service:8888 140 | spring.rabbitmq.host: rabbitmq 141 | spring.zipkin.base-url: http://zipkin:9411 142 | eureka.client.serviceUrl.defaultZone: http://discovery-service:8761/eureka/ 143 | logging.file: /api-logs/catalogs-ws.log 144 | depends_on: 145 | - apigateway-service 146 | networks: 147 | my-network: 148 | ipv4_address: 172.18.0.14 149 | 150 | networks: 151 | my-network: 152 | external: true 153 | name: ecommerce-network 154 | -------------------------------------------------------------------------------- /docker-files/mysql_data/mysql/mydb/db.opt: -------------------------------------------------------------------------------- 1 | default-character-set=utf8mb4 2 | default-collation=utf8mb4_general_ci 3 | -------------------------------------------------------------------------------- /docker-files/mysql_data/mysql/mydb/hibernate_sequence.frm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/docker-files/mysql_data/mysql/mydb/hibernate_sequence.frm -------------------------------------------------------------------------------- /docker-files/mysql_data/mysql/mydb/hibernate_sequence.ibd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/docker-files/mysql_data/mysql/mydb/hibernate_sequence.ibd -------------------------------------------------------------------------------- /docker-files/mysql_data/mysql/mydb/orders.frm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/docker-files/mysql_data/mysql/mydb/orders.frm -------------------------------------------------------------------------------- /docker-files/mysql_data/mysql/mydb/orders.ibd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/docker-files/mysql_data/mysql/mydb/orders.ibd -------------------------------------------------------------------------------- /eclipse_lombok.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/eclipse_lombok.pdf -------------------------------------------------------------------------------- /first-service/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /first-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/first-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /first-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /first-service/first-service-demo.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/first-service/first-service-demo.zip -------------------------------------------------------------------------------- /first-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.5 9 | 10 | 11 | com.example 12 | first-service 13 | 0.0.1-SNAPSHOT 14 | first-service 15 | Demo project for Spring Boot 16 | 17 | 11 18 | 2020.0.0 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-starter-netflix-eureka-client 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-oauth2-resource-server 33 | 34 | 35 | 36 | org.projectlombok 37 | lombok 38 | true 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | org.junit.vintage 47 | junit-vintage-engine 48 | 49 | 50 | 51 | 52 | 53 | org.springframework.cloud 54 | spring-cloud-starter-bootstrap 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.cloud 61 | spring-cloud-dependencies 62 | ${spring-cloud.version} 63 | pom 64 | import 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-maven-plugin 74 | 75 | 76 | 77 | org.projectlombok 78 | lombok 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /first-service/src/main/java/com/example/firstservice/CustomizationPort.java: -------------------------------------------------------------------------------- 1 | package com.example.firstservice; 2 | 3 | import org.springframework.boot.web.server.WebServerFactoryCustomizer; 4 | import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.util.Random; 8 | 9 | @Component 10 | public class CustomizationPort implements WebServerFactoryCustomizer { 11 | 12 | @Override 13 | public void customize(ConfigurableServletWebServerFactory server) { 14 | Random random = new Random(); 15 | var port = random.ints(10000, 51000) 16 | .findFirst() 17 | .getAsInt(); 18 | 19 | server.setPort(port); 20 | } 21 | } -------------------------------------------------------------------------------- /first-service/src/main/java/com/example/firstservice/FirstServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.firstservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class FirstServiceApplication { 8 | 9 | public static void main(String[] args) { 10 | 11 | SpringApplication.run(FirstServiceApplication.class, args); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /first-service/src/main/java/com/example/firstservice/FirstServiceController.java: -------------------------------------------------------------------------------- 1 | package com.example.firstservice; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.boot.autoconfigure.web.ServerProperties; 7 | import org.springframework.boot.web.server.LocalServerPort; 8 | import org.springframework.core.env.Environment; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.RequestHeader; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import java.util.Collections; 16 | import java.util.Enumeration; 17 | 18 | @RestController 19 | @RequestMapping("/first-service") 20 | @Slf4j 21 | public class FirstServiceController { 22 | Environment env; 23 | 24 | @Autowired 25 | public FirstServiceController(Environment env) { 26 | this.env = env; 27 | } 28 | 29 | @GetMapping("/welcome") 30 | public String welcome() { 31 | return "Welcome to the First service."; 32 | } 33 | 34 | @GetMapping("/message") 35 | public String message(@RequestHeader("first-request") String header) { 36 | log.info(header); 37 | return "Hello World in First Service."; 38 | } 39 | 40 | @GetMapping("/check") 41 | public String check(HttpServletRequest request) { 42 | Enumeration headers = request.getHeaderNames(); 43 | Collections.list(headers).stream().forEach(name -> { 44 | Enumeration values = request.getHeaders(name); 45 | Collections.list(values).stream().forEach(value -> System.out.println(name + "=" + value)); 46 | }); 47 | 48 | log.info("Server port={}", request.getServerPort()); 49 | 50 | log.info("spring.cloud.client.hostname={}", env.getProperty("spring.cloud.client.hostname")); 51 | log.info("spring.cloud.client.ip-address={}", env.getProperty("spring.cloud.client.ip-address")); 52 | 53 | return String.format("Hi, there. This is a message from First Service on PORT %s" 54 | , env.getProperty("local.server.port")); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /first-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 0 3 | # port: ${random.int(10000,51000)} 4 | 5 | spring: 6 | application: 7 | name: my-first-service 8 | 9 | eureka: 10 | client: 11 | register-with-eureka: true 12 | fetch-registry: true 13 | service-url: 14 | defaultZone: http://localhost:8761/eureka 15 | instance: 16 | instance-id: ${spring.cloud.client.ip-address}:${spring.application.instance_id:${random.value}} 17 | prefer-ip-address: true -------------------------------------------------------------------------------- /first-service/src/test/java/com/example/firstservice/FirstServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.firstservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class FirstServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /order-service/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /order-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/order-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /order-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /order-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17-ea-11-jdk-slim 2 | VOLUME /tmp 3 | COPY target/order-service-1.0.jar OrderService.jar 4 | ENTRYPOINT ["java", "-jar", "OrderService.jar"] -------------------------------------------------------------------------------- /order-service/mem/testdb.h2.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/order-service/mem/testdb.h2.db -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/OrderServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class OrderServiceApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(OrderServiceApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/controller/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.controller; 2 | 3 | import com.example.orderservice.dto.OrderDto; 4 | import com.example.orderservice.jpa.OrderEntity; 5 | import com.example.orderservice.messagequeue.KafkaProducer; 6 | import com.example.orderservice.messagequeue.OrderProducer; 7 | import com.example.orderservice.service.OrderService; 8 | import com.example.orderservice.vo.RequestOrder; 9 | import com.example.orderservice.vo.ResponseOrder; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.modelmapper.ModelMapper; 12 | import org.modelmapper.convention.MatchingStrategies; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.core.env.Environment; 15 | import org.springframework.http.HttpStatus; 16 | import org.springframework.http.ResponseEntity; 17 | import org.springframework.web.bind.annotation.*; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.UUID; 22 | 23 | @RestController 24 | @RequestMapping("/order-service") 25 | @Slf4j 26 | public class OrderController { 27 | Environment env; 28 | OrderService orderService; 29 | KafkaProducer kafkaProducer; 30 | 31 | OrderProducer orderProducer; 32 | 33 | @Autowired 34 | public OrderController(Environment env, OrderService orderService, 35 | KafkaProducer kafkaProducer, OrderProducer orderProducer) { 36 | this.env = env; 37 | this.orderService = orderService; 38 | this.kafkaProducer = kafkaProducer; 39 | this.orderProducer = orderProducer; 40 | } 41 | 42 | @GetMapping("/health_check") 43 | public String status() { 44 | return String.format("It's Working in Order Service on PORT %s", 45 | env.getProperty("local.server.port")); 46 | } 47 | 48 | @PostMapping("/{userId}/orders") 49 | public ResponseEntity createOrder(@PathVariable("userId") String userId, 50 | @RequestBody RequestOrder orderDetails) { 51 | log.info("Before add orders data"); 52 | ModelMapper mapper = new ModelMapper(); 53 | mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); 54 | 55 | OrderDto orderDto = mapper.map(orderDetails, OrderDto.class); 56 | orderDto.setUserId(userId); 57 | /* jpa */ 58 | OrderDto createdOrder = orderService.createOrder(orderDto); 59 | ResponseOrder responseOrder = mapper.map(createdOrder, ResponseOrder.class); 60 | 61 | /* kafka */ 62 | // orderDto.setOrderId(UUID.randomUUID().toString()); 63 | // orderDto.setTotalPrice(orderDetails.getQty() * orderDetails.getUnitPrice()); 64 | 65 | /* send this order to the kafka */ 66 | // kafkaProducer.send("example-catalog-topic", orderDto); 67 | // orderProducer.send("orders", orderDto); 68 | 69 | // ResponseOrder responseOrder = mapper.map(orderDto, ResponseOrder.class); 70 | 71 | log.info("After added orders data"); 72 | return ResponseEntity.status(HttpStatus.CREATED).body(responseOrder); 73 | } 74 | 75 | @GetMapping("/{userId}/orders") 76 | public ResponseEntity> getOrder(@PathVariable("userId") String userId) throws Exception { 77 | log.info("Before retrieve orders data"); 78 | Iterable orderList = orderService.getOrdersByUserId(userId); 79 | 80 | List result = new ArrayList<>(); 81 | orderList.forEach(v -> { 82 | result.add(new ModelMapper().map(v, ResponseOrder.class)); 83 | }); 84 | 85 | try { 86 | Thread.sleep(1000); 87 | throw new Exception("장애 발생"); 88 | } catch(InterruptedException ex) { 89 | log.warn(ex.getMessage()); 90 | } 91 | 92 | log.info("Add retrieved orders data"); 93 | 94 | return ResponseEntity.status(HttpStatus.OK).body(result); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/dto/Field.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | @AllArgsConstructor 8 | public class Field { 9 | private String type; 10 | private boolean optional; 11 | private String field; 12 | } 13 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/dto/KafkaOrderDto.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | public class KafkaOrderDto implements Serializable { 11 | private Schema schema; 12 | private Payload payload; 13 | } 14 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/dto/OrderDto.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class OrderDto implements Serializable { 9 | private String productId; 10 | private Integer qty; 11 | private Integer unitPrice; 12 | private Integer totalPrice; 13 | 14 | private String orderId; 15 | private String userId; 16 | } 17 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/dto/Payload.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.dto; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Builder 8 | public class Payload { 9 | private String order_id; 10 | private String user_id; 11 | private String product_id; 12 | private int qty; 13 | private int unit_price; 14 | private int total_price; 15 | } 16 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/dto/Schema.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.dto; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | @Data 9 | @Builder 10 | public class Schema { 11 | private String type; 12 | private List fields; 13 | private boolean optional; 14 | private String name; 15 | } 16 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/jpa/OrderEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.jpa; 2 | 3 | import lombok.Data; 4 | import org.hibernate.annotations.ColumnDefault; 5 | 6 | import javax.persistence.*; 7 | import java.io.Serializable; 8 | import java.util.Date; 9 | 10 | @Data 11 | @Entity 12 | @Table(name="orders") 13 | public class OrderEntity implements Serializable { 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.IDENTITY) 16 | private Long id; 17 | 18 | @Column(nullable = false, length = 120, unique = true) 19 | private String productId; 20 | @Column(nullable = false) 21 | private Integer qty; 22 | @Column(nullable = false) 23 | private Integer unitPrice; 24 | @Column(nullable = false) 25 | private Integer totalPrice; 26 | 27 | @Column(nullable = false) 28 | private String userId; 29 | @Column(nullable = false, unique = true) 30 | private String orderId; 31 | 32 | @Column(nullable = false, updatable = false, insertable = false) 33 | @ColumnDefault(value = "CURRENT_TIMESTAMP") 34 | private Date createdAt; 35 | } 36 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/jpa/OrderRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.jpa; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface OrderRepository extends CrudRepository { 6 | OrderEntity findByOrderId(String orderId); 7 | Iterable findByUserId(String userId); 8 | } 9 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/messagequeue/KafkaProducer.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.messagequeue; 2 | 3 | import com.example.orderservice.dto.OrderDto; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.kafka.core.KafkaTemplate; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Service 12 | @Slf4j 13 | public class KafkaProducer { 14 | private KafkaTemplate kafkaTemplate; 15 | 16 | @Autowired 17 | public KafkaProducer(KafkaTemplate kafkaTemplate) { 18 | this.kafkaTemplate = kafkaTemplate; 19 | } 20 | 21 | public OrderDto send(String topic, OrderDto orderDto) { 22 | ObjectMapper mapper = new ObjectMapper(); 23 | String jsonInString = ""; 24 | try { 25 | jsonInString = mapper.writeValueAsString(orderDto); 26 | } catch(JsonProcessingException ex) { 27 | ex.printStackTrace(); 28 | } 29 | 30 | kafkaTemplate.send(topic, jsonInString); 31 | log.info("Kafka Producer sent data from the Order microservice: " + orderDto); 32 | 33 | return orderDto; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/messagequeue/KafkaProducerConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.messagequeue; 2 | 3 | import org.apache.kafka.clients.consumer.ConsumerConfig; 4 | import org.apache.kafka.clients.producer.ProducerConfig; 5 | import org.apache.kafka.common.serialization.StringDeserializer; 6 | import org.apache.kafka.common.serialization.StringSerializer; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.kafka.annotation.EnableKafka; 10 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; 11 | import org.springframework.kafka.core.*; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | @EnableKafka 17 | @Configuration 18 | public class KafkaProducerConfig { 19 | @Bean 20 | public ProducerFactory producerFactory() { 21 | Map properties = new HashMap<>(); 22 | properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "172.18.0.101:9092"); 23 | properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 24 | properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 25 | 26 | return new DefaultKafkaProducerFactory<>(properties); 27 | } 28 | 29 | @Bean 30 | public KafkaTemplate kafkaTemplate() { 31 | return new KafkaTemplate<>(producerFactory()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/messagequeue/OrderProducer.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.messagequeue; 2 | 3 | import com.example.orderservice.dto.*; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.kafka.core.KafkaTemplate; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | @Service 15 | @Slf4j 16 | public class OrderProducer { 17 | private KafkaTemplate kafkaTemplate; 18 | 19 | List fields = Arrays.asList(new Field("string", true, "order_id"), 20 | new Field("string", true, "user_id"), 21 | new Field("string", true, "product_id"), 22 | new Field("int32", true, "qty"), 23 | new Field("int32", true, "unit_price"), 24 | new Field("int32", true, "total_price")); 25 | Schema schema = Schema.builder() 26 | .type("struct") 27 | .fields(fields) 28 | .optional(false) 29 | .name("orders") 30 | .build(); 31 | 32 | @Autowired 33 | public OrderProducer(KafkaTemplate kafkaTemplate) { 34 | this.kafkaTemplate = kafkaTemplate; 35 | } 36 | 37 | public OrderDto send(String topic, OrderDto orderDto) { 38 | Payload payload = Payload.builder() 39 | .order_id(orderDto.getOrderId()) 40 | .user_id(orderDto.getUserId()) 41 | .product_id(orderDto.getProductId()) 42 | .qty(orderDto.getQty()) 43 | .unit_price(orderDto.getUnitPrice()) 44 | .total_price(orderDto.getTotalPrice()) 45 | .build(); 46 | 47 | KafkaOrderDto kafkaOrderDto = new KafkaOrderDto(schema, payload); 48 | 49 | ObjectMapper mapper = new ObjectMapper(); 50 | String jsonInString = ""; 51 | try { 52 | jsonInString = mapper.writeValueAsString(kafkaOrderDto); 53 | } catch(JsonProcessingException ex) { 54 | ex.printStackTrace(); 55 | } 56 | 57 | kafkaTemplate.send(topic, jsonInString); 58 | log.info("Order Producer sent data from the Order microservice: " + kafkaOrderDto); 59 | 60 | return orderDto; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.service; 2 | 3 | import com.example.orderservice.dto.OrderDto; 4 | import com.example.orderservice.jpa.OrderEntity; 5 | 6 | public interface OrderService { 7 | OrderDto createOrder(OrderDto orderDetails); 8 | OrderDto getOrderByOrderId(String orderId); 9 | Iterable getOrdersByUserId(String userId); 10 | } 11 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/service/OrderServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.service; 2 | 3 | import com.example.orderservice.dto.OrderDto; 4 | import com.example.orderservice.jpa.OrderEntity; 5 | import com.example.orderservice.jpa.OrderRepository; 6 | import org.modelmapper.ModelMapper; 7 | import org.modelmapper.convention.MatchingStrategies; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.UUID; 12 | 13 | @Service 14 | public class OrderServiceImpl implements OrderService { 15 | OrderRepository orderRepository; 16 | 17 | @Autowired 18 | public OrderServiceImpl(OrderRepository orderRepository) { 19 | this.orderRepository = orderRepository; 20 | } 21 | 22 | @Override 23 | public OrderDto createOrder(OrderDto orderDto) { 24 | orderDto.setOrderId(UUID.randomUUID().toString()); 25 | orderDto.setTotalPrice(orderDto.getQty() * orderDto.getUnitPrice()); 26 | 27 | ModelMapper mapper = new ModelMapper(); 28 | mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); 29 | OrderEntity orderEntity = mapper.map(orderDto, OrderEntity.class); 30 | 31 | orderRepository.save(orderEntity); 32 | 33 | OrderDto returnValue = mapper.map(orderEntity, OrderDto.class); 34 | 35 | return returnValue; 36 | } 37 | 38 | @Override 39 | public OrderDto getOrderByOrderId(String orderId) { 40 | OrderEntity orderEntity = orderRepository.findByOrderId(orderId); 41 | OrderDto orderDto = new ModelMapper().map(orderEntity, OrderDto.class); 42 | 43 | return orderDto; 44 | } 45 | 46 | @Override 47 | public Iterable getOrdersByUserId(String userId) { 48 | return orderRepository.findByUserId(userId); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/vo/RequestOrder.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.vo; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class RequestOrder { 7 | private String productId; 8 | private Integer qty; 9 | private Integer unitPrice; 10 | } 11 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/vo/ResponseOrder.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | import java.util.Date; 7 | 8 | @Data 9 | @JsonInclude(JsonInclude.Include.NON_NULL) 10 | public class ResponseOrder { 11 | private String productId; 12 | private Integer qty; 13 | private Integer unitPrice; 14 | private Integer totalPrice; 15 | private Date createdAt; 16 | 17 | private String orderId; 18 | } 19 | -------------------------------------------------------------------------------- /order-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 0 3 | 4 | spring: 5 | application: 6 | name: order-service 7 | zipkin: 8 | base-url: http://localhost:9411 9 | enabled: true 10 | sleuth: 11 | sampler: 12 | probability: 1.0 13 | h2: 14 | console: 15 | enabled: true 16 | settings: 17 | web-allow-others: true 18 | path: /h2-console 19 | jpa: 20 | hibernate: 21 | ddl-auto: update 22 | datasource: 23 | driver-class-name: org.h2.Driver 24 | url: jdbc:h2:mem:testdb 25 | username: sa 26 | password: 27 | # driver-class-name: org.mariadb.jdbc.Driver 28 | # url: jdbc:mariadb://localhost:3306/mydb 29 | # username: root 30 | # password: test1357 31 | 32 | eureka: 33 | instance: 34 | instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}} 35 | client: 36 | register-with-eureka: true 37 | fetch-registry: true 38 | service-url: 39 | defaultZone: http://127.0.0.1:8761/eureka 40 | 41 | logging: 42 | level: 43 | com.example.orderservice: DEBUG 44 | 45 | management: 46 | endpoints: 47 | web: 48 | exposure: 49 | include: health, httptrace, info, metrics, prometheus 50 | -------------------------------------------------------------------------------- /order-service/src/test/java/com/example/orderservice/OrderServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.orderservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class OrderServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /pdf/Section 0. Microservice와 Spring Cloud 소개.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section 0. Microservice와 Spring Cloud 소개.pdf -------------------------------------------------------------------------------- /pdf/Section 1. Service Discovery.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section 1. Service Discovery.pdf -------------------------------------------------------------------------------- /pdf/Section 2. API Gateway Service.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section 2. API Gateway Service.pdf -------------------------------------------------------------------------------- /pdf/Section 3. E-commerce 애플리케이션.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section 3. E-commerce 애플리케이션.pdf -------------------------------------------------------------------------------- /pdf/Section 4. Users Microservice-part1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section 4. Users Microservice-part1.pdf -------------------------------------------------------------------------------- /pdf/Section 5. Catalogs, Orders Microservice.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section 5. Catalogs, Orders Microservice.pdf -------------------------------------------------------------------------------- /pdf/Section 6. Users Microservice-part2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section 6. Users Microservice-part2.pdf -------------------------------------------------------------------------------- /pdf/Section 7. Spring Config Server.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section 7. Spring Config Server.pdf -------------------------------------------------------------------------------- /pdf/Section 8. Spring Cloud Bus.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section 8. Spring Cloud Bus.pdf -------------------------------------------------------------------------------- /pdf/Section 9. Encryption and Decryption.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section 9. Encryption and Decryption.pdf -------------------------------------------------------------------------------- /pdf/Section10. 마이크로서비스간 통신.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section10. 마이크로서비스간 통신.pdf -------------------------------------------------------------------------------- /pdf/Section11. Kafka를 이용한 데이터동기화-part1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section11. Kafka를 이용한 데이터동기화-part1.pdf -------------------------------------------------------------------------------- /pdf/Section12. Kafka를 이용한 데이터동기화-part2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section12. Kafka를 이용한 데이터동기화-part2.pdf -------------------------------------------------------------------------------- /pdf/Section13. Resilience4J_Trace.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section13. Resilience4J_Trace.pdf -------------------------------------------------------------------------------- /pdf/Section14. Monitoring.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section14. Monitoring.pdf -------------------------------------------------------------------------------- /pdf/Section15. Docker.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section15. Docker.pdf -------------------------------------------------------------------------------- /pdf/Section16. Deployment.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/pdf/Section16. Deployment.pdf -------------------------------------------------------------------------------- /second-service/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /second-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/second-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /second-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /second-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.3.8.RELEASE 9 | 10 | 11 | com.example 12 | second-service 13 | 0.0.1-SNAPSHOT 14 | second-service 15 | Demo project for Spring Boot 16 | 17 | 11 18 | Hoxton.SR9 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-starter-netflix-eureka-client 28 | 29 | 30 | 31 | org.projectlombok 32 | lombok 33 | true 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | test 39 | 40 | 41 | org.junit.vintage 42 | junit-vintage-engine 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-dependencies 52 | ${spring-cloud.version} 53 | pom 54 | import 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-maven-plugin 64 | 65 | 66 | 67 | org.projectlombok 68 | lombok 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /second-service/src/main/java/com/example/secondservice/SecondServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.secondservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SecondServiceApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SecondServiceApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /second-service/src/main/java/com/example/secondservice/SecondServiceController.java: -------------------------------------------------------------------------------- 1 | package com.example.secondservice; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RequestHeader; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | @RestController 10 | @RequestMapping("/second-service") 11 | @Slf4j 12 | public class SecondServiceController { 13 | @GetMapping("/welcome") 14 | public String welcome() { 15 | return "Welcome to the Second service."; 16 | } 17 | 18 | @GetMapping("/message") 19 | public String message(@RequestHeader("second-request") String header) { 20 | log.info(header); 21 | return "Hello World in Second Service."; 22 | } 23 | 24 | @GetMapping("/check") 25 | public String check() { 26 | return "Hi, there. This is a message from Second Service."; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /second-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /second-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8082 3 | 4 | spring: 5 | application: 6 | name: my-second-service 7 | 8 | eureka: 9 | client: 10 | register-with-eureka: true 11 | fetch-registry: true 12 | service-url: 13 | defaultZone: http://localhost:8761/eureka 14 | -------------------------------------------------------------------------------- /second-service/src/test/java/com/example/secondservice/SecondServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.secondservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SecondServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /user-service-no-config/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /user-service-no-config/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/user-service-no-config/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /user-service-no-config/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /user-service-no-config/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17-ea-11-jdk-slim 2 | VOLUME /tmp 3 | COPY target/user-service-1.0.jar UserService.jar 4 | ENTRYPOINT ["java", "-jar", "UserService.jar"] -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/EncryptDemo.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | 5 | public class EncryptDemo { 6 | public static void main(String[] args) { 7 | // $2a$10$/FK01g0jzGvygjONWyne.O.x0ZM2O7ZCW2PyvTQx4wauDbMYgzVtG 8 | BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); 9 | System.out.println(passwordEncoder.matches("3333", 10 | "$2a$10$/FK01g0jzGvygjONWyne.O.x0ZM2O7ZCW2PyvTQx4wauDbMYgzVtG")); 11 | System.out.println(passwordEncoder.matches("3333", 12 | "$2a$10$D1SqS6SJuA2KV6v1PqnRGOpR4K4hp7eHcWEAl3uviGnDvXX5PC/n6")); 13 | System.out.println(passwordEncoder.matches("22222", 14 | "$2a$10$lQ6Hns9I//E/kMrSdTCMs.8ujSw6YOAnAlWaO/cBP2J79Pz5fFAHS")); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/UserServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice; 2 | 3 | import com.example.userservice.error.FeignErrorDecoder; 4 | import feign.Logger; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 8 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 9 | import org.springframework.cloud.openfeign.EnableFeignClients; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | import org.springframework.web.client.RestTemplate; 13 | 14 | @SpringBootApplication 15 | @EnableDiscoveryClient 16 | @EnableFeignClients 17 | public class UserServiceApplication { 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(UserServiceApplication.class, args); 21 | } 22 | 23 | @Bean 24 | public BCryptPasswordEncoder passwordEncoder() { 25 | return new BCryptPasswordEncoder(); 26 | } 27 | 28 | @Bean 29 | @LoadBalanced 30 | public RestTemplate getRestTemplate() { 31 | return new RestTemplate(); 32 | } 33 | 34 | @Bean 35 | public Logger.Level feignLoggerLevel() { 36 | return Logger.Level.FULL; 37 | } 38 | 39 | // @Bean 40 | // public FeignErrorDecoder getFeignErrorDecoder() { 41 | // return new FeignErrorDecoder(); 42 | // } 43 | } 44 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/client/OrderServiceClient.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.client; 2 | 3 | import com.example.userservice.vo.ResponseOrder; 4 | import org.springframework.cloud.openfeign.FeignClient; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | 8 | import java.util.List; 9 | 10 | @FeignClient(name="order-service") 11 | public interface OrderServiceClient { 12 | 13 | @GetMapping("/order-service/{userId}/orders") 14 | List getOrders(@PathVariable String userId); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/config/CustomContainer.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.config; 2 | 3 | import org.springframework.boot.web.server.WebServerFactoryCustomizer; 4 | import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.stereotype.Component; 8 | 9 | //@Component 10 | public class CustomContainer implements 11 | WebServerFactoryCustomizer { 12 | 13 | public void customize(ConfigurableServletWebServerFactory factory) { 14 | factory.setPort(8080); 15 | factory.setContextPath(""); 16 | } 17 | } -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/config/Resilience4JConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.config; 2 | 3 | import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; 4 | import io.github.resilience4j.timelimiter.TimeLimiterConfig; 5 | import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory; 6 | import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder; 7 | import org.springframework.cloud.client.circuitbreaker.Customizer; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | import java.time.Duration; 12 | 13 | @Configuration 14 | public class Resilience4JConfig { 15 | @Bean 16 | public Customizer globalCustomConfiguration() { 17 | CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() 18 | .failureRateThreshold(4) 19 | .waitDurationInOpenState(Duration.ofMillis(1000)) 20 | .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) 21 | .slidingWindowSize(2) 22 | .build(); 23 | 24 | TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom() 25 | .timeoutDuration(Duration.ofSeconds(4)) 26 | .build(); 27 | 28 | return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id) 29 | .timeLimiterConfig(timeLimiterConfig) 30 | .circuitBreakerConfig(circuitBreakerConfig) 31 | .build() 32 | ); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.controller; 2 | 3 | import com.example.userservice.dto.UserDto; 4 | import com.example.userservice.jpa.UserEntity; 5 | import com.example.userservice.service.UserService; 6 | import com.example.userservice.vo.Greeting; 7 | import com.example.userservice.vo.RequestUser; 8 | import com.example.userservice.vo.ResponseUser; 9 | import io.micrometer.core.annotation.Timed; 10 | import org.apache.catalina.User; 11 | import org.modelmapper.ModelMapper; 12 | import org.modelmapper.convention.MatchingStrategies; 13 | import org.modelmapper.spi.MatchingStrategy; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.core.env.Environment; 16 | import org.springframework.http.HttpStatus; 17 | import org.springframework.http.ResponseCookie; 18 | import org.springframework.http.ResponseEntity; 19 | import org.springframework.web.bind.annotation.*; 20 | 21 | import javax.servlet.http.Cookie; 22 | import javax.servlet.http.HttpServletRequest; 23 | import javax.servlet.http.HttpServletResponse; 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | 28 | @RestController 29 | @RequestMapping("/") 30 | public class UserController { 31 | private Environment env; 32 | private UserService userService; 33 | 34 | @Autowired 35 | private Greeting greeting; 36 | 37 | @Autowired 38 | public UserController(Environment env, UserService userService) { 39 | this.env = env; 40 | this.userService = userService; 41 | } 42 | 43 | @GetMapping("/health_check") 44 | @Timed(value="users.status", longTask = true) 45 | public String status() { 46 | return String.format("It's Working in User Service" 47 | + ", port(local.server.port)=" + env.getProperty("local.server.port") 48 | + ", port(server.port)=" + env.getProperty("server.port") 49 | + ", gateway ip=" + env.getProperty("gateway.ip") 50 | + ", message=" + env.getProperty("greeting.message") 51 | + ", token secret=" + env.getProperty("token.secret") 52 | + ", token expiration time=" + env.getProperty("token.expiration_time")); 53 | } 54 | 55 | @GetMapping("/welcome") 56 | @Timed(value="users.welcome", longTask = true) 57 | public String welcome(HttpServletRequest request, HttpServletResponse response) { 58 | // Cookie[] cookies = request.getCookies(); 59 | // if (cookies != null) { 60 | // Arrays.stream(cookies).forEach(cookie -> { 61 | // System.out.print(cookie.getName() + "=" + cookie.getValue()); 62 | // }); 63 | // } 64 | // Cookie c1 = new Cookie("myuser_token", "abcd1234"); 65 | // response.addCookie(c1); 66 | 67 | // return env.getProperty("greeting.message"); 68 | return greeting.getMessage(); 69 | } 70 | 71 | @PostMapping("/users") 72 | public ResponseEntity createUser(@RequestBody RequestUser user) { 73 | ModelMapper mapper = new ModelMapper(); 74 | mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); 75 | 76 | UserDto userDto = mapper.map(user, UserDto.class); 77 | userService.createUser(userDto); 78 | 79 | ResponseUser responseUser = mapper.map(userDto, ResponseUser.class); 80 | 81 | return ResponseEntity.status(HttpStatus.CREATED).body(responseUser); 82 | } 83 | 84 | @GetMapping("/users") 85 | public ResponseEntity> getUsers() { 86 | Iterable userList = userService.getUserByAll(); 87 | 88 | List result = new ArrayList<>(); 89 | userList.forEach(v -> { 90 | result.add(new ModelMapper().map(v, ResponseUser.class)); 91 | }); 92 | 93 | return ResponseEntity.status(HttpStatus.OK).body(result); 94 | } 95 | 96 | @GetMapping("/users/{userId}") 97 | public ResponseEntity getUser(@PathVariable("userId") String userId) { 98 | UserDto userDto = userService.getUserByUserId(userId); 99 | 100 | ResponseUser returnValue = new ModelMapper().map(userDto, ResponseUser.class); 101 | 102 | return ResponseEntity.status(HttpStatus.OK).body(returnValue); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/dto/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.dto; 2 | 3 | import com.example.userservice.vo.ResponseOrder; 4 | import lombok.Data; 5 | 6 | import java.util.Date; 7 | import java.util.List; 8 | 9 | @Data 10 | public class UserDto { 11 | private String email; 12 | private String name; 13 | private String pwd; 14 | private String userId; 15 | private Date createdAt; 16 | 17 | private String decryptedPwd; 18 | 19 | private String encryptedPwd; 20 | 21 | private List orders; 22 | } 23 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/error/FeignErrorDecoder.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.error; 2 | 3 | import feign.Response; 4 | import feign.codec.ErrorDecoder; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.core.env.Environment; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.server.ResponseStatusException; 10 | 11 | @Component 12 | public class FeignErrorDecoder implements ErrorDecoder { 13 | Environment env; 14 | 15 | @Autowired 16 | public FeignErrorDecoder(Environment env) { 17 | this.env = env; 18 | } 19 | 20 | @Override 21 | public Exception decode(String methodKey, Response response) { 22 | switch(response.status()) { 23 | case 400: 24 | break; 25 | case 404: 26 | if (methodKey.contains("getOrders")) { 27 | return new ResponseStatusException(HttpStatus.valueOf(response.status()), 28 | env.getProperty("order_service.exception.orders_is_empty")); 29 | } 30 | break; 31 | default: 32 | return new Exception(response.reason()); 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/jpa/UserEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.jpa; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | 7 | @Data 8 | @Entity 9 | @Table(name = "users") 10 | public class UserEntity { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | private Long id; 14 | 15 | @Column(nullable = false, length = 50, unique = true) 16 | private String email; 17 | @Column(nullable = false, length = 50) 18 | private String name; 19 | @Column(nullable = false, unique = true) 20 | private String userId; 21 | @Column(nullable = false, unique = true) 22 | private String encryptedPwd; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/jpa/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.jpa; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface UserRepository extends CrudRepository { 6 | UserEntity findByUserId(String userId); 7 | UserEntity findByEmail(String username); 8 | } 9 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/security/AuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.security; 2 | 3 | import com.example.userservice.dto.UserDto; 4 | import com.example.userservice.service.UserService; 5 | import com.example.userservice.vo.RequestLogin; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import io.jsonwebtoken.Jwts; 8 | import io.jsonwebtoken.SignatureAlgorithm; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.core.env.Environment; 11 | import org.springframework.security.authentication.AuthenticationManager; 12 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 13 | import org.springframework.security.core.Authentication; 14 | import org.springframework.security.core.AuthenticationException; 15 | import org.springframework.security.core.userdetails.User; 16 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 17 | 18 | import javax.servlet.FilterChain; 19 | import javax.servlet.ServletException; 20 | import javax.servlet.http.HttpServletRequest; 21 | import javax.servlet.http.HttpServletResponse; 22 | import java.io.IOException; 23 | import java.util.ArrayList; 24 | import java.util.Date; 25 | 26 | @Slf4j 27 | public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter { 28 | private UserService userService; 29 | private Environment env; 30 | 31 | public AuthenticationFilter(AuthenticationManager authenticationManager, 32 | UserService userService, 33 | Environment env) { 34 | super.setAuthenticationManager(authenticationManager); 35 | this.userService = userService; 36 | this.env = env; 37 | } 38 | 39 | @Override 40 | public Authentication attemptAuthentication(HttpServletRequest request, 41 | HttpServletResponse response) throws AuthenticationException { 42 | try { 43 | RequestLogin creds = new ObjectMapper().readValue(request.getInputStream(), RequestLogin.class); 44 | 45 | return getAuthenticationManager().authenticate( 46 | new UsernamePasswordAuthenticationToken( 47 | creds.getEmail(), 48 | creds.getPassword(), 49 | new ArrayList<>() 50 | ) 51 | ); 52 | } catch(IOException e) { 53 | throw new RuntimeException(e); 54 | } 55 | } 56 | 57 | @Override 58 | protected void successfulAuthentication(HttpServletRequest request, 59 | HttpServletResponse response, 60 | FilterChain chain, 61 | Authentication authResult) throws IOException, ServletException { 62 | String userName = ((User)authResult.getPrincipal()).getUsername(); 63 | UserDto userDetails = userService.getUserDetailsByEmail(userName); 64 | String token = Jwts.builder() 65 | .setSubject(userDetails.getUserId()) 66 | .setExpiration(new Date(System.currentTimeMillis() + 67 | Long.parseLong(env.getProperty("token.expiration_time")))) 68 | .signWith(SignatureAlgorithm.HS512, env.getProperty("token.secret")) 69 | .compact(); 70 | 71 | response.addHeader("token", token); 72 | response.addHeader("userId", userDetails.getUserId()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/security/WebSecurity.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.security; 2 | 3 | import com.example.userservice.service.UserService; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.core.env.Environment; 6 | import org.springframework.http.HttpMethod; 7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | 13 | import javax.servlet.Filter; 14 | 15 | @Configuration 16 | @EnableWebSecurity 17 | public class WebSecurity extends WebSecurityConfigurerAdapter { 18 | private UserService userService; 19 | private BCryptPasswordEncoder bCryptPasswordEncoder; 20 | private Environment env; 21 | 22 | public WebSecurity(Environment env, UserService userService, BCryptPasswordEncoder bCryptPasswordEncoder) { 23 | this.env = env; 24 | this.userService = userService; 25 | this.bCryptPasswordEncoder = bCryptPasswordEncoder; 26 | } 27 | 28 | @Override 29 | protected void configure(HttpSecurity http) throws Exception { 30 | http.csrf().disable(); 31 | // http.authorizeRequests().antMatchers("/users/**").permitAll(); 32 | 33 | http.authorizeRequests().antMatchers("/actuator/**").permitAll(); 34 | http.authorizeRequests().antMatchers("/health_check/**").permitAll(); 35 | http.authorizeRequests().antMatchers("/**") 36 | .hasIpAddress(env.getProperty("gateway.ip")) // <- IP 변경 37 | .and() 38 | .addFilter(getAuthenticationFilter()); 39 | 40 | // http.authorizeRequests().antMatchers("/users") 41 | // .hasIpAddress(env.getProperty("gateway.ip")) // <- IP 변경 42 | // .and() 43 | // .addFilter(getAuthenticationFilter()); 44 | // 45 | // http.authorizeRequests().anyRequest().denyAll(); 46 | 47 | http.headers().frameOptions().disable(); 48 | } 49 | 50 | private AuthenticationFilter getAuthenticationFilter() throws Exception { 51 | AuthenticationFilter authenticationFilter = 52 | new AuthenticationFilter(authenticationManager(), userService, env); 53 | 54 | return authenticationFilter; 55 | } 56 | 57 | // select pwd from users where email=? 58 | // db_pwd(encrypted) == input_pwd(encrypted) 59 | @Override 60 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 61 | auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.service; 2 | 3 | import com.example.userservice.dto.UserDto; 4 | import com.example.userservice.jpa.UserEntity; 5 | import org.springframework.security.core.userdetails.UserDetailsService; 6 | 7 | public interface UserService extends UserDetailsService { 8 | UserDto createUser(UserDto userDto); 9 | 10 | UserDto getUserByUserId(String userId); 11 | Iterable getUserByAll(); 12 | 13 | UserDto getUserDetailsByEmail(String userName); 14 | } 15 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/vo/Greeting.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @Data 11 | //@AllArgsConstructor 12 | //@NoArgsConstructor 13 | public class Greeting { 14 | @Value("${greeting.message}") 15 | private String message; 16 | } 17 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/vo/RequestLogin.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.Email; 6 | import javax.validation.constraints.NotNull; 7 | import javax.validation.constraints.Size; 8 | 9 | @Data 10 | public class RequestLogin { 11 | @NotNull(message = "Email cannot be null") 12 | @Size(min = 2, message = "Email not be less than two characters") 13 | @Email 14 | private String email; 15 | 16 | @NotNull(message = "Password cannot be null") 17 | @Size(min = 8, message = "Password must be equals or grater than 8 characters") 18 | private String password; 19 | } 20 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/vo/RequestUser.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.Email; 6 | import javax.validation.constraints.NotNull; 7 | import javax.validation.constraints.Size; 8 | 9 | @Data 10 | public class RequestUser { 11 | @NotNull(message = "Email cannot be null") 12 | @Size(min = 2, message = "Email not be less than two characters") 13 | @Email 14 | private String email; 15 | 16 | @NotNull(message = "Name cannot be null") 17 | @Size(min = 2, message = "Name not be less than two characters") 18 | private String name; 19 | 20 | @NotNull(message = "Password cannot be null") 21 | @Size(min = 8, message = "Password must be equal or grater than 8 characters") 22 | private String pwd; 23 | } 24 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/vo/ResponseOrder.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Date; 6 | 7 | @Data 8 | public class ResponseOrder { 9 | private String productId; 10 | private Integer qty; 11 | private Integer unitPrice; 12 | private Integer totalPrice; 13 | private Date createdAt; 14 | 15 | private String orderId; 16 | } 17 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/java/com/example/userservice/vo/ResponseUser.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | @Data 9 | @JsonInclude(JsonInclude.Include.NON_NULL) 10 | public class ResponseUser { 11 | private String email; 12 | private String name; 13 | private String userId; 14 | 15 | private List orders; 16 | } 17 | -------------------------------------------------------------------------------- /user-service-no-config/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 0 3 | # port: ${random.int(50000,50001)} 4 | # address: 127.0.0.1 5 | 6 | spring: 7 | application: 8 | name: user-service 9 | # zipkin: 10 | # base-url: http://127.0.0.1:9411 11 | # enabled: true 12 | # sleuth: 13 | # sampler: 14 | # probability: 1.0 15 | 16 | # rabbitmq: 17 | # host: 127.0.0.1 18 | # port: 5672 19 | # username: guest 20 | # password: guest 21 | h2: 22 | console: 23 | enabled: true 24 | settings: 25 | web-allow-others: true 26 | path: /h2-console 27 | datasource: 28 | driver-class-name: org.h2.Driver 29 | url: jdbc:h2:mem:testdb 30 | username: sa 31 | password: 12345 32 | 33 | eureka: 34 | instance: 35 | instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}} 36 | # prefer-ip-address: true 37 | # ip-address: ${server.address} 38 | client: 39 | service-url: 40 | defaultZone: http://127.0.0.1:8761/eureka 41 | fetch-registry: true 42 | register-with-eureka: true 43 | 44 | greeting: 45 | message: Welcome to the Simple E-commerce. 46 | 47 | logging: 48 | level: 49 | com.example.userservice.client: DEBUG 50 | 51 | gateway: 52 | # ip: 127.0.0.1 53 | ip: 10.204.136.204 54 | 55 | management: 56 | endpoints: 57 | web: 58 | exposure: 59 | include: refresh, health, beans, busrefresh, info, metrics, prometheus 60 | 61 | token: 62 | expiration_time: 86400000 63 | secret: user_token -------------------------------------------------------------------------------- /user-service-no-config/src/test/java/com/example/userservice/UserServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class UserServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /user-service-no-security/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /user-service-no-security/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/user-service-no-security/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /user-service-no-security/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /user-service-no-security/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17-ea-11-jdk-slim 2 | VOLUME /tmp 3 | COPY target/user-service-1.0.jar UserService.jar 4 | ENTRYPOINT ["java", "-jar", "UserService.jar"] -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/UserServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice; 2 | 3 | import feign.Logger; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 7 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 8 | import org.springframework.cloud.openfeign.EnableFeignClients; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 11 | import org.springframework.web.client.RestTemplate; 12 | 13 | @SpringBootApplication 14 | @EnableDiscoveryClient 15 | @EnableFeignClients 16 | public class UserServiceApplication { 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(UserServiceApplication.class, args); 20 | } 21 | 22 | @Bean 23 | public BCryptPasswordEncoder passwordEncoder() { 24 | return new BCryptPasswordEncoder(); 25 | } 26 | 27 | @Bean 28 | @LoadBalanced 29 | public RestTemplate getRestTemplate() { 30 | return new RestTemplate(); 31 | } 32 | 33 | @Bean 34 | public Logger.Level feignLoggerLevel() { 35 | return Logger.Level.FULL; 36 | } 37 | 38 | // @Bean 39 | // public FeignErrorDecoder getFeignErrorDecoder() { 40 | // return new FeignErrorDecoder(); 41 | // } 42 | } 43 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.controller; 2 | 3 | import com.example.userservice.dto.UserDto; 4 | import com.example.userservice.jpa.UserEntity; 5 | import com.example.userservice.service.UserService; 6 | import com.example.userservice.vo.Greeting; 7 | import com.example.userservice.vo.RequestUser; 8 | import com.example.userservice.vo.ResponseUser; 9 | import io.micrometer.core.annotation.Timed; 10 | import org.modelmapper.ModelMapper; 11 | import org.modelmapper.convention.MatchingStrategies; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.core.env.Environment; 14 | import org.springframework.http.HttpStatus; 15 | import org.springframework.http.ResponseEntity; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import javax.servlet.http.HttpServletRequest; 19 | import javax.servlet.http.HttpServletResponse; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | @RestController 24 | @RequestMapping("/") 25 | public class UserController { 26 | private Environment env; 27 | private UserService userService; 28 | 29 | @Autowired 30 | private Greeting greeting; 31 | 32 | @Autowired 33 | public UserController(Environment env, UserService userService) { 34 | this.env = env; 35 | this.userService = userService; 36 | } 37 | 38 | @GetMapping("/health_check") 39 | @Timed(value="users.status", longTask = true) 40 | public String status() { 41 | return String.format("It's Working in User Service" 42 | + ", port(local.server.port)=" + env.getProperty("local.server.port") 43 | + ", port(server.port)=" + env.getProperty("server.port") 44 | + ", gateway ip=" + env.getProperty("gateway.ip") 45 | + ", message=" + env.getProperty("greeting.message") 46 | + ", token secret=" + env.getProperty("token.secret") 47 | + ", token expiration time=" + env.getProperty("token.expiration_time")); 48 | } 49 | 50 | @GetMapping("/welcome") 51 | public String welcome(HttpServletRequest request, HttpServletResponse response) { 52 | return greeting.getMessage(); 53 | } 54 | 55 | @PostMapping("/users") 56 | public ResponseEntity createUser(@RequestBody RequestUser user) { 57 | ModelMapper mapper = new ModelMapper(); 58 | mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); 59 | 60 | UserDto userDto = mapper.map(user, UserDto.class); 61 | userService.createUser(userDto); 62 | 63 | ResponseUser responseUser = mapper.map(userDto, ResponseUser.class); 64 | 65 | return ResponseEntity.status(HttpStatus.CREATED).body(responseUser); 66 | } 67 | 68 | @GetMapping("/users") 69 | public ResponseEntity> getUsers() { 70 | Iterable userList = userService.getUserByAll(); 71 | 72 | List result = new ArrayList<>(); 73 | userList.forEach(v -> { 74 | result.add(new ModelMapper().map(v, ResponseUser.class)); 75 | }); 76 | 77 | return ResponseEntity.status(HttpStatus.OK).body(result); 78 | } 79 | 80 | @GetMapping("/users/{userId}") 81 | public ResponseEntity getUser(@PathVariable("userId") String userId) { 82 | UserDto userDto = userService.getUserByUserId(userId); 83 | 84 | ResponseUser returnValue = new ModelMapper().map(userDto, ResponseUser.class); 85 | 86 | return ResponseEntity.status(HttpStatus.OK).body(returnValue); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/dto/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.dto; 2 | 3 | import com.example.userservice.vo.ResponseOrder; 4 | import lombok.Data; 5 | 6 | import java.util.Date; 7 | import java.util.List; 8 | 9 | @Data 10 | public class UserDto { 11 | private String email; 12 | private String name; 13 | private String pwd; 14 | private String userId; 15 | private Date createdAt; 16 | 17 | private String decryptedPwd; 18 | 19 | private String encryptedPwd; 20 | 21 | private List orders; 22 | } 23 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/jpa/UserEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.jpa; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | 7 | @Data 8 | @Entity 9 | @Table(name = "users") 10 | public class UserEntity { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | private Long id; 14 | 15 | @Column(nullable = false, length = 50, unique = true) 16 | private String email; 17 | @Column(nullable = false, length = 50) 18 | private String name; 19 | @Column(nullable = false, unique = true) 20 | private String userId; 21 | @Column(nullable = false, unique = true) 22 | private String encryptedPwd; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/jpa/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.jpa; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface UserRepository extends CrudRepository { 6 | UserEntity findByUserId(String userId); 7 | UserEntity findByEmail(String username); 8 | } 9 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/security/WebSecurity.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.security; 2 | 3 | import com.example.userservice.service.UserService; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.core.env.Environment; 6 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 11 | import org.springframework.security.web.authentication.AuthenticationFilter; 12 | 13 | @Configuration 14 | @EnableWebSecurity 15 | public class WebSecurity extends WebSecurityConfigurerAdapter { 16 | private UserService userService; 17 | private BCryptPasswordEncoder bCryptPasswordEncoder; 18 | private Environment env; 19 | 20 | public WebSecurity(Environment env, UserService userService, BCryptPasswordEncoder bCryptPasswordEncoder) { 21 | this.env = env; 22 | this.userService = userService; 23 | this.bCryptPasswordEncoder = bCryptPasswordEncoder; 24 | } 25 | 26 | @Override 27 | protected void configure(HttpSecurity http) throws Exception { 28 | http.csrf().disable(); 29 | http.authorizeRequests().antMatchers("/users/**").permitAll(); 30 | http.headers().frameOptions().disable(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.service; 2 | 3 | import com.example.userservice.dto.UserDto; 4 | import com.example.userservice.jpa.UserEntity; 5 | 6 | public interface UserService { 7 | UserDto createUser(UserDto userDto); 8 | 9 | UserDto getUserByUserId(String userId); 10 | Iterable getUserByAll(); 11 | 12 | UserDto getUserDetailsByEmail(String userName); 13 | } 14 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.service; 2 | 3 | import com.example.userservice.dto.UserDto; 4 | import com.example.userservice.jpa.UserEntity; 5 | import com.example.userservice.jpa.UserRepository; 6 | import com.example.userservice.vo.ResponseOrder; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.modelmapper.ModelMapper; 9 | import org.modelmapper.convention.MatchingStrategies; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.core.env.Environment; 12 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 13 | import org.springframework.stereotype.Service; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.UUID; 18 | 19 | @Service 20 | @Slf4j 21 | public class UserServiceImpl implements UserService { 22 | UserRepository userRepository; 23 | BCryptPasswordEncoder passwordEncoder; 24 | Environment env; 25 | 26 | @Autowired 27 | public UserServiceImpl(UserRepository userRepository, 28 | BCryptPasswordEncoder passwordEncoder, 29 | Environment env) { 30 | this.userRepository = userRepository; 31 | this.passwordEncoder = passwordEncoder; 32 | this.env = env; 33 | } 34 | 35 | @Override 36 | public UserDto createUser(UserDto userDto) { 37 | userDto.setUserId(UUID.randomUUID().toString()); 38 | 39 | ModelMapper mapper = new ModelMapper(); 40 | mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); 41 | UserEntity userEntity = mapper.map(userDto, UserEntity.class); 42 | userEntity.setEncryptedPwd(passwordEncoder.encode(userDto.getPwd())); 43 | userRepository.save(userEntity); 44 | 45 | UserDto returnUserDto = mapper.map(userEntity, UserDto.class); 46 | 47 | return returnUserDto; 48 | } 49 | 50 | @Override 51 | public UserDto getUserByUserId(String userId) { 52 | UserEntity userEntity = userRepository.findByUserId(userId); 53 | 54 | if (userEntity == null) 55 | return null; 56 | 57 | UserDto userDto = new ModelMapper().map(userEntity, UserDto.class); 58 | 59 | List ordersList = new ArrayList<>(); 60 | userDto.setOrders(ordersList); 61 | 62 | return userDto; 63 | } 64 | 65 | @Override 66 | public Iterable getUserByAll() { 67 | return userRepository.findAll(); 68 | } 69 | 70 | @Override 71 | public UserDto getUserDetailsByEmail(String email) { 72 | UserEntity userEntity = userRepository.findByEmail(email); 73 | // if (userEntity == null) 74 | // throw new UsernameNotFoundException(email); 75 | 76 | ModelMapper mapper = new ModelMapper(); 77 | mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); 78 | 79 | UserDto userDto = mapper.map(userEntity, UserDto.class); 80 | return userDto; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/vo/Greeting.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @Data 11 | //@AllArgsConstructor 12 | //@NoArgsConstructor 13 | public class Greeting { 14 | @Value("${greeting.message}") 15 | private String message; 16 | } 17 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/vo/RequestLogin.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.Email; 6 | import javax.validation.constraints.NotNull; 7 | import javax.validation.constraints.Size; 8 | 9 | @Data 10 | public class RequestLogin { 11 | @NotNull(message = "Email cannot be null") 12 | @Size(min = 2, message = "Email not be less than two characters") 13 | @Email 14 | private String email; 15 | 16 | @NotNull(message = "Password cannot be null") 17 | @Size(min = 8, message = "Password must be equals or grater than 8 characters") 18 | private String password; 19 | } 20 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/vo/RequestUser.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.Email; 6 | import javax.validation.constraints.NotNull; 7 | import javax.validation.constraints.Size; 8 | 9 | @Data 10 | public class RequestUser { 11 | @NotNull(message = "Email cannot be null") 12 | @Size(min = 2, message = "Email not be less than two characters") 13 | @Email 14 | private String email; 15 | 16 | @NotNull(message = "Name cannot be null") 17 | @Size(min = 2, message = "Name not be less than two characters") 18 | private String name; 19 | 20 | @NotNull(message = "Password cannot be null") 21 | @Size(min = 8, message = "Password must be equal or grater than 8 characters") 22 | private String pwd; 23 | } 24 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/vo/ResponseOrder.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Date; 6 | 7 | @Data 8 | public class ResponseOrder { 9 | private String productId; 10 | private Integer qty; 11 | private Integer unitPrice; 12 | private Integer totalPrice; 13 | private Date createdAt; 14 | 15 | private String orderId; 16 | } 17 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/java/com/example/userservice/vo/ResponseUser.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | @Data 9 | @JsonInclude(JsonInclude.Include.NON_NULL) 10 | public class ResponseUser { 11 | private String email; 12 | private String name; 13 | private String userId; 14 | 15 | private List orders; 16 | } 17 | -------------------------------------------------------------------------------- /user-service-no-security/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 0 3 | 4 | spring: 5 | application: 6 | name: user-service 7 | h2: 8 | console: 9 | enabled: true 10 | settings: 11 | web-allow-others: true 12 | path: /h2-console 13 | datasource: 14 | driver-class-name: org.h2.Driver 15 | url: jdbc:h2:mem:testdb 16 | username: sa 17 | password: 12345 18 | 19 | eureka: 20 | instance: 21 | instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}} 22 | client: 23 | service-url: 24 | defaultZone: http://127.0.0.1:8761/eureka 25 | fetch-registry: true 26 | register-with-eureka: true 27 | 28 | greeting: 29 | message: Welcome to the Simple E-commerce. 30 | 31 | logging: 32 | level: 33 | com.example.userservice.client: DEBUG 34 | 35 | management: 36 | endpoints: 37 | web: 38 | exposure: 39 | include: refresh, health, beans, busrefresh, info, metrics, prometheus 40 | -------------------------------------------------------------------------------- /user-service-no-security/src/test/java/com/example/userservice/UserServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class UserServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /user-service/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /user-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/user-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /user-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /user-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17-ea-11-jdk-slim 2 | VOLUME /tmp 3 | COPY target/user-service-1.0.jar UserService.jar 4 | ENTRYPOINT ["java", "-jar", "UserService.jar"] -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/EncryptDemo.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | 5 | public class EncryptDemo { 6 | public static void main(String[] args) { 7 | // $2a$10$/FK01g0jzGvygjONWyne.O.x0ZM2O7ZCW2PyvTQx4wauDbMYgzVtG 8 | BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); 9 | System.out.println(passwordEncoder.matches("3333", 10 | "$2a$10$/FK01g0jzGvygjONWyne.O.x0ZM2O7ZCW2PyvTQx4wauDbMYgzVtG")); 11 | System.out.println(passwordEncoder.matches("3333", 12 | "$2a$10$D1SqS6SJuA2KV6v1PqnRGOpR4K4hp7eHcWEAl3uviGnDvXX5PC/n6")); 13 | System.out.println(passwordEncoder.matches("22222", 14 | "$2a$10$lQ6Hns9I//E/kMrSdTCMs.8ujSw6YOAnAlWaO/cBP2J79Pz5fFAHS")); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/UserServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice; 2 | 3 | import com.example.userservice.error.FeignErrorDecoder; 4 | import feign.Logger; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 8 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 9 | import org.springframework.cloud.openfeign.EnableFeignClients; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | import org.springframework.web.client.RestTemplate; 13 | 14 | @SpringBootApplication 15 | @EnableDiscoveryClient 16 | @EnableFeignClients 17 | public class UserServiceApplication { 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(UserServiceApplication.class, args); 21 | } 22 | 23 | @Bean 24 | public BCryptPasswordEncoder passwordEncoder() { 25 | return new BCryptPasswordEncoder(); 26 | } 27 | 28 | @Bean 29 | @LoadBalanced 30 | public RestTemplate getRestTemplate() { 31 | return new RestTemplate(); 32 | } 33 | 34 | @Bean 35 | public Logger.Level feignLoggerLevel() { 36 | return Logger.Level.FULL; 37 | } 38 | 39 | // @Bean 40 | // public FeignErrorDecoder getFeignErrorDecoder() { 41 | // return new FeignErrorDecoder(); 42 | // } 43 | } 44 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/client/OrderServiceClient.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.client; 2 | 3 | import com.example.userservice.vo.ResponseOrder; 4 | import org.springframework.cloud.openfeign.FeignClient; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | 8 | import java.util.List; 9 | 10 | @FeignClient(name="order-service") 11 | public interface OrderServiceClient { 12 | 13 | @GetMapping("/order-service/{userId}/orders") 14 | List getOrders(@PathVariable String userId); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/config/CustomContainer.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.config; 2 | 3 | import org.springframework.boot.web.server.WebServerFactoryCustomizer; 4 | import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.stereotype.Component; 8 | 9 | //@Component 10 | public class CustomContainer implements 11 | WebServerFactoryCustomizer { 12 | 13 | public void customize(ConfigurableServletWebServerFactory factory) { 14 | factory.setPort(8080); 15 | factory.setContextPath(""); 16 | } 17 | } -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/config/Resilience4JConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.config; 2 | 3 | import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; 4 | import io.github.resilience4j.timelimiter.TimeLimiterConfig; 5 | import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory; 6 | import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder; 7 | import org.springframework.cloud.client.circuitbreaker.Customizer; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | import java.time.Duration; 12 | 13 | @Configuration 14 | public class Resilience4JConfig { 15 | @Bean 16 | public Customizer globalCustomConfiguration() { 17 | CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() 18 | .failureRateThreshold(4) 19 | .waitDurationInOpenState(Duration.ofMillis(1000)) 20 | .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) 21 | .slidingWindowSize(2) 22 | .build(); 23 | 24 | TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom() 25 | .timeoutDuration(Duration.ofSeconds(4)) 26 | .build(); 27 | 28 | return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id) 29 | .timeLimiterConfig(timeLimiterConfig) 30 | .circuitBreakerConfig(circuitBreakerConfig) 31 | .build() 32 | ); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.controller; 2 | 3 | import com.example.userservice.dto.UserDto; 4 | import com.example.userservice.jpa.UserEntity; 5 | import com.example.userservice.service.UserService; 6 | import com.example.userservice.vo.Greeting; 7 | import com.example.userservice.vo.RequestUser; 8 | import com.example.userservice.vo.ResponseUser; 9 | import io.micrometer.core.annotation.Timed; 10 | import org.apache.catalina.User; 11 | import org.modelmapper.ModelMapper; 12 | import org.modelmapper.convention.MatchingStrategies; 13 | import org.modelmapper.spi.MatchingStrategy; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.core.env.Environment; 16 | import org.springframework.http.HttpStatus; 17 | import org.springframework.http.ResponseCookie; 18 | import org.springframework.http.ResponseEntity; 19 | import org.springframework.web.bind.annotation.*; 20 | 21 | import javax.servlet.http.Cookie; 22 | import javax.servlet.http.HttpServletRequest; 23 | import javax.servlet.http.HttpServletResponse; 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | 28 | @RestController 29 | @RequestMapping("/") 30 | public class UserController { 31 | private Environment env; 32 | private UserService userService; 33 | 34 | @Autowired 35 | private Greeting greeting; 36 | 37 | @Autowired 38 | public UserController(Environment env, UserService userService) { 39 | this.env = env; 40 | this.userService = userService; 41 | } 42 | 43 | @GetMapping("/health_check") 44 | @Timed(value="users.status", longTask = true) 45 | public String status() { 46 | return String.format("It's Working in User Service" 47 | + ", port(local.server.port)=" + env.getProperty("local.server.port") 48 | + ", port(server.port)=" + env.getProperty("server.port") 49 | + ", gateway ip=" + env.getProperty("gateway.ip") 50 | + ", message=" + env.getProperty("greeting.message") 51 | + ", token secret=" + env.getProperty("token.secret") 52 | + ", token expiration time=" + env.getProperty("token.expiration_time")); 53 | } 54 | 55 | @GetMapping("/welcome") 56 | @Timed(value="users.welcome", longTask = true) 57 | public String welcome(HttpServletRequest request, HttpServletResponse response) { 58 | // Cookie[] cookies = request.getCookies(); 59 | // if (cookies != null) { 60 | // Arrays.stream(cookies).forEach(cookie -> { 61 | // System.out.print(cookie.getName() + "=" + cookie.getValue()); 62 | // }); 63 | // } 64 | // Cookie c1 = new Cookie("myuser_token", "abcd1234"); 65 | // response.addCookie(c1); 66 | 67 | // return env.getProperty("greeting.message"); 68 | return greeting.getMessage(); 69 | } 70 | 71 | @PostMapping("/users") 72 | public ResponseEntity createUser(@RequestBody RequestUser user) { 73 | ModelMapper mapper = new ModelMapper(); 74 | mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); 75 | 76 | UserDto userDto = mapper.map(user, UserDto.class); 77 | userService.createUser(userDto); 78 | 79 | ResponseUser responseUser = mapper.map(userDto, ResponseUser.class); 80 | 81 | return ResponseEntity.status(HttpStatus.CREATED).body(responseUser); 82 | } 83 | 84 | @GetMapping("/users") 85 | public ResponseEntity> getUsers() { 86 | Iterable userList = userService.getUserByAll(); 87 | 88 | List result = new ArrayList<>(); 89 | userList.forEach(v -> { 90 | result.add(new ModelMapper().map(v, ResponseUser.class)); 91 | }); 92 | 93 | return ResponseEntity.status(HttpStatus.OK).body(result); 94 | } 95 | 96 | @GetMapping("/users/{userId}") 97 | public ResponseEntity getUser(@PathVariable("userId") String userId) { 98 | UserDto userDto = userService.getUserByUserId(userId); 99 | 100 | ResponseUser returnValue = new ModelMapper().map(userDto, ResponseUser.class); 101 | 102 | return ResponseEntity.status(HttpStatus.OK).body(returnValue); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/dto/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.dto; 2 | 3 | import com.example.userservice.vo.ResponseOrder; 4 | import lombok.Data; 5 | 6 | import java.util.Date; 7 | import java.util.List; 8 | 9 | @Data 10 | public class UserDto { 11 | private String email; 12 | private String name; 13 | private String pwd; 14 | private String userId; 15 | private Date createdAt; 16 | 17 | private String decryptedPwd; 18 | 19 | private String encryptedPwd; 20 | 21 | private List orders; 22 | } 23 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/error/FeignErrorDecoder.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.error; 2 | 3 | import feign.Response; 4 | import feign.codec.ErrorDecoder; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.core.env.Environment; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.server.ResponseStatusException; 10 | 11 | @Component 12 | public class FeignErrorDecoder implements ErrorDecoder { 13 | Environment env; 14 | 15 | @Autowired 16 | public FeignErrorDecoder(Environment env) { 17 | this.env = env; 18 | } 19 | 20 | @Override 21 | public Exception decode(String methodKey, Response response) { 22 | switch(response.status()) { 23 | case 400: 24 | break; 25 | case 404: 26 | if (methodKey.contains("getOrders")) { 27 | return new ResponseStatusException(HttpStatus.valueOf(response.status()), 28 | env.getProperty("order_service.exception.orders_is_empty")); 29 | } 30 | break; 31 | default: 32 | return new Exception(response.reason()); 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/jpa/UserEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.jpa; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | 7 | @Data 8 | @Entity 9 | @Table(name = "users") 10 | public class UserEntity { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | private Long id; 14 | 15 | @Column(nullable = false, length = 50, unique = true) 16 | private String email; 17 | @Column(nullable = false, length = 50) 18 | private String name; 19 | @Column(nullable = false, unique = true) 20 | private String userId; 21 | @Column(nullable = false, unique = true) 22 | private String encryptedPwd; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/jpa/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.jpa; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface UserRepository extends CrudRepository { 6 | UserEntity findByUserId(String userId); 7 | UserEntity findByEmail(String username); 8 | } 9 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/security/AuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.security; 2 | 3 | import com.example.userservice.dto.UserDto; 4 | import com.example.userservice.service.UserService; 5 | import com.example.userservice.vo.RequestLogin; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import io.jsonwebtoken.Jwts; 8 | import io.jsonwebtoken.SignatureAlgorithm; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.core.env.Environment; 11 | import org.springframework.security.authentication.AuthenticationManager; 12 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 13 | import org.springframework.security.core.Authentication; 14 | import org.springframework.security.core.AuthenticationException; 15 | import org.springframework.security.core.userdetails.User; 16 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 17 | 18 | import javax.servlet.FilterChain; 19 | import javax.servlet.ServletException; 20 | import javax.servlet.http.HttpServletRequest; 21 | import javax.servlet.http.HttpServletResponse; 22 | import java.io.IOException; 23 | import java.util.ArrayList; 24 | import java.util.Date; 25 | 26 | @Slf4j 27 | public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter { 28 | private UserService userService; 29 | private Environment env; 30 | 31 | public AuthenticationFilter(AuthenticationManager authenticationManager, 32 | UserService userService, 33 | Environment env) { 34 | super.setAuthenticationManager(authenticationManager); 35 | this.userService = userService; 36 | this.env = env; 37 | } 38 | 39 | @Override 40 | public Authentication attemptAuthentication(HttpServletRequest request, 41 | HttpServletResponse response) throws AuthenticationException { 42 | try { 43 | RequestLogin creds = new ObjectMapper().readValue(request.getInputStream(), RequestLogin.class); 44 | 45 | return getAuthenticationManager().authenticate( 46 | new UsernamePasswordAuthenticationToken( 47 | creds.getEmail(), 48 | creds.getPassword(), 49 | new ArrayList<>() 50 | ) 51 | ); 52 | } catch(IOException e) { 53 | throw new RuntimeException(e); 54 | } 55 | } 56 | 57 | @Override 58 | protected void successfulAuthentication(HttpServletRequest request, 59 | HttpServletResponse response, 60 | FilterChain chain, 61 | Authentication authResult) throws IOException, ServletException { 62 | String userName = ((User)authResult.getPrincipal()).getUsername(); 63 | UserDto userDetails = userService.getUserDetailsByEmail(userName); 64 | String token = Jwts.builder() 65 | .setSubject(userDetails.getUserId()) 66 | .setExpiration(new Date(System.currentTimeMillis() + 67 | Long.parseLong(env.getProperty("token.expiration_time")))) 68 | .signWith(SignatureAlgorithm.HS512, env.getProperty("token.secret")) 69 | .compact(); 70 | 71 | response.addHeader("token", token); 72 | response.addHeader("userId", userDetails.getUserId()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/security/WebSecurity.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.security; 2 | 3 | import com.example.userservice.service.UserService; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.core.env.Environment; 6 | import org.springframework.http.HttpMethod; 7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | 13 | import javax.servlet.Filter; 14 | 15 | @Configuration 16 | @EnableWebSecurity 17 | public class WebSecurity extends WebSecurityConfigurerAdapter { 18 | private UserService userService; 19 | private BCryptPasswordEncoder bCryptPasswordEncoder; 20 | private Environment env; 21 | 22 | public WebSecurity(Environment env, UserService userService, BCryptPasswordEncoder bCryptPasswordEncoder) { 23 | this.env = env; 24 | this.userService = userService; 25 | this.bCryptPasswordEncoder = bCryptPasswordEncoder; 26 | } 27 | 28 | @Override 29 | protected void configure(HttpSecurity http) throws Exception { 30 | http.csrf().disable(); 31 | // http.authorizeRequests().antMatchers("/users/**").permitAll(); 32 | 33 | http.authorizeRequests().antMatchers("/actuator/**").permitAll(); 34 | http.authorizeRequests().antMatchers("/health_check/**").permitAll(); 35 | http.authorizeRequests().antMatchers("/**") 36 | .hasIpAddress(env.getProperty("gateway.ip")) // <- IP 변경 37 | .and() 38 | .addFilter(getAuthenticationFilter()); 39 | 40 | // http.authorizeRequests().antMatchers("/users") 41 | // .hasIpAddress(env.getProperty("gateway.ip")) // <- IP 변경 42 | // .and() 43 | // .addFilter(getAuthenticationFilter()); 44 | // 45 | // http.authorizeRequests().anyRequest().denyAll(); 46 | 47 | http.headers().frameOptions().disable(); 48 | } 49 | 50 | private AuthenticationFilter getAuthenticationFilter() throws Exception { 51 | AuthenticationFilter authenticationFilter = 52 | new AuthenticationFilter(authenticationManager(), userService, env); 53 | 54 | return authenticationFilter; 55 | } 56 | 57 | // select pwd from users where email=? 58 | // db_pwd(encrypted) == input_pwd(encrypted) 59 | @Override 60 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 61 | auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.service; 2 | 3 | import com.example.userservice.dto.UserDto; 4 | import com.example.userservice.jpa.UserEntity; 5 | import org.springframework.security.core.userdetails.UserDetailsService; 6 | 7 | public interface UserService extends UserDetailsService { 8 | UserDto createUser(UserDto userDto); 9 | 10 | UserDto getUserByUserId(String userId); 11 | Iterable getUserByAll(); 12 | 13 | UserDto getUserDetailsByEmail(String userName); 14 | } 15 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/vo/Greeting.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @Data 11 | //@AllArgsConstructor 12 | //@NoArgsConstructor 13 | public class Greeting { 14 | @Value("${greeting.message}") 15 | private String message; 16 | } 17 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/vo/RequestLogin.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.Email; 6 | import javax.validation.constraints.NotNull; 7 | import javax.validation.constraints.Size; 8 | 9 | @Data 10 | public class RequestLogin { 11 | @NotNull(message = "Email cannot be null") 12 | @Size(min = 2, message = "Email not be less than two characters") 13 | @Email 14 | private String email; 15 | 16 | @NotNull(message = "Password cannot be null") 17 | @Size(min = 8, message = "Password must be equals or grater than 8 characters") 18 | private String password; 19 | } 20 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/vo/RequestUser.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.Email; 6 | import javax.validation.constraints.NotNull; 7 | import javax.validation.constraints.Size; 8 | 9 | @Data 10 | public class RequestUser { 11 | @NotNull(message = "Email cannot be null") 12 | @Size(min = 2, message = "Email not be less than two characters") 13 | @Email 14 | private String email; 15 | 16 | @NotNull(message = "Name cannot be null") 17 | @Size(min = 2, message = "Name not be less than two characters") 18 | private String name; 19 | 20 | @NotNull(message = "Password cannot be null") 21 | @Size(min = 8, message = "Password must be equal or grater than 8 characters") 22 | private String pwd; 23 | } 24 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/vo/ResponseOrder.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Date; 6 | 7 | @Data 8 | public class ResponseOrder { 9 | private String productId; 10 | private Integer qty; 11 | private Integer unitPrice; 12 | private Integer totalPrice; 13 | private Date createdAt; 14 | 15 | private String orderId; 16 | } 17 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/example/userservice/vo/ResponseUser.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | 8 | @Data 9 | @JsonInclude(JsonInclude.Include.NON_NULL) 10 | public class ResponseUser { 11 | private String email; 12 | private String name; 13 | private String userId; 14 | 15 | private List orders; 16 | } 17 | -------------------------------------------------------------------------------- /user-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 0 3 | # port: ${random.int(50000,50001)} 4 | # address: 127.0.0.1 5 | 6 | spring: 7 | application: 8 | name: user-service 9 | # zipkin: 10 | # base-url: http://127.0.0.1:9411 11 | # enabled: true 12 | # sleuth: 13 | # sampler: 14 | # probability: 1.0 15 | 16 | rabbitmq: 17 | host: 127.0.0.1 18 | port: 5672 19 | username: guest 20 | password: guest 21 | h2: 22 | console: 23 | enabled: true 24 | settings: 25 | web-allow-others: true 26 | path: /h2-console 27 | # datasource: 28 | # driver-class-name: org.h2.Driver 29 | # url: jdbc:h2:mem:testdb 30 | # username: sa 31 | # password: 12345 32 | 33 | eureka: 34 | instance: 35 | instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}} 36 | # prefer-ip-address: true 37 | # ip-address: ${server.address} 38 | client: 39 | service-url: 40 | defaultZone: http://127.0.0.1:8761/eureka 41 | fetch-registry: true 42 | register-with-eureka: true 43 | 44 | greeting: 45 | message: Welcome to the Simple E-commerce. 46 | 47 | logging: 48 | level: 49 | com.example.userservice.client: DEBUG 50 | 51 | management: 52 | endpoints: 53 | web: 54 | exposure: 55 | include: refresh, health, beans, busrefresh, info, metrics, prometheus 56 | 57 | #token: 58 | # expiration_time: 86400000 59 | # secret: user_token -------------------------------------------------------------------------------- /user-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | uri: http://127.0.0.1:8888 5 | name: user-service 6 | # active: dev 7 | -------------------------------------------------------------------------------- /user-service/src/test/java/com/example/userservice/UserServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.userservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class UserServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /zuul-service/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /zuul-service/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joneconsulting/msa_with_spring_cloud/5827d698c9793f1799eeafb26002cab50cd65d5d/zuul-service/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /zuul-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /zuul-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.3.10.RELEASE 9 | 10 | 11 | com.example 12 | zuul-service 13 | 0.0.1-SNAPSHOT 14 | zuul-service 15 | Demo project for Spring Boot 16 | 17 | 11 18 | Hoxton.SR9 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-starter-netflix-zuul 28 | 29 | 30 | 31 | org.projectlombok 32 | lombok 33 | true 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | test 39 | 40 | 41 | org.junit.vintage 42 | junit-vintage-engine 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-dependencies 52 | ${spring-cloud.version} 53 | pom 54 | import 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-maven-plugin 64 | 65 | 66 | 67 | org.projectlombok 68 | lombok 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /zuul-service/src/main/java/com/example/zuulservice/ZuulServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.zuulservice; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 6 | 7 | @SpringBootApplication 8 | @EnableZuulProxy 9 | public class ZuulServiceApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(ZuulServiceApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /zuul-service/src/main/java/com/example/zuulservice/filter/ZuulLoggingFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.zuulservice.filter; 2 | 3 | import com.netflix.zuul.ZuulFilter; 4 | import com.netflix.zuul.context.RequestContext; 5 | import com.netflix.zuul.exception.ZuulException; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.stereotype.Component; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | 12 | @Slf4j 13 | @Component 14 | public class ZuulLoggingFilter extends ZuulFilter { 15 | 16 | @Override 17 | public Object run() throws ZuulException { 18 | log.info("**************** printing logs: "); 19 | 20 | RequestContext ctx = RequestContext.getCurrentContext(); 21 | HttpServletRequest request = ctx.getRequest(); 22 | log.info("**************** " + request.getRequestURI()); 23 | 24 | return null; 25 | } 26 | 27 | @Override 28 | public String filterType() { 29 | return "pre"; 30 | } 31 | 32 | @Override 33 | public int filterOrder() { 34 | return 1; 35 | } 36 | 37 | @Override 38 | public boolean shouldFilter() { 39 | return true; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /zuul-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8000 3 | 4 | spring: 5 | application: 6 | name: my-zuul-service 7 | 8 | zuul: 9 | routes: 10 | first-service: 11 | path: /first-service/** 12 | url: http://localhost:8081 13 | second-service: 14 | path: /second-service/** 15 | url: http://localhost:8082 16 | -------------------------------------------------------------------------------- /zuul-service/src/test/java/com/example/zuulservice/ZuulServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.zuulservice; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ZuulServiceApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------