Azure Cosmos DB Gremlin/Tinkerpop 令牌身份验证 Java SDK
Azure Cosmos DB Gremlin/Tinkerpop Token Auth with Java SDK
我正在尝试使用资源令牌连接到 Azure Cosmos DB 中的 Gremlin collection。我从这里改编了文档(主要针对 C#):https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-use-resource-tokens-gremlin
问题是,当我尝试访问数据时,令牌的日期 header 似乎无效:
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给的,方法名应该是self-explanatory.
令牌生成,这是使用 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();
}
}
}
连接并查询 Gremlin:
...
import org.apache.tinkerpop.gremlin.driver.AuthProperties;
import org.apache.tinkerpop.gremlin.driver.AuthProperties.Property;
import org.apache.tinkerpop.gremlin.driver.Client;
import org.apache.tinkerpop.gremlin.driver.Cluster;
import org.apache.tinkerpop.gremlin.driver.ResultSet;
...
Cluster cluster;
String collectionLink = String.format("/dbs/%s/colls/%s", cfg.getDatabaseId(), cfg.getCollectionId());
TokenGenerator tg = new TokenGenerator();
String token = tg.generateToken(cfg);
Cluster.Builder builder = Cluster.build(new File("src/remote.yaml"));
AuthProperties authenticationProperties = new AuthProperties();
authenticationProperties.with(AuthProperties.Property.USERNAME, collectionLink);
authenticationProperties.with(Property.PASSWORD, token);
builder.authProperties(authenticationProperties);
cluster = builder.create();
Client client = cluster.connect();
ResultSet results = client.submit("g.V().limit(1)");
// the following call fails
results.stream().forEach(System.out::println);
client.close();
cluster.close();
}
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 }}
我无法在我这边重现你的问题。我尝试扩展此 cosmos graph demo for java.In that demo,master key is used,so i followed your partial code and official sample 以使用资源令牌访问图形数据库。
您与数据库的连接代码似乎 working.My main class 如下,与您的类似:
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);
}
}
yaml 文件:
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 }}
所以,恐怕根本原因是令牌的生成。
The input date header is invalid format. Please pass in RFC 1123 style
date format.
我搜索了这个与 cosmos db 相关的错误消息,我发现这个 case 适合你的 reference.It 似乎解决方案正在改变本地区域 settings.You 可以追踪 x-ms-date
header 你对 Fiddler 的请求。
通过 Azure 支持,我发现问题是 Azure 方面的错误。它发生在将自定义 VNet 与 Cosmos 一起使用时。与此同时,Azure 已修复它,一切正常。
我正在尝试使用资源令牌连接到 Azure Cosmos DB 中的 Gremlin collection。我从这里改编了文档(主要针对 C#):https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-use-resource-tokens-gremlin
问题是,当我尝试访问数据时,令牌的日期 header 似乎无效:
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
这是代码。请注意,这是一个仅用于测试连接性的 Java CLI 应用程序。 cfg
的所有数据基本上都是cli给的,方法名应该是self-explanatory.
令牌生成,这是使用 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();
}
}
}
连接并查询 Gremlin:
...
import org.apache.tinkerpop.gremlin.driver.AuthProperties;
import org.apache.tinkerpop.gremlin.driver.AuthProperties.Property;
import org.apache.tinkerpop.gremlin.driver.Client;
import org.apache.tinkerpop.gremlin.driver.Cluster;
import org.apache.tinkerpop.gremlin.driver.ResultSet;
...
Cluster cluster;
String collectionLink = String.format("/dbs/%s/colls/%s", cfg.getDatabaseId(), cfg.getCollectionId());
TokenGenerator tg = new TokenGenerator();
String token = tg.generateToken(cfg);
Cluster.Builder builder = Cluster.build(new File("src/remote.yaml"));
AuthProperties authenticationProperties = new AuthProperties();
authenticationProperties.with(AuthProperties.Property.USERNAME, collectionLink);
authenticationProperties.with(Property.PASSWORD, token);
builder.authProperties(authenticationProperties);
cluster = builder.create();
Client client = cluster.connect();
ResultSet results = client.submit("g.V().limit(1)");
// the following call fails
results.stream().forEach(System.out::println);
client.close();
cluster.close();
}
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 }}
我无法在我这边重现你的问题。我尝试扩展此 cosmos graph demo for java.In that demo,master key is used,so i followed your partial code and official sample 以使用资源令牌访问图形数据库。
您与数据库的连接代码似乎 working.My main class 如下,与您的类似:
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);
}
}
yaml 文件:
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 }}
所以,恐怕根本原因是令牌的生成。
The input date header is invalid format. Please pass in RFC 1123 style date format.
我搜索了这个与 cosmos db 相关的错误消息,我发现这个 case 适合你的 reference.It 似乎解决方案正在改变本地区域 settings.You 可以追踪 x-ms-date
header 你对 Fiddler 的请求。
通过 Azure 支持,我发现问题是 Azure 方面的错误。它发生在将自定义 VNet 与 Cosmos 一起使用时。与此同时,Azure 已修复它,一切正常。