<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    paulwong

    #

    EVEN DRIVEN - SPRING CLOUD STREAM - Routing Function

    SPRING CLOUD STREAM內置了一個RoutingFunction,能將MESSAGE路由到應用的其他FUNCTION中。
    對接RoutingFunction可發送消息到其外部DESTINATION中或用“|”連接符連接。

    application.yaml
    # This setting can increase or decrease the rate of message production (1000 = 1s)
    #
     spring.cloud.stream.poller.fixed-delay=1000
    #
     DefaultPollerProperties

    # This setting can control which function method in our code will be triggered if there are multiple
    #
     spring.cloud.function.definition=supplyLoan

    # Give the autogenerated binding a friendlier name
    spring:
       application:
          name: loan-check-rabbit
       banner:
          location: classpath:/banner-rabbit.txt
       cloud:
          #BindingServiceProperties
          stream:
             #StreamFunctionProperties
             function:
                definition: loadCheckerFunction;loanCheckerDecieder;loanCheckerConsumer;\
                            loanDeclinedConsumer;loanApprovedConsumer;loanCheckerProcessor|functionRouter
                routing:
                   enabled: true
             #BindingProperties
             bindings:
                loanCheckerProcessor|functionRouter-in-0:
                   destination: queue.pretty.log.messages
                   binder: local_rabbit
                   
                loanApprovedConsumer-in-0:
                   destination: load.approved
                   binder: local_rabbit
                loanDeclinedConsumer-in-0:
                   destination: load.declined
                   binder: local_rabbit
                   
                loanCheckerDecieder-in-0:
                   destination: queue.pretty.log.messages.222
                   binder: local_rabbit
                loanCheckerDecieder-out-0:
                   destination: queue.pretty.approved.messages
                   binder: local_rabbit
                loanCheckerConsumer-in-0:
                   destination: queue.pretty.approved.messages
                   binder: local_rabbit
             #BinderProperties
             binders:
                local_rabbit:
                   type: rabbit
                   environment:
                      spring:
                         rabbitmq:
                            host: 10.80.27.69
                            port: 5672
                            username: guest
                            password: guest
                            virtual-host: my-virtual-host
                            
                            
    logging:
       level:
          root: info
          org.springframework:
             cloud.function: debug
             #retry: debug


    LoanCheckConfiguration.java
    package com.paul.testspringcloudstream.loancheck.config;

    import java.util.function.Consumer;
    import java.util.function.Function;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.function.context.MessageRoutingCallback;
    import org.springframework.cloud.stream.function.StreamBridge;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.integration.support.MessageBuilder;
    import org.springframework.messaging.Message;

    import com.paul.testspringcloudstream.common.model.Loan;
    import com.paul.testspringcloudstream.common.model.Status;
    import com.paul.testspringcloudstream.loancheck.router.LoanCheckerRouter;
    import com.paul.testspringcloudstream.loancheck.service.LoanProcessor;
    import com.paul.testspringcloudstream.loancheck.service.LoanService;

    @Configuration
    public class LoanCheckConfiguration {
        
        private static final Logger log = LoggerFactory.getLogger(LoanCheckConfiguration.class);
        private static final Long MAX_AMOUNT = 10000L;
        private static final String LOG_PATTERN = "{} - {} {} for ${} for {}";
        
        @Autowired
        public void test(Consumer<Loan> loanCheckerConsumer) {
            log.info("{}", loanCheckerConsumer.getClass());
        }
        
        
        @Bean
        public Consumer<Loan> loanCheckerConsumer(){
            return loan -> 
                log.info(LOG_PATTERN, "loanCheckerConsumer", loan.getStatus(), loan.getUuid(), loan.getAmount(), loan.getName());
        }
        
        @Bean
        public Consumer<Loan> loanDeclinedConsumer(){
            return loan -> 
                log.info(LOG_PATTERN, "loanDeclinedConsumer", loan.getStatus(), loan.getUuid(), loan.getAmount(), loan.getName());
        }
        
        @Bean
        public Consumer<Loan> loanApprovedConsumer(){
            return loan -> 
                log.info(LOG_PATTERN, "loanApprovedConsumer", loan.getStatus(), loan.getUuid(), loan.getAmount(), loan.getName());
        }
        
        @Bean
        public MessageRoutingCallback loanCheckerRouter() {
            return new LoanCheckerRouter();
        }
        
        @Bean
        public Function<Loan, Loan> loanCheckerProcessor(
            LoanService loanService
        ){
            return loan -> loanService.check(loan);
        }
        
        @Bean
        public Function<Loan, Message<Loan>> loanCheckerProcessorBak(
            LoanService loanService
        ){
            return loan -> {
                Loan result = loanService.check(loan);
                String sendTo = Status.DECLINED.name().equals(result.getStatus()) ? 
                            LoanProcessor.DECLINED_OUT : LoanProcessor.APPROVED_OUT;
                
                return MessageBuilder.withPayload(result)
                            .setHeader("spring.cloud.stream.sendto.destination", sendTo)
                            .build();
            };
        }
        
        @Bean
        public Consumer<Loan> loanCheckerDecieder(StreamBridge streamBridge){
            return loan -> {
                log.info(LOG_PATTERN, "loanCheckerDecieder", loan.getStatus(), loan.getUuid(), loan.getAmount(), loan.getName());

                if (loan.getAmount() > MAX_AMOUNT) {
                    loan.setStatus(Status.DECLINED.name());
                    streamBridge.send(LoanProcessor.DECLINED_OUT, "local_rabbit", loan);
                } else {
                    loan.setStatus(Status.APPROVED.name());
                    streamBridge.send(LoanProcessor.APPROVED_OUT, "local_rabbit", loan);
                }

                log.info(LOG_PATTERN, "loanCheckerDecieder", loan.getStatus(), loan.getUuid(), loan.getAmount(), loan.getName());
            };
        }

    }


    LoanCheckerRouter.java,將路由條件統一在此處
    package com.paul.testspringcloudstream.loancheck.router;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.cloud.function.context.MessageRoutingCallback;
    import org.springframework.messaging.Message;

    import com.paul.testspringcloudstream.common.model.Loan;
    import com.paul.testspringcloudstream.common.model.Status;

    public class LoanCheckerRouter implements MessageRoutingCallback{
        
        private static final Logger log = LoggerFactory.getLogger(LoanCheckerRouter.class);

        @Override
        public String functionDefinition(Message<?> message) {
            
    //        byte[] resultByte = (byte[])message.getPayload();
    //        String resultString = new String(resultByte);
    //        
    //        return "loanDeclinedConsumer";
            
            Loan result = (Loan)message.getPayload();
            
            log.info("Loan status: {}", result.getStatus());
            
            return Status.DECLINED.name().equals(result.getStatus()) ? 
                        "loanDeclinedConsumer" : "loanApprovedConsumer";
        }

    }

    posted @ 2021-11-15 14:46 paulwong 閱讀(315) | 評論 (0)編輯 收藏

    RABBITMQ增加消息追蹤

    https://blog.csdn.net/as140507/article/details/101104900

    posted @ 2021-11-15 14:08 paulwong 閱讀(143) | 評論 (0)編輯 收藏

    EVEN DRIVEN - SPRING CLOUD STREAM 3.x - Functional Programming Model

    SPRING CLOUD STREAM 3.x 版本時,之前的一些編程模式,如@Enablebindding,@StreamListenner等注釋被廢棄了,這是由于一些框架的代碼必需由用戶編寫,如配置框架用的Input MessageChannel,Output  MessageChannel,連接MessageHandler與MessageChannel等,被視為不必要的動作。為了簡化用戶代碼,于是推出Functional Programming Model。

    引入了新名詞:Supplier、Function與Consumer。實際上這幾個類可視為Adapter,如果之前已經有存在的Service類,且方法名為各種各樣,可以重新包裝成Supplier、Function與Consumer,并在固定的方法名:apply/get/accept中調用Service的方法。

    Supplier

    當在配置文件中注入此類型的Bean,并在spring.cloud.stream.function.definition加入此Bean的名稱,SPRING CLOUD STREAM就會幫你生成一個Output  MessageChannel,并連接上此Bean,后續只需要在BINDDING中加入對應的Destination Name,即可向BROKER發消息了。

    Consumer

    當在配置文件中注入此類型的Bean,并在spring.cloud.stream.function.definition加入此Bean的名稱,SPRING CLOUD STREAM就會幫你生成一個Input  MessageChannel,并連接上此Bean,后續只需要在BINDDING中加入對應的Destination Name,即可收到BROKER推送關于此Destination的消息了。

    Function

    當在配置文件中注入此類型的Bean,并在spring.cloud.stream.function.definition加入此Bean的名稱,SPRING CLOUD STREAM就會幫你生成一個Input和Output  MessageChannel,并連接上此Bean,后續只需要在BINDDING中分別對Input和Output MessageChannel加入對應的Destination Name1/Name2,即可收到BROKER推送關于此Destination的消息,也可以向BROKER發消息了。

    與SPRING INTEGRATION的整合

    如果要對消息進行復雜處理,如拆分消息、聚合消息、IF ELSE消息等,就要借助SPRING INTEGRATION了。

    @Bean
        public IntegrationFlow upperCaseFlow(LoanService loanService) {
            return IntegrationFlows
                        //turn this IntegrationFlow as a gateway, here is a Function interface 
                        
    //with loadCheckerFunction as bean name
                        .from(LoadCheckerFunction.class, gateway -> gateway.beanName("loadCheckerFunction"))
                        .handle(loanService, "check")
                        .logAndReply(LoggingHandler.Level.WARN);
        }

        public interface LoadCheckerFunction extends Function<Loan, Loan>{

        }

    IntegrationFlows.from(Class<?> serviceInterface)是可以將本IntegrationFlow包裝成serviceInterface的實現類,如果調用此接口,最終會返回IntegrationFlow最后一個步驟的實體,如果這個serviceInterface是Function的話,剛好和SPRING CLOUD STREAM對接上。

    后續在spring.cloud.stream.function.definition加入此Bean的名稱loadCheckerFunction,SPRING CLOUD STREAM就會幫你生成一個Input和Output  MessageChannel,并連接上此Bean,再在BINDDING中分別對Input和Output MessageChannel加入對應的Destination Name1/Name2,即可收到BROKER推送關于此Destination的消息,也可以向BROKER發消息。

    application.yaml
    # This setting can increase or decrease the rate of message production (1000 = 1s)
    # spring.cloud.stream.poller.fixed-delay=1000

    # This setting can control which function method in our code will be triggered if there are multiple
    # spring.cloud.function.definition=supplyLoan

    # Give the autogenerated binding a friendlier name

    spring:
       application:
          name: loan-check-rabbit
       banner:
          location: classpath:/banner-rabbit.txt
       cloud:
          stream:
             function.definition: loadCheckerFunction
             #BindingProperties
             bindings:
                loadCheckerFunction-in-0:
                   destination: queue.pretty.log.messages
                   binder: local_rabbit
                loadCheckerFunction-out-0:
                   destination: queue.pretty.approved.messages
                   binder: local_rabbit
             #BinderProperties
             binders:
                local_rabbit:
                   type: rabbit
                   environment:
                      spring:
                         rabbitmq:
                            host: 10.80.27.69
                            port: 5672
                            username: guest
                            password: guest
                            virtual-host: my-virtual-host

    Reference

    https://spring.io/blog/2019/10/25/spring-cloud-stream-and-spring-integration

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

    在CENTOS LINUX上安裝RABBITMQ

    安裝ERLANG

    從這里下載0依賴的ERLANG安裝包:
    https://github.com/rabbitmq/erlang-rpm/releases 
    象這種erlang-23.3.4.8-1.el7.x86_64.rpm含el7的是CENTOS7版本,含el8的是CENTOS8版本,安裝腳本
    yum install -y erlang-23.3.4.8-1.el7.x86_64.rpm

    安裝RABBITMQ

    下載地址:https://github.com/rabbitmq/rabbitmq-server/releases
    安裝腳本:yum install -y erlang-23.3.4.8-1.el7.x86_64.rpm

    拷貝配置文件

    下載配置文件樣例:https://github.com/rabbitmq/rabbitmq-server/blob/master/deps/rabbit/docs/rabbitmq.conf.example
    粘貼并重命名文件:/etc/rabbitmq/rabbitmq.conf

    開啟WEB控制臺

    /lib/rabbitmq/bin/rabbitmq-plugins enable rabbitmq_management

    配置guest可遠程訪問

    ## Uncomment the following line if you want to allow access to the
    ## guest user from anywhere on the network.
    loopback_users.guest = false

    配置開機啟動

    chkconfig rabbitmq-server on

    啟動實例

    systemctl start rabbitmq-serve
    systemctl stop rabbitmq-serve

    訪問控制臺,guest/guest

    http://10.80.27.69:15672/#/

    Reference
    https://www.cnblogs.com/ZhuChangwu/p/14093107.html
    https://juejin.cn/post/6933040530519506957

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

    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 閱讀(756) | 評論 (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 閱讀(580) | 評論 (0)編輯 收藏

    Nginx代理轉發SFTP

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

    posted @ 2021-10-15 10:04 paulwong 閱讀(353) | 評論 (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 閱讀(790) | 評論 (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 閱讀(172) | 評論 (0)編輯 收藏

    僅列出標題
    共115頁: First 上一頁 6 7 8 9 10 11 12 13 14 下一頁 Last 
    主站蜘蛛池模板: 国产精品免费大片| 国产亚洲高清在线精品不卡| 亚洲一级片在线观看| 国产成人精品亚洲日本在线| 亚洲另类无码专区首页| 国产成人亚洲精品91专区高清 | 亚洲第一页在线播放| 激情五月亚洲色图| 色费女人18女人毛片免费视频| 中国国语毛片免费观看视频| 91精品国产免费| 永久免费AV无码网站在线观看| 亚洲国产精品第一区二区三区| 久久精品国产精品亚洲蜜月| 亚洲国产成人99精品激情在线| 国产亚洲美女精品久久| 在线观看免费播放av片| 最近免费中文字幕视频高清在线看 | 日本免费中文字幕在线看| 伊人久久综在合线亚洲91| 亚洲国产精品久久人人爱| 国产精品亚洲五月天高清| 久久久久免费精品国产| 成年性午夜免费视频网站不卡| 亚洲综合区小说区激情区| 亚洲第一网站免费视频| 在线亚洲精品视频| 91香蕉国产线在线观看免费| 日本a级片免费看| 亚洲国产精品lv| 美女黄频a美女大全免费皮| 91视频免费网址| 亚洲AV无码成人精品区大在线| 亚洲一区综合在线播放| 国产午夜亚洲精品不卡电影| 亚欧免费一级毛片| AV在线亚洲男人的天堂| 亚洲精品免费网站| 免费在线中文日本| 亚洲av无码成人精品区| 2020天堂在线亚洲精品专区|