Azure Cosmos DB Gremlin/Tinkerpop令牌身份验证与Java SDK

Azure Cosmos DB Gremlin/Tinkerpop令牌身份验证与Java SDK,java,azure,azure-cosmosdb,azure-cosmosdb-gremlinapi,Java,Azure,Azure Cosmosdb,Azure Cosmosdb Gremlinapi,我正在尝试使用资源令牌连接到Azure Cosmos DB中的Gremlin集合。我从这里改编了文档(主要是针对C): 问题是,一旦我尝试访问数据,令牌的日期标头似乎无效: Exception in thread "main" java.util.concurrent.CompletionException: org.apache.tinkerpop.gremlin.driver.exception.ResponseException: ActivityId : 00000000-0000-

我正在尝试使用资源令牌连接到Azure Cosmos DB中的Gremlin集合。我从这里改编了文档(主要是针对C):

问题是,一旦我尝试访问数据,令牌的日期标头似乎无效:

Exception in thread "main" java.util.concurrent.CompletionException: org.apache.tinkerpop.gremlin.driver.exception.ResponseException: 

ActivityId : 00000000-0000-0000-0000-000000000000
ExceptionType : UnauthorizedException
ExceptionMessage :
    The input date header is invalid format. Please pass in RFC 1123 style date format.
    ActivityId: 755ab024-fc79-47a3-bc44-3231b2db7dc1, documentdb-dotnet-sdk/2.7.0 Host/64-bit MicrosoftWindowsNT/6.2.9200.0
Source : Microsoft.Azure.Documents.ClientThe input date header is invalid format. Please pass in RFC 1123 style date format.
ActivityId: 755ab024-fc79-47a3-bc44-3231b2db7dc1, documentdb-dotnet-sdk/2.7.0 Host/64-bit MicrosoftWindowsNT/6.2.9200.0
    BackendStatusCode : Unauthorized
    BackendActivityId : 755ab024-fc79-47a3-bc44-3231b2db7dc1
    HResult : 0x80131500
有人知道怎么解决吗?JVM通过
-Duser.timezone=GMT设置为GMT

这是密码。请注意,它是一个Java CLI应用程序,仅用于测试连接性。
cfg
的所有数据基本上都是由cli提供的,方法名称应该是自解释的

令牌生成,这将使用
DocumentClient
实例的主密钥:

...
import com.microsoft.azure.documentdb.DocumentClient;
import com.microsoft.azure.documentdb.DocumentClientException;
import com.microsoft.azure.documentdb.FeedResponse;
import com.microsoft.azure.documentdb.Permission;
import com.microsoft.azure.documentdb.PermissionMode;
import com.microsoft.azure.documentdb.ResourceResponse;
import com.microsoft.azure.documentdb.User;
...

public class TokenGenerator {

    private String USER_ID = "demo-1";

    public String generateToken(CmdLineConfiguration cfg) throws DocumentClientException {
        try (DocumentClient client = Utilities.documentClientFrom(cfg)) {
            String databaseLink = String.format("/dbs/%s", cfg.getDatabaseId());
            String collectionLink = String.format("/dbs/%s/colls/%s", cfg.getDatabaseId(), cfg.getCollectionId());

            // get all users within database
            FeedResponse<User> queryResults = client.readUsers(databaseLink, null);
            List<User> onlineUsers = queryResults.getQueryIterable().toList();

            // if a user exists, grab the first one, if not create it
            User user;
            Optional<User> onlineUser = onlineUsers.stream().filter(u -> u.getId().equals(USER_ID)).findFirst();
            if (onlineUser.isPresent()) {
                user = onlineUser.get();
            } else {
                User u = new User();
                u.setId(USER_ID);
                ResourceResponse<User> generatedUser = client.createUser(databaseLink, u, null);
                user = generatedUser.getResource();
            }

            // read permissions, if existent use, else create
            FeedResponse<Permission> permissionResponse = client.readPermissions(user.getSelfLink(), null);
            List<Permission> onlinePermissions = permissionResponse.getQueryIterable().toList();
            Permission permission;
            if (onlinePermissions.size() == 0) {
                Permission p = new Permission();
                p.setPermissionMode(PermissionMode.Read);
                p.setId(USER_ID + "_READ");
                p.setResourceLink(collectionLink);
                ResourceResponse<Permission> generatedPermission = client.createPermission(user.getSelfLink(), p, null);
                permission = generatedPermission.getResource();
            } else {
                permission = onlinePermissions.get(0);
            }
            // return the token
            return permission.getToken();
        }
    }
}
src/remote.yml

hosts: [COSMOSNAME.gremlin.cosmosdb.azure.com]
port: 443
username: /dbs/DBNAME/colls/COLLECTIONNAME
connectionPool: { enableSsl: true}
serializer: { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV2d0, config: { serializeResultToString: true }}

我不能在我这边重复你的问题。我尝试将其扩展到java。在那个演示中,使用了主密钥,所以我遵循了您的部分代码,并使用资源令牌访问graph db

您与db的连接代码似乎正常工作。我的主要类如下所示,与您的类似:

import org.apache.tinkerpop.gremlin.driver.*;
import org.apache.tinkerpop.gremlin.driver.exception.ResponseException;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;


public class Program
{
        static final String gremlinQueries[] = new String[] {"g.V().limit(1)"};

  public static void main( String[] args ) throws ExecutionException, InterruptedException {

        String token = "***";
        Cluster cluster;
        Client client;

        try {
            Cluster.Builder builder = Cluster.build(new File("src/remote.yaml"));
//            Cluster.Builder builder = Cluster.build();
            AuthProperties authenticationProperties = new AuthProperties();
            authenticationProperties.with(AuthProperties.Property.USERNAME,
                    String.format("/dbs/%s/colls/%s", "db", "coll"));

// The format of the token is "type=resource&ver=1&sig=<base64 string>;<base64 string>;".
            authenticationProperties.with(AuthProperties.Property.PASSWORD, token);

            builder.authProperties(authenticationProperties);

            // Attempt to create the connection objects
            cluster = builder.create();
//            cluster = Cluster.build(new File("src/remote.yaml")).create();
            client = cluster.connect();
        } catch (Exception e) {
            // Handle file errors.
            System.out.println("Couldn't find the configuration file.");
            e.printStackTrace();
            return;
        }

        // After connection is successful, run all the queries against the server.
        for (String query : gremlinQueries) {
            System.out.println("\nSubmitting this Gremlin query: " + query);

            // Submitting remote query to the server.
            ResultSet results = client.submit(query);

            CompletableFuture<List<Result>> completableFutureResults;
            CompletableFuture<Map<String, Object>> completableFutureStatusAttributes;
            List<Result> resultList;
            Map<String, Object> statusAttributes;

            try{
                completableFutureResults = results.all();
                completableFutureStatusAttributes = results.statusAttributes();
                resultList = completableFutureResults.get();
                statusAttributes = completableFutureStatusAttributes.get();            
            }
            catch(ExecutionException | InterruptedException e){
                e.printStackTrace();
                break;
            }
            catch(Exception e){
                ResponseException re = (ResponseException) e.getCause();

                // Response status codes. You can catch the 429 status code response and work on retry logic.
                System.out.println("Status code: " + re.getStatusAttributes().get().get("x-ms-status-code")); 
                System.out.println("Substatus code: " + re.getStatusAttributes().get().get("x-ms-substatus-code")); 

                // If error code is 429, this value will inform how many milliseconds you need to wait before retrying.
                System.out.println("Retry after (ms): " + re.getStatusAttributes().get().get("x-ms-retry-after"));

                // Total Request Units (RUs) charged for the operation, upon failure.
                System.out.println("Request charge: " + re.getStatusAttributes().get().get("x-ms-total-request-charge"));

                // ActivityId for server-side debugging
                System.out.println("ActivityId: " + re.getStatusAttributes().get().get("x-ms-activity-id"));
                throw(e);
            }

            for (Result result : resultList) {
                System.out.println("\nQuery result:");
                System.out.println(result.toString());
            }

            // Status code for successful query. Usually HTTP 200.
            System.out.println("Status: " + statusAttributes.get("x-ms-status-code").toString());

            // Total Request Units (RUs) charged for the operation, after a successful run.
            System.out.println("Total charge: " + statusAttributes.get("x-ms-total-request-charge").toString());
        }

        System.out.println("Demo complete!\n Press Enter key to continue...");
        try{
            System.in.read();
        } catch (IOException e){
            e.printStackTrace();
            return;
        }

        // Properly close all opened clients and the cluster
        cluster.close();

        System.exit(0);
    }
}
所以,我担心根本原因是代币的产生

输入日期标题的格式无效。请输入RFC1123样式 日期格式


我搜索了这个与cosmos db相关的错误消息,找到了这个消息供您参考。似乎解决方案正在更改本地区域设置。您可以使用Fiddler跟踪请求的
x-ms-date
头。

使用Azure支持,我发现问题是Azure端的一个bug。与Cosmos一起使用自定义VNET时会发生这种情况。与此同时,Azure已修复,一切正常。

嗨,这里有更新吗?我的答案对你有帮助吗?嗨@jay gong,谢谢你的回答。不幸的是,我现在生病了,不能复习。等我康复后我会这么做的,当然!早日康复!我将此作为Azure支持票证提出,他们确认似乎存在问题。希望他们能很快提供修复。好的,如果你得到了更新,你可以在这里发布。所以我调试了应用程序,当通过Azure Java SDK请求权限时,
x-ms-date
头被正确发送。在Gremlin连接中,它没有被使用,我也不知道如何设置它。在Azure文档之后,权限上的令牌会在访问权限后更新。我的代码不会触发这种情况,我通过REST请求权限(从而请求令牌),并获得一个更新的令牌。尽管如此,RFC问题也存在于此令牌中。
import org.apache.tinkerpop.gremlin.driver.*;
import org.apache.tinkerpop.gremlin.driver.exception.ResponseException;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;


public class Program
{
        static final String gremlinQueries[] = new String[] {"g.V().limit(1)"};

  public static void main( String[] args ) throws ExecutionException, InterruptedException {

        String token = "***";
        Cluster cluster;
        Client client;

        try {
            Cluster.Builder builder = Cluster.build(new File("src/remote.yaml"));
//            Cluster.Builder builder = Cluster.build();
            AuthProperties authenticationProperties = new AuthProperties();
            authenticationProperties.with(AuthProperties.Property.USERNAME,
                    String.format("/dbs/%s/colls/%s", "db", "coll"));

// The format of the token is "type=resource&ver=1&sig=<base64 string>;<base64 string>;".
            authenticationProperties.with(AuthProperties.Property.PASSWORD, token);

            builder.authProperties(authenticationProperties);

            // Attempt to create the connection objects
            cluster = builder.create();
//            cluster = Cluster.build(new File("src/remote.yaml")).create();
            client = cluster.connect();
        } catch (Exception e) {
            // Handle file errors.
            System.out.println("Couldn't find the configuration file.");
            e.printStackTrace();
            return;
        }

        // After connection is successful, run all the queries against the server.
        for (String query : gremlinQueries) {
            System.out.println("\nSubmitting this Gremlin query: " + query);

            // Submitting remote query to the server.
            ResultSet results = client.submit(query);

            CompletableFuture<List<Result>> completableFutureResults;
            CompletableFuture<Map<String, Object>> completableFutureStatusAttributes;
            List<Result> resultList;
            Map<String, Object> statusAttributes;

            try{
                completableFutureResults = results.all();
                completableFutureStatusAttributes = results.statusAttributes();
                resultList = completableFutureResults.get();
                statusAttributes = completableFutureStatusAttributes.get();            
            }
            catch(ExecutionException | InterruptedException e){
                e.printStackTrace();
                break;
            }
            catch(Exception e){
                ResponseException re = (ResponseException) e.getCause();

                // Response status codes. You can catch the 429 status code response and work on retry logic.
                System.out.println("Status code: " + re.getStatusAttributes().get().get("x-ms-status-code")); 
                System.out.println("Substatus code: " + re.getStatusAttributes().get().get("x-ms-substatus-code")); 

                // If error code is 429, this value will inform how many milliseconds you need to wait before retrying.
                System.out.println("Retry after (ms): " + re.getStatusAttributes().get().get("x-ms-retry-after"));

                // Total Request Units (RUs) charged for the operation, upon failure.
                System.out.println("Request charge: " + re.getStatusAttributes().get().get("x-ms-total-request-charge"));

                // ActivityId for server-side debugging
                System.out.println("ActivityId: " + re.getStatusAttributes().get().get("x-ms-activity-id"));
                throw(e);
            }

            for (Result result : resultList) {
                System.out.println("\nQuery result:");
                System.out.println(result.toString());
            }

            // Status code for successful query. Usually HTTP 200.
            System.out.println("Status: " + statusAttributes.get("x-ms-status-code").toString());

            // Total Request Units (RUs) charged for the operation, after a successful run.
            System.out.println("Total charge: " + statusAttributes.get("x-ms-total-request-charge").toString());
        }

        System.out.println("Demo complete!\n Press Enter key to continue...");
        try{
            System.in.read();
        } catch (IOException e){
            e.printStackTrace();
            return;
        }

        // Properly close all opened clients and the cluster
        cluster.close();

        System.exit(0);
    }
}
hosts: [***.gremlin.cosmosdb.azure.com]
port: 443
username: /dbs/db/colls/coll

connectionPool: {
  enableSsl: true}
serializer: { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV2d0, config: { serializeResultToString: true }}