paulwong

          #

          EVEN DRIVEN - SPRING CLOUD STREAM - SPRING CLOUD微服務的EVEN DRIVEN框架

          通常微服務應用之間的通信是通過HTTP調用,吞吐性不建都高,高并發的場景建議使用EVENT DRIVEN的框架,即使用MESSAGE通信。

          即A微服務應用將數據發送到MESSAGE BROKER中的某個DESTINATION,此DESTINATION是廣播型,非點對點型。B微服務應用訂閱此DESTINATION,當有新MESSAGE到達此DESTINATION時,MESSAGE BROKER會將此MESSAGE推送給B應用。所有對此MESSAGE有需要的應用均可訂閱,從而收到此MESSAGE。

          SPRING CLOUD 中EVENT DRIVEN的框架就是SPRING CLOUD STREAM。其底層是使用SPRING INTEGRATION實現。

          SPRING CLOUD STREAM有以下新名詞:

          • BINDER:
          是對MESSAGE BROKER操作方法的抽象,即應用通過此BINDER操作MESSAGE BROKER。目前只實現了RABITMQ和KAFKA。
          • CHANNEL
          MESSAGE從SPRING CLOUD STREAM傳給應用或相反是通過CHANNEL傳遞的,這點和SPRING INTEGRATION是一樣的。
          • SOURCE
          MESSAGE從應用傳給SPRING CLOUD STREAM的CHANNEL,叫@INPUT,包含這種CHANNEL的接口叫SOURCE。
          • SINK
          MESSAGE從SPRING CLOUD STREAM傳給應用的CHANNEL,叫@OUPUT,包含這種CHANNEL的接口叫SINK。
          • BIDDING
          綁定哪個@INPUT或哪個@OUPUT與哪個DESTINATION發送或接收關系的MAPPING。
          • EnableBinding
          應用啟動時就會建立EnableBinding指定的接口中的CHANNEL
          • 消費者群組
          默認下如果同一個應用部署了多個實例,則每個實例都會收到MESSAGE,這時如果設置了消費者群組名稱,則同一個名稱下的多個實例,只有一個能收到MESSAGE。
          • PARTITION
          如果為MESSAGE指定規則,如MESSAGE某個字段值以A開頭為一個規則,以B開頭為一個規則,那么以A開頭的MESSAGE會放到同一個分區中。

          這樣使用就很簡單了,只要取得OUTPUT CHANNEL,就可以發送MESSAGE,將代碼關聯到INPUT CHANNEL,就能在收到MESSAGE時,相關代碼就會被執行。

          posted @ 2021-11-05 14:58 paulwong 閱讀(209) | 評論 (0)編輯 收藏

          OAUTH2 - SPRING SECURITY + KEYCLOAK

               摘要: 根據OAUTH2協議,如果需要用戶協助的,則使用authorization_code流程,此時需要用戶登錄頁面、CLIENT SERVER、RESOURCE SERVER和AUTHORIZATION SERVER,其中CLIENT SERVER是通過http調用RESOURCE SERVER的api,AUTHORIZATION SERVER使用現成的KEYCLOAK。如果不需要用戶協助的,即SER...  閱讀全文

          posted @ 2021-11-03 16:58 paulwong 閱讀(753) | 評論 (0)編輯 收藏

          SPRING BOOT OAUTH2 + KEYCLOAK - service to service call

          employee-service調用department-service,如果要按OAUTH2.0流程,只需要提供client-id和client-secrect即可。在KEYCLOAK中引入service-account,即配置該employee-service時,取消standard-flow,同時激活service-account。
          employee-service的application.yaml文件,其中的public-key要從KEYCLOAK中取
          server:
             port: 8090
          # Can be set to false to disable security during local development
          rest:
             security:
                enabled: true
                #issuer-uri: http://localhost:8080/auth/realms/dev
                api-matcher: /api/**
                cors:
                   allowed-origins: '*'
                   allowed-headers: '*'
                   allowed-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
                   max-age: 3600

          security:
             oauth2:
                resource:
                   filter-order: 3
                   id: test-employee-service
                   token-info-uri: ${rest.security.issuer-uri}/protocol/openid-connect/token/introspect
                   user-info-uri: ${rest.security.issuer-uri}/protocol/openid-connect/userinfo
                   jwt:
                      key-value: | 
                         -----BEGIN PUBLIC KEY-----
                         MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB
                         -----END PUBLIC KEY-----

          # To access another secured micro-service
                client:
                   client-id: test-employee-service
                   #client-secret: 25c33006-e1b9-4fc2-a6b9-c43dbc41ecd0
                   user-authorization-uri: ${rest.security.issuer-uri}/protocol/openid-connect/auth
                   access-token-uri: ${rest.security.issuer-uri}/protocol/openid-connect/token
                   scope: openid
                   grant-type: client_credentials
                   is-client-only: true

          #Logging Configuration
          logging:
             level:
                org.springframework.boot.autoconfigure.logging: INFO
                org.springframework.security: DEBUG
                org.arun: DEBUG
                root: INFO

          application-dev.yaml
          rest:
             security:
                issuer-uri: http://10.80.27.69:8180/auth/realms/quickstart

          department-service:
             url: http://10.80.27.69:8095/api/departments/1

          security:
             oauth2:
                client:
                   client-secret: db25cdbd-605b-429d-bd92-96705bdf1474

          department-service的application.yaml
          server:
             port: 8095
          # Can be set to false to disable security during local development
          rest:
             security:
                enabled: true
                #issuer-uri: http://localhost:8080/auth/realms/dev
                api-matcher: /api/**
                cors:
                   allowed-origins: '*'
                   allowed-headers: '*'
                   allowed-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
                   max-age: 3600

          security:
             oauth2:
                resource:
                   filter-order: 3
                   id: test-department-service
                   token-info-uri: ${rest.security.issuer-uri}/protocol/openid-connect/token/introspect
                   user-info-uri: ${rest.security.issuer-uri}/protocol/openid-connect/userinfo
                   jwt:
                      key-value: | 
                         -----BEGIN PUBLIC KEY-----
                         MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB
                         -----END PUBLIC KEY-----

          #Logging Configuration
          logging:
             level:
                org.springframework.boot.autoconfigure.logging: INFO
                org.springframework.security: DEBUG
                org.arun: DEBUG
                root: INFO

          application-dev.yaml
          rest:
             security:
                issuer-uri: http://10.80.27.69:8180/auth/realms/quickstart

          employee-service的pom.xml
          <?xml version="1.0" encoding="UTF-8"?>
          <project xmlns="http://maven.apache.org/POM/4.0.0"
              xmlns:xsi
          ="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation
          ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
              <modelVersion>4.0.0</modelVersion>
              <parent>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-parent</artifactId>
                  <version>2.1.18.RELEASE</version>
                  <relativePath /> <!-- lookup parent from repository -->
              </parent>
              <groupId>org.arun.springoauth</groupId>
              <artifactId>spring-oauth2-employee-service</artifactId>
              <version>1.0.0</version>
              <name>spring-oauth2-employee-service</name>
              <description>Employee Service</description>

              <properties>
                  <java.version>1.8</java.version>
                  <spring-boot.version>2.1.18.RELEASE</spring-boot.version>
              </properties>

              <dependencies>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-security</artifactId>
                  </dependency>

                  <dependency>
                      <groupId>org.springframework.security.oauth.boot</groupId>
                      <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                      <!-- <version>2.1.18.RELEASE</version> -->
                      <version>${spring-boot.version}</version>
                  </dependency>

                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-web</artifactId>
                  </dependency>

                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-devtools</artifactId>
                      <scope>runtime</scope>
                  </dependency>

                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-configuration-processor</artifactId>
                      <optional>true</optional>
                  </dependency>

                  <dependency>
                      <groupId>org.projectlombok</groupId>
                      <artifactId>lombok</artifactId>
                      <optional>true</optional>
                  </dependency>

                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-test</artifactId>
                      <scope>test</scope>
                  </dependency>

                  <dependency>
                      <groupId>org.springframework.security</groupId>
                      <artifactId>spring-security-test</artifactId>
                      <scope>test</scope>
                  </dependency>
              </dependencies>

              <build>
                  <plugins>
                      <plugin>
                          <groupId>org.springframework.boot</groupId>
                          <artifactId>spring-boot-maven-plugin</artifactId>
                          <configuration>
                              <layout>ZIP</layout>
                              <excludes>
                                  <exclude>
                                      <groupId>*</groupId>
                                      <artifactId>*</artifactId>
                                  </exclude>
                              </excludes>
                              <includes>
                                  <include>
                                      <groupId>com.paul</groupId>
                                  </include>
                              </includes>
                          </configuration>
                      </plugin>
                  </plugins>
              </build>

          </project>

          將jwt格式的access_token轉成Authentication的類JwtAccessTokenCustomizer
          package org.arun.springoauth.employee.config;

          import com.fasterxml.jackson.databind.JsonNode;
          import com.fasterxml.jackson.databind.ObjectMapper;
          import java.util.HashSet;
          import java.util.List;
          import java.util.Map;
          import java.util.Set;
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.boot.autoconfigure.security.oauth2.resource.JwtAccessTokenConverterConfigurer;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
          import org.springframework.security.core.Authentication;
          import org.springframework.security.core.GrantedAuthority;
          import org.springframework.security.core.authority.AuthorityUtils;
          import org.springframework.security.oauth2.provider.OAuth2Authentication;
          import org.springframework.security.oauth2.provider.OAuth2Request;
          import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
          import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

          @Configuration
          public class JwtAccessTokenCustomizer extends DefaultAccessTokenConverter implements JwtAccessTokenConverterConfigurer {

              private static final Logger LOG = LoggerFactory.getLogger(JwtAccessTokenCustomizer.class);

              private static final String CLIENT_NAME_ELEMENT_IN_JWT = "resource_access";

              private static final String ROLE_ELEMENT_IN_JWT = "roles";

              private ObjectMapper mapper;

              @Autowired
              public JwtAccessTokenCustomizer(ObjectMapper mapper) {
                  this.mapper = mapper;
                  LOG.info("Initialized {}", JwtAccessTokenCustomizer.class.getSimpleName());
              }

              @Override
              public void configure(JwtAccessTokenConverter converter) {
                  converter.setAccessTokenConverter(this);
                  LOG.info("Configured {}", JwtAccessTokenConverter.class.getSimpleName());
              }

              /**
               * Spring oauth2 expects roles under authorities element in tokenMap, but
               * keycloak provides it under resource_access. Hence extractAuthentication
               * method is overriden to extract roles from resource_access.
               *
               * 
          @return OAuth2Authentication with authorities for given application
               
          */
              @Override
              public OAuth2Authentication extractAuthentication(Map<String, ?> tokenMap) {
                  LOG.debug("Begin extractAuthentication: tokenMap = {}", tokenMap);
                  JsonNode token = mapper.convertValue(tokenMap, JsonNode.class);
                  Set<String> audienceList = extractClients(token); // extracting client names
                  List<GrantedAuthority> authorities = extractRoles(token); // extracting client roles

                  OAuth2Authentication authentication = super.extractAuthentication(tokenMap);
                  OAuth2Request oAuth2Request = authentication.getOAuth2Request();

                  OAuth2Request request = new OAuth2Request(oAuth2Request.getRequestParameters(), oAuth2Request.getClientId(),
                          authorities, true, oAuth2Request.getScope(), audienceList, nullnullnull);

                  Authentication usernamePasswordAuthentication = new UsernamePasswordAuthenticationToken(
                          authentication.getPrincipal(), "N/A", authorities);
                  LOG.debug("End extractAuthentication");
                  return new OAuth2Authentication(request, usernamePasswordAuthentication);
              }

              private List<GrantedAuthority> extractRoles(JsonNode jwt) {
                  LOG.debug("Begin extractRoles: jwt = {}", jwt);
                  Set<String> rolesWithPrefix = new HashSet<>();

                  jwt.path(CLIENT_NAME_ELEMENT_IN_JWT).elements().forEachRemaining(e -> e.path(ROLE_ELEMENT_IN_JWT).elements()
                          .forEachRemaining(r -> rolesWithPrefix.add("ROLE_" + r.asText())));

                  final List<GrantedAuthority> authorityList = AuthorityUtils
                          .createAuthorityList(rolesWithPrefix.toArray(new String[0]));
                  LOG.debug("End extractRoles: roles = {}", authorityList);
                  return authorityList;
              }

              private Set<String> extractClients(JsonNode jwt) {
                  LOG.debug("Begin extractClients: jwt = {}", jwt);
                  if (jwt.has(CLIENT_NAME_ELEMENT_IN_JWT)) {
                      JsonNode resourceAccessJsonNode = jwt.path(CLIENT_NAME_ELEMENT_IN_JWT);
                      final Set<String> clientNames = new HashSet<>();
                      resourceAccessJsonNode.fieldNames().forEachRemaining(clientNames::add);

                      LOG.debug("End extractClients: clients = {}", clientNames);
                      return clientNames;

                  } else {
                      throw new IllegalArgumentException(
                              "Expected element " + CLIENT_NAME_ELEMENT_IN_JWT + " not found in token");
                  }

              }

          }



          Reference
          https://medium.com/@bcarunmail/securing-rest-api-using-keycloak-and-spring-oauth2-6ddf3a1efcc2



          posted @ 2021-10-26 17:06 paulwong 閱讀(577) | 評論 (0)編輯 收藏

          Nginx代理轉發SFTP

          https://blog.csdn.net/qq_27127385/article/details/103666143

          posted @ 2021-10-15 10:04 paulwong 閱讀(352) | 評論 (0)編輯 收藏

          使用REST API與KEYCLOAK進行OUATH2協議的登錄認證

          KEYCLOAK是一套用戶、WEB API登錄管理,授權管理的WEB應用。
          如果要訪問受KEYCLOAK保護的REST API服務,則需要夾帶一個ACCESS_TOKEN。

          前端頁面:
          • 前端頁面一般是給用戶使用的,則需要用戶輸入在KEYCLOAK中有效的用戶名和密碼,并提供CALL BAK的URL,提交給KEYCLOAK
            http://10.80.27.69:8180/auth/realms/quickstart/protocol/openid-connect/auth?client_id=app-springboot-confidential&redirect_uri=http://10.80.27.69:8183/&response_type=code&scope=openid
          • 如果KEYCLOAK驗證通過,則通知頁面重導向回調的URL,并附上code=xxx,此code則是AUTHORIZATION_CODE
            http://10.80.27.69:8183/?session_state=2ad9ab98-6c39-43a8-872f-2112c27b74df&code=3f48ce19-58f9-45d9-8c09-30d492bf4b24.2ad9ab98-6c39-43a8-872f-2112c27b74df.bd7526ef-b1bf-447f-baef-b7dfd6f0df93
          • 回調的URL對應的SERVELET,取得AUTHORIZATION_CODE,并加上client_id和client_secrect,調用KEYLOAK的取ACCESS_TOKEN的HTTP API,取得ACCESS_TOKEN,返回給頁面
            http://10.80.27.69:8180/auth/realms/quickstart/protocol/openid-connect/token
            client_id=app-springboot-confidential&client_secret=3acf7692-49cb-4c45-9943-6f3dba512dae&redirect_uri=http://10.80.27.69:8183/&grant_type=authorization_code&code=cc7ac566-90f9-404e-b88e-fa28037b07d1.591311e1-5380-46a2-9363-834f17337922.bd7526ef-b1bf-447f-baef-b7dfd6f0df93
          • 頁面保存此ACCESS_TOKEN,就可以調用后臺的各種API獲取數據
            {
                "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJGSjg2R2NGM2pUYk5MT2NvNE52WmtVQ0lVbWZZQ3FvcXRPUWVNZmJoTmxFIn0.eyJleHAiOjE2MzQwMjA4ODksImlhdCI6MTYzNDAyMDU4OSwianRpIjoiNDAwOTQ4ZmQtMGU0MS00YWRjLTlhY2MtMzczZWM2NDVhNzM5IiwiaXNzIjoiaHR0cDovLzEwLjgwLjI3LjY5OjgxODAvYXV0aC9yZWFsbXMvcXVpY2tzdGFydCIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJkZGVkMDA2YS0xY2QxLTRjODUtOTQ1MS0wMjFlZmY3OTFiMmUiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhcHAtc3ByaW5nYm9vdC1jb25maWRlbnRpYWwiLCJzZXNzaW9uX3N0YXRlIjoiYzRlN2QzYTgtMDg2My00OTAwLTkxZmEtMGExYmFmYmRlNGU3IiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYXBwLXNwcmluZ2Jvb3QtY29uZmlkZW50aWFsIjp7InJvbGVzIjpbInVtYV9wcm90ZWN0aW9uIl19LCJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImNsaWVudElkIjoiYXBwLXNwcmluZ2Jvb3QtY29uZmlkZW50aWFsIiwiY2xpZW50SG9zdCI6IjEwLjEwLjIwLjU3IiwidXNlcl9uYW1lIjoic2VydmljZS1hY2NvdW50LWFwcC1zcHJpbmdib290LWNvbmZpZGVudGlhbCIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1hcHAtc3ByaW5nYm9vdC1jb25maWRlbnRpYWwiLCJjbGllbnRBZGRyZXNzIjoiMTAuMTAuMjAuNTcifQ.Ut6aZ6E1d4Esz0gRv2ubxdvrxmGvZLHHZepD5pnGxlqb_yZ4Q82TdGTG0iL4JJn2NH3QAU501dhzzuv6-OT9BUBKP-4ufyKv2DxSvt3GgdN30au5JsATHFyOWuuZGRBd3iWcynf9u3OJnSkHEnrIwRYatgndLzy8dy3AeqF12CI",
                "expires_in": 300,
                "refresh_expires_in": 600,
                "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2MTlhMmJjOS0yMWIwLTRmNGMtODI4OC1kNTJmMjA3OWEzY2EifQ.eyJleHAiOjE2MzQwMjExODksImlhdCI6MTYzNDAyMDU4OSwianRpIjoiYTM0NTQ1MTYtMzc3NC00YmRlLTgzOTMtN2QyMTdkZjdkZmJkIiwiaXNzIjoiaHR0cDovLzEwLjgwLjI3LjY5OjgxODAvYXV0aC9yZWFsbXMvcXVpY2tzdGFydCIsImF1ZCI6Imh0dHA6Ly8xMC44MC4yNy42OTo4MTgwL2F1dGgvcmVhbG1zL3F1aWNrc3RhcnQiLCJzdWIiOiJkZGVkMDA2YS0xY2QxLTRjODUtOTQ1MS0wMjFlZmY3OTFiMmUiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoiYXBwLXNwcmluZ2Jvb3QtY29uZmlkZW50aWFsIiwic2Vzc2lvbl9zdGF0ZSI6ImM0ZTdkM2E4LTA4NjMtNDkwMC05MWZhLTBhMWJhZmJkZTRlNyIsInNjb3BlIjoicHJvZmlsZSBlbWFpbCJ9.QhjkJBGz5UvwBF7xHM7_V_yjfF0lrA_EWzAVdFf-BRI",
                "token_type": "bearer",
                "not-before-policy": 0,
                "session_state": "c4e7d3a8-0863-4900-91fa-0a1bafbde4e7",
                "scope": "profile email"
            }
          • 這就是authorization_code流程

          后端服務:
          • 如果是在一個API中要請求另外一個API的數據,不存在具體用戶的情況
          • 需提供如下參數:client_id、client_secrect和grant_type,且grant_type=client_credentials,調用KEYLOAK的取ACCESS_TOKEN的HTTP API,取得ACCESS_TOKEN
            http://10.80.27.69:8180/auth/realms/quickstart/protocol/openid-connect/token
            client_id=app-springboot-confidential&client_secret=3acf7692-49cb-4c45-9943-6f3dba512dae&grant_type=client_credentials
          • 再將此ACCESS_TOKEN以Bearer TOKEN的方式調用別的的API
          • 這就是client_credentials流程

          驗證Access Token和獲取Token元信息:
          • http://10.80.27.69:8180/auth/realms/quickstart/protocol/openid-connect/token/introspect
            client_id=app-springboot-confidential&client_secret=3acf7692-49cb-4c45-9943-6f3dba512dae
          • Access Token無效時返回:
            {
                "active": false
            }

          刷新Token:
          • http://10.80.27.69:8180/auth/realms/quickstart/protocol/openid-connect/token
            client_id=app-springboot-confidential&client_secret=3acf7692-49cb-4c45-9943-6f3dba512dae&grant_type=refresh_token&refresh_token=asdfasd
          • 返回
            {
                "access_token": "eyJhbGciOiJSUzI1NiIsIn",
                "expires_in": 300,
                "refresh_expires_in": 1800,
                "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOi",
                "token_type": "Bearer",
                "not-before-policy": 1610728470,
                "session_state": "c1273eb5-f922-420c-b23a-854be9735c1d",
                "scope": "profile email"
            }



          Reference:
          https://blog.csdn.net/nklinsirui/article/details/112706006

          https://www.baeldung.com/?s=keycloak

          https://www.doag.org/formes/pubfiles/11143470/2019-NN-Sebastien_Blanc-Easily_Secure_your_Microservices_with_Keycloak-Praesentation.pdf




          posted @ 2021-10-12 14:40 paulwong 閱讀(787) | 評論 (0)編輯 收藏

          Enterprise Architect VS Solution Architect VS Software Architect

          • Enterprise Architect
            定義企業的大概方向
          • Solution Architect
            定義系統使用哪些框架技術
          • Software  Architect
            定義系統行為
          • Technical  Architect
            定義有關部署所使用服務器

          https://stackoverflow.com/questions/524941/whats-the-difference-between-solutions-architect-and-applications-architect

          https://www.youtube.com/watch?v=zB9WuYE1REI

          posted @ 2021-10-11 15:29 paulwong 閱讀(170) | 評論 (0)編輯 收藏

          SPRING CLOUD CONFIG有界面的配置管理中心




          https://dyc87112.github.io/spring-cloud-config-admin/

          posted @ 2021-10-07 16:55 paulwong 閱讀(297) | 評論 (0)編輯 收藏

          開源流程引擎哪個好,如何選型?

          https://zhuanlan.zhihu.com/p/369761832

          posted @ 2021-09-27 11:05 paulwong 閱讀(242) | 評論 (0)編輯 收藏

          Camunda流程引擎

          http://shaochenfeng.com/camunda/

          posted @ 2021-09-27 10:55 paulwong 閱讀(176) | 評論 (0)編輯 收藏

          Camunda/Flowable/Activiti技術發展史

          https://blog.csdn.net/qq_30739519/article/details/86583765

          posted @ 2021-09-27 10:45 paulwong 閱讀(346) | 評論 (0)編輯 收藏

          僅列出標題
          共115頁: First 上一頁 6 7 8 9 10 11 12 13 14 下一頁 Last 
          主站蜘蛛池模板: 湟中县| 神木县| 稻城县| 阳曲县| 青川县| 淮阳县| 虞城县| 九龙县| 吉木乃县| 阿拉善右旗| 道真| 平遥县| 衡水市| 弋阳县| 从化市| 荣昌县| 汝城县| 唐河县| 文成县| 新巴尔虎右旗| 海阳市| 介休市| 张家界市| 龙海市| 扶风县| 饶平县| 石河子市| 彭山县| 龙井市| 吉隆县| 牙克石市| 日喀则市| 东光县| 枝江市| 汉阴县| 呼和浩特市| 寿光市| 襄樊市| 潍坊市| 东阿县| 新干县|