代码之家  ›  专栏  ›  技术社区  ›  zzdhxu

如何使用谷歌云SQL JAVA认证服务帐户

  •  1
  • zzdhxu  · 技术社区  · 8 年前

    我知道有云sql jdbc套接字工厂。但是,这需要应用程序的默认凭据。我要做的是使用一个服务帐户(json机密文件)对云sql进行身份验证。有人能告诉我正确的方向吗?谢谢!

    GettingStarted的代码( https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory ):

    public class GettingStarted2{
        private static final ImmutableSet<String> SYSTEM_DATABASES =
                ImmutableSet.of(
                        // MySQL.
                        "mysql", "information_schema", "performance_schema",
                        // Postgres.
                        "cloudsqladmin", "postgres");
    
        @Parameter(names = "-v", description = "Verbose logging.")
        private boolean verbose = false;
    
        private void run() throws IOException, SQLException {
            System.out.println("Checking API credentials.");
            GoogleCredential apiCredentials;
            try {
    //            apiCredentials = GoogleCredential.getApplicationDefault();
                apiCredentials = GoogleCredential.fromStream(new FileInputStream("<keypath.json>"))
                        .createScoped(Lists.newArrayList("https://www.googleapis.com/auth/cloud-platform"));
            } catch (IOException e) {
                System.err.println(
                        "Unable to find API credentials. \nPlease run "
                                + "'gcloud auth application-default login' to make credentials available to "
                                + "this application.");
                if (verbose) {
                    e.printStackTrace();
                }
                System.exit(-1);
                return;
            }
    
            SQLAdmin adminApiClient = createAdminApiClient(apiCredentials);
            Optional<List<DatabaseInstance>> instances = askForProject(adminApiClient);
            if (!instances.isPresent()) {
                return;
            }
    
            if (instances.get().isEmpty()) {
                System.out.println(
                        "This project does not contain any Cloud SQL instances. "
                                + "Please create one using the Cloud Console.");
                return;
            }
    
            Optional<DatabaseInstance> optionalInstance = askForInstance(instances.get());
            if (!optionalInstance.isPresent()) {
                return;
            }
    
            String instanceConnectionName = optionalInstance.get().getConnectionName();
            Optional<DatabaseCredentials> optionalDatabaseCredentials =
                    askForDatabaseCredentials(optionalInstance.get());
            if (!optionalDatabaseCredentials.isPresent()) {
                return;
            }
    
            Connection connection = optionalDatabaseCredentials.get().getConnection();
            List<String> databases = listDatabases(optionalInstance.get(), connection);
            connection.close();
            if (databases.isEmpty()) {
                printConnectionDetails(
                        optionalInstance.get(), Optional.empty(), optionalDatabaseCredentials.get());
                return;
            }
    
            Optional<String> database = askForDatabase(databases);
            if (!database.isPresent()) {
                return;
            }
    
            printConnectionDetails(optionalInstance.get(), database, optionalDatabaseCredentials.get());
        }
    
        private List<String> listDatabases(
                DatabaseInstance databaseInstance, Connection connection) throws SQLException {
            String listDatabasesQuery;
            switch (getDatabaseType(databaseInstance)) {
                case MYSQL:
                    listDatabasesQuery = "SHOW DATABASES";
                    break;
                case POSTGRES:
                    listDatabasesQuery =
                            "SELECT datname AS database FROM pg_database WHERE datistemplate = false";
                    break;
                default:
                    throw new IllegalStateException();
            }
    
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery(listDatabasesQuery);
            List<String> databases = new ArrayList<>();
            while (resultSet.next()) {
                String database = resultSet.getString("database");
                if (SYSTEM_DATABASES.contains(database)) {
                    continue;
                }
                databases.add(database);
            }
            statement.close();
            databases.sort(String::compareTo);
            return databases;
        }
    
        private Optional<DatabaseCredentials> askForDatabaseCredentials(DatabaseInstance databaseInstance)
                throws SQLException {
    
            String defaultUser;
            String displayDatabaseType;
            String defaultDatabase;
            switch (getDatabaseType(databaseInstance)) {
                case MYSQL:
                    defaultUser = "root";
                    displayDatabaseType = "MySQL";
                    defaultDatabase = "mysql";
                    break;
                case POSTGRES:
                    defaultUser = "postgres";
                    displayDatabaseType = "Postgres";
                    defaultDatabase = "postgres";
                    break;
                default:
                    return Optional.empty();
            }
    
            Console console = System.console();
            String user;
            String lastUser = defaultUser;
            for (; ; ) {
                char[] password={'r','o','o','t'};
                System.out.printf("Please enter %s username [%s]: ", displayDatabaseType, lastUser);
    //            user = console.readLine();
                user="root";
                if (user == null) {
                    return Optional.empty();
                }
    
                if (user.trim().isEmpty()) {
                    user = lastUser;
                } else {
                    lastUser = user;
                }
    
                System.out.printf("Please enter %s password: ", displayDatabaseType);
                //password = console.readPassword();
                if (password == null) {
                    return Optional.empty();
                }
    
                try {
                    return Optional.of(
                            new DatabaseCredentials(
                                    user,
                                    password,
                                    DriverManager.getConnection(
                                            constructJdbcUrl(databaseInstance, defaultDatabase),
                                            user,
                                            new String(password))));
                } catch (SQLException e) {
                    if (e.getErrorCode() == 1045) {
                        System.out.println("Invalid username/password. Please try again.");
                        continue;
                    }
                    // Too bad Postgres doesn't set the error code...
                    if (e instanceof PSQLException
                            && e.getMessage().contains("password authentication failed")) {
                        System.out.println("Invalid username/password. Please try again.");
                        continue;
                    }
                    throw e;
                }
            }
        }
    
        private static String constructJdbcUrl(DatabaseInstance databaseInstance, String database) {
            switch (getDatabaseType(databaseInstance)) {
                case MYSQL:
                    return String.format(
                            "jdbc:mysql://google/%s?socketFactory=com.google.cloud.sql.mysql.SocketFactory" +
                                    "&cloudSqlInstance=%s",
                            database,
                            databaseInstance.getConnectionName());
                case POSTGRES:
                    return String.format(
                            "jdbc:postgresql://google/%s?socketFactory=com.google.cloud.sql.postgres.SocketFactory" +
                                    "&socketFactoryArg=%s",
                            database,
                            databaseInstance.getConnectionName());
                default:
                    throw new IllegalStateException();
            }
        }
    
        private Optional<DatabaseInstance> askForInstance(List<DatabaseInstance> instances) {
            Optional<Integer> instanceChoice =
                    chooseFromList(
                            "Please enter the number of the instance you want to use [1]: ",
                            instances.stream()
                                    .map(
                                            inst ->
                                                    String.format(
                                                            "%s [%s] (%s)",
                                                            inst.getName(),
                                                            inst.getDatabaseVersion(),
                                                            inst.getConnectionName()))
                                    .collect(Collectors.toList()));
            if (!instanceChoice.isPresent()) {
                return Optional.empty();
            }
    
            return Optional.of(instances.get(instanceChoice.get()));
        }
    
        private Optional<String> askForDatabase(List<String> databases) {
            Optional<Integer> databaseIndex =
                    chooseFromList("Please enter the number of the database you want to use [1]: ", databases);
            if (!databaseIndex.isPresent()) {
                return Optional.empty();
            }
    
            return Optional.of(databases.get(databaseIndex.get()));
        }
    
        private Optional<Integer> chooseFromList(String prompt, List<String> options) {
            Console console = System.console();
    
            for (int i = 0; i < options.size(); i++) {
                System.out.println(String.format("%d: %s", i + 1, options.get(i)));
            }
    
            int choice;
            for (;;) {
                System.out.print(prompt);
    //            String line = console.readLine();
                String line="1";
                if (line == null) {
                    return Optional.empty();
                }
    
                if (line.trim().isEmpty()) {
                    return Optional.of(0);
                } else {
                    try {
                        choice = Integer.parseInt(line);
                    } catch (NumberFormatException e) {
                        System.out.println("Invalid choice.");
                        continue;
                    }
    
                    if (choice < 1 || choice > options.size()) {
                        System.out.println("Invalid choice.");
                        continue;
                    }
    
                    return Optional.of(choice - 1);
                }
            }
        }
    
        private Optional<List<DatabaseInstance>> askForProject(SQLAdmin adminApiClient)
                throws IOException {
            Console console = System.console();
    
            InstancesListResponse instancesListResponse = null;
            while (instancesListResponse == null) {
                String project = "";
                while (project.isEmpty()) {
                    System.out.print("Enter the name of your Cloud project: ");
                    //project = console.readLine();
                    project="<projectname>";
                    if (project == null) {
                        return Optional.empty();
                    }
                    project = project.trim();
                }
    
                System.out.println("Listing Cloud SQL instances.");
    
                try {
                    instancesListResponse = adminApiClient.instances().list(project).execute();
                } catch (GoogleJsonResponseException e) {
                    if (e.getStatusCode() >= 400 && e.getStatusCode() < 500) {
                        System.out.println("That doesn't appear to be a valid project, try again.");
                        continue;
                    }
                    throw e;
                }
            }
    
            ArrayList<DatabaseInstance> instances = new ArrayList<>(instancesListResponse.getItems());
            instances.sort(Comparator.comparing(DatabaseInstance::getName));
            return Optional.of(instances);
        }
    
        private void printConnectionDetails(
                DatabaseInstance databaseInstance,
                Optional<String> database,
                DatabaseCredentials databaseCredentials) {
            String databaseName = database.orElse("<database_name>");
    
            System.out.println("\n\n");
            System.out.printf(
                    "Use the following JDBC URL%s:\n\n    %s\n",
                    !database.isPresent() ? " after creating a database" : "",
                    constructJdbcUrl(databaseInstance, databaseName));
            System.out.println();
            System.out.println("    Username: " + databaseCredentials.getUsername());
            System.out.println(
                    "    Password: " + (databaseCredentials.getPassword().length > 0 ? "<yes>" : "<empty>"));
            System.out.println("\n\n");
        }
    
        private static SQLAdmin createAdminApiClient(Credential credential) {
            HttpTransport httpTransport;
            try {
                httpTransport = GoogleNetHttpTransport.newTrustedTransport();
            } catch (GeneralSecurityException | IOException e) {
                throw new RuntimeException("Unable to initialize HTTP transport", e);
            }
    
            return new SQLAdmin.Builder(httpTransport, JacksonFactory.getDefaultInstance(), credential)
                    .setApplicationName("Cloud SQL Example")
                    .build();
        }
    
        private static final class DatabaseCredentials {
            private final String username;
            private final char[] password;
            private final Connection connection;
    
            public DatabaseCredentials(String username, char[] password, Connection connection) {
                this.username = username;
                this.password = password;
                this.connection = connection;
            }
    
            public String getUsername() {
                return username;
            }
    
            public char[] getPassword() {
                return password;
            }
    
            public Connection getConnection() {
                return connection;
            }
        }
    
        private static DatabaseType getDatabaseType(DatabaseInstance databaseInstance) {
            if (databaseInstance.getDatabaseVersion().startsWith("MYSQL_")) {
                return DatabaseType.MYSQL;
            } else if (databaseInstance.getDatabaseVersion().startsWith("POSTGRES_")) {
                return DatabaseType.POSTGRES;
            } else {
                System.err.println("Unsupported database type: " + databaseInstance.getDatabaseVersion());
                System.exit(-1);
                return null;
            }
        }
    
        private enum DatabaseType {
            MYSQL,
            POSTGRES
        }
    
        public static void main(String[] args) throws IOException, SQLException {
            GettingStarted2 gettingStarted2 = new GettingStarted2();
            new JCommander(gettingStarted2, args);
            gettingStarted2.run();
        }
    }
    

    StackTrace: https://pastebin.com/PMJsAFaK 第二个stacktrace(使用来自github的代码): https://pastebin.com/aH5vkLYF

    2 回复  |  直到 8 年前
        1
  •  0
  •   Mangu Dan Cornilescu    8 年前

    你可以 manually provide the credentials 进行身份验证时。

    如果你遵循这些例子 the JDBC Socket Factory ,尤其是 this example ,你可以看到几乎所有的东西都设置在 run() . 考虑到我的第一个链接,您必须将其更改为以下内容:

    GoogleCredential apiCredentials;
        try {
            apiCredentials = GoogleCredential.fromStream(new FileInputStream("yourkey.json"))
        .createScoped(Lists.newArrayList("https://www.googleapis.com/auth/cloud-platform"));
    

    如果您遵循我链接的示例,请不要忘记添加导入 java.io.FileInputStream 和 com.google.common.collect.Lists .

        2
  •  -1
  •   zzdhxu    8 年前

    更新(谷歌工程师回复): 在对是否可以使用json文件进行身份验证做了更多的研究之后,下面是我发现的。他们声明目前不允许外部googlecredentials认证。

    当前加载ServiceAccount的方法是: 工厂使用 ApplicationDefaultCredentials 像你提到的那样认证。您可以将这些设置为服务帐户(这是该页上的第一个示例)。

    $ gcloud iam service-accounts create my-account
    $ gcloud iam service-accounts keys create key.json --iam-account=my-account@my-project.iam.gserviceaccount.com
    $ export GOOGLE_APPLICATION_CREDENTIALS=key.json
    $ ./my_application.sh
    

    当前未解决的问题(将来他们可能会使用googlecredentials添加身份验证): https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory/issues/66