Spring Cloud Data Flow整合Cloudfoundry UAA服務做權限控制

我最新最全的文章都在 南瓜慢說 www.pkslow.com ,歡迎大家來喝茶!

1 前言

關於Spring Cloud Data Flow這裡不多介紹,有興趣可以看下面的文章。本文主要介紹如何整合Data FlowCloudFoundry UAA來做權限控制,而不是任何人都可以直接訪問操作。

Spring Cloud Data Flow相關文章:

Spring Cloud Data Flow初體驗,以Local模式運行

把Spring Cloud Data Flow部署在Kubernetes上,再跑個任務試試

Spring Cloud Data Flow用Shell來操作,方便建立CICD

被Spring坑了一把,查看源碼終於解決了DataFlow部署K8s應用的問題

UAA,即CloudFoundry User Account and Authentication,一個身份認證和授權服務系統,主要用於CloudFoundry,也可以作為一個獨立的OAuth2服務器,給客戶端分發令牌。可以在單點登陸SSO等場景使用到它。

UAA還可以整合LDAP,但為了簡化,本文只演示如何最簡單的整合。

2 啟動UAA服務

官方提供了war包形式的uaa.war,可以直接下載然後部署在Servlet容器上,如Tomcat等。UAA Bundled通過Springbootwar包包裝起來,讓啟動應用像應用springboot一樣簡單。本文通過這種形式來啟動。

通過插件maven-dependency-plugin來下載war包,如下:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <executions>
    <execution>
      <id>copy</id>
      <phase>process-resources</phase>
      <goals>
        <goal>copy</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <artifactItems>
      <!--本地IDE啟動時需要-->
      <artifactItem>
        <groupId>org.cloudfoundry.identity</groupId>
        <artifactId>cloudfoundry-identity-uaa</artifactId>
        <version>4.30.0</version>
        <type>war</type>
        <overWrite>true</overWrite>
        <outputDirectory>${project.basedir}/src/main/resources</outputDirectory>
        <destFileName>uaa.war</destFileName>
      </artifactItem>
      <!--打包成jar需要-->
      <artifactItem>
        <groupId>org.cloudfoundry.identity</groupId>
        <artifactId>cloudfoundry-identity-uaa</artifactId>
        <version>4.30.0</version>
        <type>war</type>
        <overWrite>true</overWrite>
        <outputDirectory>${project.basedir}/target/classes</outputDirectory>
        <destFileName>uaa.war</destFileName>
      </artifactItem>
    </artifactItems>
  </configuration>
</plugin>

通過一個Springboot的主函數入口來調用:

@SpringBootApplication
public class UaaServer {

    public static void main(String[] args) {
        SpringApplication.run(UaaServer.class, args);
    }

    @Bean
    public ServletWebServerFactory servletContainer() throws IOException {

        final File tempDirectory = Files.createTempDirectory("uaa").toFile();
        final File tempUaaYmlFile = new File(tempDirectory, "uaa.yml");
        final File tempUaaWarFile = new File(tempDirectory, "uaa.war");

        FileCopyUtils.copy(
                new ClassPathResource("uaa.yml").getInputStream(),
                new FileOutputStream(tempUaaYmlFile));

        FileCopyUtils.copy(
                new ClassPathResource("uaa.war").getInputStream(),
                new FileOutputStream(tempUaaWarFile));

        System.out.println("uaa.yml: " + tempUaaYmlFile.getAbsolutePath());
        System.out.println("uaa.war: " + tempUaaWarFile.getAbsolutePath());

        System.setProperty("UAA_CONFIG_FILE", tempUaaYmlFile.getAbsolutePath());

        return new TomcatServletWebServerFactory() {
            protected TomcatWebServer getTomcatWebServer(org.apache.catalina.startup.Tomcat tomcat) {
                final Server tomcatServer = tomcat.getServer();
                final File catalinaBase = new File(tempDirectory, "catalina");
                catalinaBase.mkdirs();

                tomcatServer.setCatalinaBase(catalinaBase);
                new File(tomcatServer.getCatalinaBase(), "webapps").mkdirs();
                try {
                    Context context = tomcat.addWebapp("/uaa", tempUaaWarFile.toString());
                    final ClassLoader properClassLoader = UaaServer.class.getClassLoader();

                    WebappLoader loader =
                            new WebappLoader(properClassLoader);
                    context.setLoader(loader);

                } catch (Exception ex) {
                    throw new IllegalStateException("Failed to add webapp", ex);
                }
                return super.getTomcatWebServer(tomcat);
            }
        };
    }
}

配置文件和war包的文件名硬編碼了,實際項目可以通過配置來實現。

接着要配置uaa.yml文件,具體內容查看代碼//github.com/LarryDpk/pkslow-samples ,這裡不貼出來了。注意需要生成JWT的key:

$ openssl genrsa -out signingkey.pem 2048
Generating RSA private key, 2048 bit long modulus
........................+++
..........................................................................+++
e is 65537 (0x10001)

$ openssl rsa -in signingkey.pem -pubout -out verificationkey.pem
writing RSA key

完成以上步驟後,可以打包啟動了,命令如下:

mvn clean package
java -jar target/cloudfoundry-uaa-server-1.0-SNAPSHOT.jar

默認端口為8080。成功啟動後,可以訪問://localhost:8080/uaa/login

3 配置賬號

為了方便,我們使用內存數據庫來保存賬戶信息,重啟後就會丟失。通過uaa提供的命令行工具uaac來創建用戶與權限。因為uaac是基於Ruby的,所以還要先安裝Ruby,我的電腦已經自帶,這裡就不演示了。

為了更快安裝命令行工具cf-uaac,修改Ruby包管理工具gem的源:

$ gem sources --add //gems.ruby-china.com
//gems.ruby-china.com added to sources

$ gem sources -l
*** CURRENT SOURCES ***
//rubygems.org/
//gems.ruby-china.com

$ gem sources --remove //rubygems.org/
//rubygems.org/ removed from sources

配置了國內源後,安裝:

$ sudo gem install cf-uaac
15 gems installed

安裝完成後,就可以通過下面的命令來創建用戶了。

uaac target //localhost:8080/uaa
uaac token client get admin -s adminsecret
uaac client add dataflow \
  --name dataflow \
  --secret dataflow \
  --scope cloud_controller.read,cloud_controller.write,openid,password.write,scim.userids,sample.create,sample.view,dataflow.create,dataflow.deploy,dataflow.destroy,dataflow.manage,dataflow.modify,dataflow.schedule,dataflow.view \
  --authorized_grant_types password,authorization_code,client_credentials,refresh_token \
  --authorities uaa.resource,dataflow.create,dataflow.deploy,dataflow.destroy,dataflow.manage,dataflow.modify,dataflow.schedule,dataflow.view,sample.view,sample.create \
  --redirect_uri //localhost:9393/login \
  --autoapprove openid

uaac group add "sample.view"
uaac group add "sample.create"
uaac group add "dataflow.view"
uaac group add "dataflow.create"
uaac group add "dataflow.deploy"
uaac group add "dataflow.destroy"
uaac group add "dataflow.manage"
uaac group add "dataflow.modify"
uaac group add "dataflow.schedule"


uaac user add larry -p larry --emails [email protected]
uaac member add "dataflow.view" larry
uaac member add "dataflow.create" larry
uaac member add "dataflow.deploy" larry
uaac member add "dataflow.destroy" larry
uaac member add "dataflow.manage" larry
uaac member add "dataflow.modify" larry
uaac member add "dataflow.schedule" larry

uaac user add vieweronly -p mysecret --emails [email protected]
uaac member add "dataflow.view" vieweronly

這裡關鍵的是用戶和群組,即usergroup。這裡配置的信息,會與Data Flow Server的配置對應上才可以。

4 配置與啟動Data Flow Server

Data Flow Server的配置文件非常重要,它是整合UAA的關鍵。關鍵是兩部分,第一部分是配置UAA各種信息,如clientIdToken的地址,各種鑒權地址等;第二部分是角色映射,Data Flow是基於角色的權限控制,它自己的角色要和UAA的群組映射起來才可以正常使用。

配置如下:

spring:
  security:
    oauth2:
      client:
        registration:
          uaa:
            client-id: dataflow
            client-secret: dataflow
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            authorization-grant-type: authorization_code
            scope:
              - openid
              - dataflow.create
              - dataflow.deploy
              - dataflow.destroy
              - dataflow.manage
              - dataflow.modify
              - dataflow.schedule
              - dataflow.view
        provider:
          uaa:
            jwk-set-uri: //localhost:8080/uaa/token_keys
            token-uri: //localhost:8080/uaa/oauth/token
            user-info-uri: //localhost:8080/uaa/userinfo
            user-name-attribute: user_name
            authorization-uri: //localhost:8080/uaa/oauth/authorize
      resourceserver:
        opaquetoken:
          introspection-uri: //localhost:8080/uaa/introspect
          client-id: dataflow
          client-secret: dataflow
  cloud:
    dataflow:
      security:
        authorization:
          provider-role-mappings:
            uaa:
              map-oauth-scopes: true
              role-mappings:
                ROLE_VIEW: dataflow.view
                ROLE_CREATE: dataflow.create
                ROLE_MANAGE: dataflow.manage
                ROLE_DEPLOY: dataflow.create
                ROLE_DESTROY: dataflow.create
                ROLE_MODIFY: dataflow.create
                ROLE_SCHEDULE: dataflow.create

可以看出,多個不同角色可以映射同一個群組,非常靈活。

配置完成後,就可以啟動Data Flow Server了:

java -jar data-flow-server.jar --spring.config.additional-location=./src/main/resources/application.yaml

5 體驗成果的時候到了

成功啟動UAA並配置用戶,再啟動Data Flow Server後,便可以開始使用了,過程如下:

訪問//localhost:9393/dashboard/#/apps 會自動跳轉到登陸界面,點擊uaa

跳轉到uaa的登陸界面:

輸入配置的賬號密碼:larry/larry,上面顯示為Email,其實並不是。登陸後就要確認授權:

授權後,會自動跳轉回Data Flow的界面,並已經有權限進行查看操作了:

登出後,又要要求重新登陸。至此,我們已經成功地整合了。

shell的使用如下:

$ java -jar spring-cloud-dataflow-shell-2.7.0.jar \
  --dataflow.uri=//localhost:9393            \   
  --dataflow.username=my_username                 \   
  --dataflow.password=my_password                 \   
  --skip-ssl-validation  true    

總結

本文通過一步步演示如何整合Data Flow ServerUAA,以實現Data Flow安全要求。實際UAA應該使用其它數據庫,如MySQL,或整合LDAP,這樣重啟賬號數據不會丟失。後續有空再討論吧。

代碼請查看://github.com/LarryDpk/pkslow-samples


參考文檔:

Data Flow官方文檔 2.7.0版本

A Quick Guide To Using Cloud Foundry UAA


歡迎關注微信公眾號<南瓜慢說>,將持續為你更新…

多讀書,多分享;多寫作,多整理。

Tags: