第十一節 資源伺服器api-server整合zuul閘道器

funnyok發表於2021-09-09

zuul 整合spring security 作為邊緣路由訪問時的api許可權控制策略

  • api-server作為資源伺服器。
    在上一節中,security-server中oauth2作為整個微服務的許可權控制中心,主要功能對客戶端的
    認證和token的發放,與此向對的就是資源伺服器,資源伺服器依賴於許可權伺服器。其他客戶端想要
    呼叫資源伺服器的介面,就必須透過許可權伺服器的認證。

zuul的基本介紹已在第六節中有過基本介紹,可參考

關於資源伺服器的api-server的配置使用如下:

  1. pom 新增依賴

 <dependency>
            <groupId>com.xzg</groupId>
            <artifactId>online-table-reservation-common</artifactId>
            <version>v1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-hystrix-stream</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
  1. 基本配置,啟動類@EnableResourceServer標註該服務為資源伺服器

@SpringBootApplication@EnableEurekaClient@EnableResourceServer@Configuration@ComponentScan({"com.xzg.api.service", "com.xzg.common"})public class ApiApp {    private static final Logger LOG = LoggerFactory.getLogger(ApiApp.class);    static {        // 本地測試
        LOG.warn("禁用ssl主機名檢查,開發截斷使用");
        HttpsURLConnection.setDefaultHostnameVerifier((hostname, sslSession) -> true);
    }    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {        return new RestTemplate();
    }    public static void main(String[] args) {
        LOG.info("Register MDCHystrixConcurrencyStrategy");
        HystrixPlugins.getInstance().registerConcurrencyStrategy(new MDCHystrixConcurrencyStrategy());
        SpringApplication.run(ApiApp.class, args);
    }
}
  1. 配置檔案中新增許可權認證服務配置

#其他略security:
  oauth2:
    resource:
      userInfoUri: 
  security:
    enabled: false

具體配置可參考

  • 如何訪問資源伺服器中的API

如果資源伺服器和授權伺服器在同一個應用程式中,並且使用DefaultTokenServices,那麼不必太考慮這一點,因為它實現所有必要的介面,因此它是自動一致的。如果資源伺服器是一個單獨的應用程式(比如本工程),那麼必須確保匹配授權伺服器的功能,並提供知道如何正確解碼令牌的ResourceServerTokenServices。與授權伺服器一樣,可以經常使用DefaultTokenServices,並且選項大多透過TokenStore(後端儲存或本地編碼)表示

以下基於security-server模組

  • 原始碼使用者密碼以及客戶端client_id、client_secret。均為硬編碼所以測試時,需要根據自身調整

按照上一節的步驟先獲取token(授權碼模式):

  1. 傳送url獲取code


  • 注意:如果瀏覽器未登入,則會跳轉使用者登陸頁面,成功登陸後,重定向
    返回到重定向的取得code

  1. 認證後取得code換取token

curl -X POST -k -H 'Content-Type: application/x-www-form-urlencoded' -i  --data 'grant_type=authorization_codet&redirect_uri='

圖片描述

圖片.png

  1. 獲取token後使用token,就可以在請求資源的請求頭新增Bearer  token

curl -X GET -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ' -i 

4 執行成功返回結果,Oauth2的基本也就實現了

也可以使用隱式許可方式直接獲取token(隱式許可模式),方法如下
直接傳送:如果未登陸會轉向登陸

請求:
返回:
http://localhost:7771/#access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2luZm8iOnsidXNlck5hbWUiOiJjbGllbnQiLCJwYXNzd29yOsTSQRjpKp2pE4ru2elm3uqFY_mduVtvwc92ZSPTNtTtBbijfNU86r7giIxsqaqaliu4pnvyXO2CWP7q74lOGWWDWDtI02u-a6jhpqauM-TjGHAMAxr-VUbyduw&token_type=bearer&state=553344&expires_in=7199&user_info=com.xzg.security.service.securityEtity.BaseUser@3e219620&jti=1521428f-5d94-4a72-befb-57531dab784a

還有一種是直接使用使用者密碼模式(資源所有者密碼憑證模式)
請求如下:

curl -X POST -k -H 'Content-Type: application/x-www-form-urlencoded' -i  --data 'grant_type=password&scope=apiAccess&client_id=client&client_secret=password&username=client&password=password'
  • 注意:上面這種傳送方式引數攜帶客戶端的client_id和client_secret這兩個,這倆是security-server使用https的客戶端認證,(ClientDetailsServiceConfigurer:用來配置客戶端詳情服務中的配置,儲存的密碼使用BCryptUtil加密。如果使用其他方式比如jdbc等儲存是也需要注意)

/**
     * @param clientDetailsServiceConfigurer
     * ClientDetailsServiceConfigurer:用來配置客戶端詳情服務(ClientDetailsService)
     * 客戶端詳情資訊在這裡進行初始化,你能夠把客戶端詳情資訊寫死在這裡或者是透過資料庫來儲存調取詳情資訊。
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception {
        clientDetailsServiceConfigurer
             .inMemory()//使用方法代替in-memory、JdbcClientDetailsService、jwt//        client_id: 用來標識客戶的Id。第三方使用者的id(可理解為賬號)
                .withClient("client")//        client_secret:第三方應用和授權伺服器之間的安全憑證(可理解為密碼)
                //(需要值得信任的客戶端)客戶端安全碼
                .secret(BCryptUtil.encodePassword("password"))
                .accessTokenValiditySeconds(7200)
                .authorizedGrantTypes("authorization_code", "refresh_token", "client_credentials", "implicit", "password")
                .authorities("ROLE_USER")
                .scopes("apiAccess");
    }
  • 需要注意的是
    或者使用外掛(火狐外掛RESTClient)


    圖片描述

    圖片.png

  • 當然如果剛才傳送不帶client_id和client_secret這兩個的話,傳送請求後會spring會去認證客戶端,所以會讓手動輸入client_id和client_secret的值

curl -X POST -k -H 'Content-Type: application/x-www-form-urlencoded' -i  --data 'grant_type=password&scope=apiAccess&username=client&password=password'

有一點我的username和password同client_id和client_secret相同所以可能會導致一些誤解,可以在程式硬編碼中做修改,如下:

/**
 * @author xzg
 */@Servicepublic class BaseUserDetailService implements UserDetailsService {    private Logger logger = LoggerFactory.getLogger(this.getClass());    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        logger.info("獲取登陸資訊:" + username);//注意:實際生產應該從資料庫中獲取,使用者名稱,密碼(為BCryptPasswordEncoder hash後的密碼)
        if (!"zhangsan".equals(username)) {
            logger.error("找不到該使用者,使用者名稱:" +username);            throw new UsernameNotFoundException("找不到該使用者,使用者名稱:" + username);
        }        //密碼硬編碼
        String password = BCryptUtil.encodePassword("12345");        // 獲取使用者許可權列表
        List<GrantedAuthority> authorities = CreatHardRole.createAuthorities().get();        // 返回帶有使用者許可權資訊的User
        org.springframework.security.core.userdetails.User user = new org.springframework.security.core.userdetails.User(username,
                password, true, true, true, true, authorities);        return new BaseUserDetail(new BaseUser(username, password), user);
    }
}

security-server原始碼


zuul 服務閘道器

zuul作為邊緣路由,這裡也屬於資源服務,所以重點有兩點配置,其一作為資源服務需要配置遠端的許可權伺服器

security:
  oauth2:
    resource:
      userInfoUri: 

同時作為邊緣路由,需要配置路由鏈路

zuul:
    ignoredServices: "*"
    routes:
        restaurantapi:
            path: /api/**
            serviceId: api-service
            stripPrefix: true

其他配置具體可參考原始碼
需要說明需要啟動本zuul專案,需要依賴eureka server、security-server、rabbitmq、以及其他業務服務



作者:勃列日涅夫
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3137/viewspace-2819984/,如需轉載,請註明出處,否則將追究法律責任。

相關文章