配置
驱动程序类名
Neo4j JDBC 驱动程序的类名为 org.neo4j.jdbc.Neo4jDriver。在使用现代 Java 工具时,通常不需要直接接触此类,但某些连接池和前端工具会要求提供该类名。该类属于公共 API。
我们还提供了 org.neo4j.jdbc.Neo4jDataSource,它实现了 javax.sql.DataSource 接口。
URL 和连接属性
Neo4j JDBC 驱动程序的规范 URL 格式为
jdbc:neo4j://<host>:<port>/<database>?param1=value1¶m2=value2
数据库名称和所有查询参数均为可选,可以省略。所有配置参数既可以通过查询参数传递,也可以通过 java.util.Properties 对象传递。后者有时由工具为您定义。
关于身份验证,我们建议遵循 JDBC 规范,该规范不鼓励使用任何形式的 URL 身份验证。如果查询参数包含特殊字符(例如 …?param1=space%20separated),则必须进行 百分比编码 (percent-encoded)。
|
驱动程序支持以下 URI 方案,这些方案会调整安全配置:
不支持 |
最后,当使用 2025.06 及更高版本且启用了 Http 查询 API 的 Neo4j 实例时,可以将 http 添加为子协议。
jdbc:neo4j:http//<host>:<port>/<database>?param1=value1¶m2=value2
jdbc:neo4j:https//<host>:<port>/<database>?param1=value1¶m2=value2
通常,Neo4j 在 7474 或 7473 端口提供 HTTP 服务。如果不指定端口,Neo4j JDBC 驱动程序默认将 http 和 https 分别指向 80 和 443 端口。
| 查询 API 在少数极端情况下的行为有所不同。特别是对于大数据集,驱动程序会将其完全物化,而不是像二进制 neo4j 协议那样流式传输单个记录。此外,如果查询产生 n 个结果,但在第 n+1 个结果时失败,通过 HTTP 运行时会立即报错。 某些功能(例如对 Vector 数据类型的支持和事务元数据)在 HTTP 上完全不支持。 |
驱动程序接受以下配置参数,既可以作为属性也可以作为 URL 查询参数:
| 名称 | 类型 | 含义 | 默认 |
|---|---|---|---|
|
|
连接获取超时时间(毫秒) |
|
|
|
用户代理 (User agent) |
|
|
|
启用 SQL 到 Cypher 自动转换的标志(需要在类路径中包含翻译器) |
|
|
|
启用翻译缓存的标志。SQL 翻译并非“免费”:解析 SQL 和 Cypher® 渲染都需要一定时间。此外,我们可能需要查找元数据以映射单个属性。如果耗时过长,可以缓存翻译结果。 |
|
|
|
允许在 Cypher 语句中使用 |
当 |
|
|
可选标志,是 |
|
|
|
用于对 SSL 配置进行精细控制的可选配置。允许的值为 |
|
|
|
用于身份验证的用户名(主体)。出于安全考虑,不建议将其作为 URL 查询参数使用。 |
|
|
|
用于身份验证的密码(凭据)。出于安全考虑,不建议将其作为 URL 查询参数使用。 |
|
|
|
用于身份验证的领域 (Realm)。出于安全考虑,不建议将其作为 URL 查询参数使用。 |
|
|
|
要使用的身份验证方案。出于安全考虑,不建议将其作为 URL 查询参数使用。目前支持的值为:
|
|
|
|
启用书签管理以获得完整的因果集群支持。默认情况下已启用,这是使用连接池的所有场景的推荐设置。如果禁用它,则仅对该特定连接禁用。从驱动程序实例获取的到相同或其他数据库的其他连接不受影响,且各个连接仍将管理其书签。 |
|
|
|
指向有效的 JSON 文件的文件路径、http 或 https URL,其中包含 Cypher 支持的视图的定义。 |
|
|
|
身份验证提供程序的名称,可用于插件化各种 SSO 系统(如 Keycloak),并支持自动令牌刷新。有关更多信息,请参阅下文的 自定义身份验证。 |
|
|
|
将此值设置为
|
|
只要添加了有效的 Netty 传输依赖项,它就会被发现并替代默认的 NIO 传输。这些依赖项与操作系统相关,有效选项为 netty-transport-native-epoll, netty-transport-native-kqueue 以及 Netty 4.2 或更高版本上的 netty-transport-native-io_uring。 |
获取驱动程序或连接实例
获取连接的最简单方法是直接通过 java.sql.DriverManager。
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
class Configuration {
void obtainConnection() throws SQLException {
var url = "jdbc:neo4j://:7687";
var username = "neo4j";
var password = "verysecret";
var connection = DriverManager.getConnection(url, username, password);
}
}
虽然我们的连接实现是线程安全的,但每个连接只允许一个并发事务(根据 JDBC 规范)。对于多线程应用程序,请使用连接池。虽然有 HikariCP,但通常应用服务器和容器/框架会自带连接池。使用它们中的任何一个都是安全的,因为 Neo4j JDBC 驱动程序本身不进行内部连接池管理。
如果您需要访问 Neo4j 驱动程序本身的实例,可以使用以下方法:
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
class Configuration {
void obtainDriverAndConnection() throws SQLException {
var url = "jdbc:neo4j://:7687";
var driver = DriverManager.getDriver(url);
var properties = new Properties();
properties.put("username", "neo4j");
properties.put("password", "verysecret");
var connection = driver.connect(url, properties);
}
}
使用 SSL 保护连接
Neo4j JDBC 驱动程序支持通用 Java 驱动程序的相同 SSL 选项,使用相同的 URL 协议,并使用 +s 或 +ssc 作为所需安全级别的附加指示符。
相同的配置也可以通过 URL 查询参数,或在请求连接时传递给 DriverManager 或驱动程序实例的属性条目来实现。只要您不指定相互矛盾的值,结合使用这两种方法是可以的。
理解 SSL 模式
以下列表按安全性升序排列:
-
disable— (默认),“我不关心安全性,也不想支付加密带来的开销。” -
require— “我希望数据被加密,并接受相应的开销。我相信网络能确保我始终连接到我想要的服务器。”(服务器必须支持加密,不进行主机名/CA 验证。这避免了配置证书的麻烦,仅在专用网络上安全;不应在公共网络上使用。) -
verify-full— “我希望数据被加密,并接受相应的开销。我希望确保我连接的是我信任的服务器,并且是我指定的服务器。”
| Neo4j JDBC 驱动程序不包含证书撤销检查。 |
最安全的选项也可以通过将 ssl=true 设置为查询参数或传递给 DriverManager 的属性条目来启用。此选项对应于 neo4j+s。另一方面,require 对应于 neo4j+ssc。
附加的枚举类型使我们将来可能支持其他模式,例如让服务决定 SSL 设置,或者在不强制要求的情况下表达对 SSL 的偏好。
Neo4j 服务器可以同时提供普通的 Bolt 连接和加密的 SSL 连接,或仅提供其中之一。您可以使用 neo4j+s 进行连接,并不意味着您不能只使用 neo4j 进行连接,反之亦然。这取决于服务器的设置。Neo4j Aura(Neo4j 的托管云服务)仅支持加密连接,因此您必须使用 +s, ssl=true 或 sslMode=verify-full。 |
有效 URL
以下 URL 均有效:
neo4j+s://xyz.databases.neo4j.io-
对 AuraDB 中的
xzy实例使用完全验证 neo4j://xyz.databases.neo4j.io?ssl=true-
相同,但使用简写 URL 参数
neo4j://xyz.databases.neo4j.io?sslMode=verify-full-
相同,但使用显式模式
neo4j+s://xyz.databases.neo4j.io?ssl=true&sslMode=verify-full-
没有更安全,但不会失败
neo4j+ssc://this.is.a.trustworthy.instance.for.sure.com-
信任任何证书和主机名,但务必使用 SSL
neo4j://my-testing-instance.local-
使用普通连接。
驱动程序仅会拒绝相互矛盾的配置,例如:
-
+s与ssl=false连用,或sslMode设置为disable -
+ssc与ssl=false连用,或任何不等于require的sslMode
基本上,您不能同时要求使用 SSL 又不使用它。驱动程序提供了多种机制,以便您可以将固定 URL 与动态查询参数结合使用,或使用动态 URL,或者以您喜欢的任何方式进行编程配置。
使用 .dotenv 文件
当您创建 Neo4j Aura 实例时,系统会要求您下载一个名为 Neo4j-9df57663-Created-2023-06-12.txt 类似的文本文件。这本质上是一个包含连接数据库所需信息的 .dotenv 文件。
这些文件可以通过 Neo4jDriver.fromEnv() 直接使用(请参阅 通过环境变量获取连接)。此方法有多个重载版本,允许您配置文件名和目录。此外,构建器允许您配置 Aura 文件中未包含的选项。
因果集群与书签
Neo4j JDBC 驱动程序默认使用书签以在所有 Neo4j 部署中提供因果一致性。书签是在驱动程序级别本身管理的,而不是在驱动程序实例生成的连接上管理的,因此由一个实例生成的所有连接都将参与同一个事务因果链。来自不同驱动程序实例的连接不会使用同一组书签,并且没有内置的机制来实现这一点。如果您需要这样做,可以直接访问 Neo4jDriver 类型以检索当前已知书签集合,并将它们传递给另一个驱动程序实例。
Neo4j 事务元数据
Neo4j 支持将元数据附加到事务中,请参阅 SHOW TRANSACTIONS。由于 JDBC 规范中没有显式的事务对象,Neo4j JDBC 驱动程序需要另一种机制使其可配置。
JDBC 驱动程序提供了扩展接口 Neo4jMetadataWriter。我们的驱动程序、连接实现以及所有语句变体都可以相应地进行解包。配置是累加的:为驱动程序实例配置的元数据将用于从该驱动程序生成的所有连接,连接可以添加进一步的元数据,语句也可以添加自己的元数据。在语句上添加的元数据优先级高于连接元数据,连接元数据的优先级高于驱动程序元数据。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.neo4j.jdbc.Neo4jDriver;
import org.neo4j.jdbc.Neo4jMetadataWriter;
public final class TransactionMetadata {
private static final Logger LOGGER = Logger.getLogger(TransactionMetadata.class.getPackageName());
public static void main(String... args) throws SQLException {
var url = "jdbc:neo4j://:7687";
var driver = (Neo4jDriver) DriverManager.getDriver(url);
driver.withMetadata(Map.of("md_from_driver", "v1", "will_be_overwritten", "irrelevant"));
var properties = new Properties();
properties.put("user", "neo4j");
properties.put("password", "verysecret");
try (
var con = driver.connect(url, properties)
.unwrap(Neo4jMetadataWriter.class)
.withMetadata(Map.of("md_from_connection", "v2", "will_be_overwritten", "xxx"))
.unwrap(Connection.class);
var statement = con.createStatement()
.unwrap(Neo4jMetadataWriter.class)
.withMetadata(Map.of("md_from_stmt", "v3", "will_be_overwritten", "v4"))
.unwrap(Statement.class)
) {
try (var result = statement.executeQuery("SHOW TRANSACTIONS YIELD metaData")) {
while (result.next()) {
var metaData = result.getObject("metaData", Map.class);
LOGGER.log(Level.INFO, "{0}", metaData);
}
}
}
}
}
输出将类似于
Juli 17, 2024 1:18:16 PM org.neo4j.jdbc.docs.TransactionMetadata main
INFORMATION: {md_from_driver=v1, md_from_connection=v2, md_from_stmt=v3, will_be_overwritten=v4}
日志记录
通过标准 Java 设置
要生成有用且详细的日志,必须配置 java.util.logging。这可以通过配置文件或以编程方式完成。以下部分包含一个示例配置,将全局日志级别设置为 INFO,将 Neo4j JDBC 驱动程序本身设置为 DEBUG,并将所有与网络相关的内容设置为 WARN。
.level = INFO
handlers=java.util.logging.ConsoleHandler
# The handler must be configured for the lowest level it should catch
java.util.logging.ConsoleHandler.level=FINEST
# Enables all Logging for the JDBC Driver
org.neo4j.jdbc.level=ALL
# But reduces the noise on the network and the result set
org.neo4j.jdbc.result-set.level=WARNING
org.neo4j.jdbc.network.level=WARNING
# This is an example how to configure the outbound and inbound messages
org.neo4j.jdbc.network.OutboundMessageHandler.level=FINE
org.neo4j.jdbc.network.InboundMessageHandler.level=FINE
# This produces a date, followed by the level, 3 dashes, the message and the source of the log if available
java.util.logging.SimpleFormatter.format = %1$tFT%1$tk:%1$tM:%1$tS.%1$tL%1$tz %4$-15s --- %5$s [%2$s]%n
此类文件必须通过系统属性传递给正在运行的 Java 程序:
java -Djava.util.logging.config.file=docs/src/main/resources/logging-to-console.properties <your program>
将日志捕获到文件需要更改处理程序:
.level = INFO
handlers=java.util.logging.ConsoleHandler
# Keep everything by default at INFO
java.util.logging.ConsoleHandler.level=INFO
# The handler must be configured for the lowest level it should catch
java.util.logging.FileHandler.level=FINEST
java.util.logging.FileHandler.pattern=jdbc.log
# XML Format by default
# java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
# Change the handler for JDBC
org.neo4j.jdbc.handlers=java.util.logging.FileHandler
# Keep the rest as above
org.neo4j.jdbc.level=ALL
org.neo4j.jdbc.result-set.level=WARNING
org.neo4j.jdbc.network.level=WARNING
org.neo4j.jdbc.network.OutboundMessageHandler.level=FINE
org.neo4j.jdbc.network.InboundMessageHandler.level=FINE
java.util.logging.SimpleFormatter.format = %1$tFT%1$tk:%1$tM:%1$tS.%1$tL%1$tz %4$-15s --- %5$s [%2$s]%n
JDBC 驱动程序的诊断功能基于标准的 java.util.logging 框架。该框架本身的配置超出了本文档的范围。如需完整概述,您可以从 "Java 日志概述" 开始。Java 使用的格式化程序是 java.util.Formatter。 |
在 Spring Boot 应用程序内部
Neo4j JDBC 驱动程序将集成到 Spring Boot 日志配置中,并从中获取所有相关的格式和其他设置。级别可以使用 Spring 理解的级别进行配置。以下配置等同于上面的配置:
logging.level.org.neo4j.jdbc=trace
logging.level.org.neo4j.jdbc.result-set=warn
logging.level.org.neo4j.jdbc.network=warn
logging.level.org.neo4j.jdbc.network.OutboundMessageHandler=debug
logging.level.org.neo4j.jdbc.network.InboundMessageHandler=debug
在 Quarkus 应用程序内部
Neo4j JDBC 驱动程序将集成到 Quarkus 日志配置中,并从中获取所有相关的格式和其他设置。级别可以使用 Quarkus 理解的级别进行配置。以下配置等同于上面的配置:
quarkus.log.category."org.neo4j.jdbc".min-level=TRACE
quarkus.log.category."org.neo4j.jdbc".level=TRACE
quarkus.log.category."org.neo4j.jdbc.result-set".level=DEBUG
quarkus.log.category."org.neo4j.jdbc.network".level=WARNING
quarkus.log.category."org.neo4j.jdbc.network.OutboundMessageHandler".level=DEBUG
quarkus.log.category."org.neo4j.jdbc.network.InboundMessageHandler".level=DEBUG
指标 (Metrics)
Neo4j JDBC 驱动程序将通过 Micrometer 发布指标。如果您将 Micrometer 放在类路径中,则无需进一步配置。例如,Spring Boot Actuator 或 Micrometer for Quarkus 会自动包含此功能。为了充分利用此功能,您还需要配置您的 监控系统。
Neo4j JDBC 驱动程序发布以下指标:
org.neo4j.jdbc.connections-
一个表示打开连接数的仪表 (gauge),带有连接 URL 标签
org.neo4j.jdbc.statements-
一个表示打开语句数的仪表,带有打开语句的 URL 和 JDBC 语句类型(
Statement,PreparedStatement或CallableStatement)标签 org.neo4j.jdbc.queries-
一个复合度量,包含成功和失败查询的计数,以及测量查询持续时间的计时器
org.neo4j.jdbc.cached-translations-
一个表示缓存的 SQL 到 Cypher 翻译数量的仪表
追踪
Neo4j JDBC 驱动程序支持追踪,并将为 JDBC Statement 实例上的以下操作提供追踪跨度 (trace spans):
-
#execute -
#executeQuery -
#executeUpdate
以及它们的所有重载版本和所有继承类型。此外,在使用 JDBC ResultSet 时,它会在结果集从第一行之前首次移动到超出最后一行或被主动关闭时打开一个跨度。跨度将包含各种事件,例如查询何时由 Neo4j 数据库实际执行,或记录批次何时从数据库中提取。
该功能是可选的:当您想使用它时,必须引入额外的依赖项。这适用于所有分发版本,包括完整包,以确保可选功能不会增加包的大小。对于 Maven,请使用以下附加依赖项声明:
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-jdbc-tracing-micrometer</artifactId>
<version>6.9.1</version>
</dependency>
对于 Gradle,您需要声明以下运行时依赖项:
dependencies {
runtimeOnly 'org.neo4j:neo4j-jdbc-tracing-micrometer:6.9.1'
}
此依赖项引入了 Micrometer Tracing。Micrometer Tracing 允许您绑定到各种追踪器和导出器,例如 Zipkin 或 OpenTelemetry。
一旦拥有 Micrometer 追踪器,就可以按如下方式配置 Neo4jDataSource:
Neo4jDataSourceimport javax.sql.DataSource;
import org.neo4j.jdbc.Neo4jDataSource;
import org.neo4j.jdbc.tracing.micrometer.Neo4jTracingBridge;
import io.micrometer.tracing.Tracer;
public class TracingExampleSetup {
DataSource neo4jDataSource() {
Tracer tracer = Tracer.NOOP; (1)
var neo4jDataSource = new Neo4jDataSource();
neo4jDataSource.setUrl("jdbc:neo4j://yourhost:7687");
neo4jDataSource.setPassword("neo4j");
neo4jDataSource.setUser("verysecret");
neo4jDataSource.setTracer(Neo4jTracingBridge.to(tracer)); (2)
return neo4jDataSource;
}
}
| 1 | 在大多数情况下,您的应用程序框架将为您提供一个 Micrometer 追踪器实例。 |
| 2 | 通过此调用,您可以启用数据源以及从中生成的所有连接的追踪。 |
您还可以通过将单个连接解包为 Neo4jConnection 扩展来在它们上启用追踪:
import java.sql.Connection;
import java.sql.DriverManager;
import org.neo4j.jdbc.Neo4jConnection;
import org.neo4j.jdbc.tracing.micrometer.Neo4jTracingBridge;
import io.micrometer.tracing.Tracer;
public class TracingExampleSetup {
Connection connection() {
Tracer tracer = Tracer.NOOP;
Neo4jConnection connection = DriverManager.getConnection("jdbc:neo4j://yourhost:7687", "neo4j", "verysecret")
.unwrap(Neo4jConnection.class); (1)
connection.setTracer(Neo4jTracingBridge.to(tracer)); (2)
}
}
| 1 | 将连接解包到我们的扩展中,以便配置追踪器 |
| 2 | 配置追踪器 |
如果您既不想使用数据源也不想使用单个连接,请使用 Neo4jDriver.registerTracer(Neo4jTracingBridge.to(tracer)) 向所有在 JDBC DriverManager 中注册的 Neo4j 驱动程序注册追踪器。 |
在 Spring Boot 中使用追踪
Neo4j JDBC 驱动程序提供的追踪实现利用了 Micrometer Tracing,这在 Spring Boot 生态系统中得到了广泛认可,在 Quarkus、Micronaut 等中也是如此。我们提供了一些示例,但请参阅您框架的文档了解如何配置追踪。
如果您乐意为通过 DriverManager 获取的所有连接配置追踪,请按如下方式配置:
DriverManager 中注册的驱动程序配置追踪import io.micrometer.tracing.Tracer;
import org.neo4j.jdbc.tracing.micrometer.Neo4jTracingBridge;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TracingConfiguration {
@Bean
InitializingBean neo4jTracingInitializer(Tracer tracer) {
return () -> Neo4jDriver.registerTracer(Neo4jTracingBridge.to(tracer));
}
}
否则,请使用如下所示的自定义数据源。
配置追踪数据源
通常,Spring Boot 会根据您的驱动程序配置 HikariDataSource。Hikari 提供连接池,您不应放弃它。无需为所有配置值创建自己的数据源属性,只需按如下方式注入即可:
import javax.sql.DataSource;
import org.neo4j.jdbc.Neo4jDataSource;
import org.neo4j.jdbc.tracing.micrometer.Neo4jTracingBridge;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.micrometer.tracing.Tracer;
@Configuration
class TracingTestConfiguration {
@Bean
DataSource neo4jDataSource(Tracer tracer, DataSourceProperties dataSourceProperties) {
var neo4jDataSource = new Neo4jDataSource();
neo4jDataSource.setUrl(dataSourceProperties.getUrl());
neo4jDataSource.setPassword(dataSourceProperties.getPassword());
neo4jDataSource.setUser(dataSourceProperties.getUsername());
neo4jDataSource.setTracer(Neo4jTracingBridge.to(tracer));
var cfg = new HikariConfig();
cfg.setDataSource(neo4jDataSource);
return new HikariDataSource(cfg);
}
}
将 org.springframework.boot:spring-boot-starter-actuator 也添加到类路径中,它将为您配置 Micrometer。
| 您的数据源设置保持不变:对于以下示例要工作,此处无需进行任何更改,因为我们的追踪 API 将通过 Micrometer 自动选择正确的追踪器! |
导出到 Zipkin
要将追踪导出到 Zipkin,请添加 io.micrometer:micrometer-tracing-bridge-brave 和 io.zipkin.reporter2:zipkin-reporter-brave,并在 Spring 应用程序中将 management.zipkin.tracing.export.enabled 设置为 true。
您应该会看到如下所示的跨度:
通过 OpenTelemetry 导出
OpenTelemetry 不仅允许导出追踪,还允许导出指标和日志。
以下是通过 OpenTelemetry 使用 Dash0 的示例。导出器对于其他服务应该同样有效。OpenTelemetry 已将其 SDK 分割为多个模块,因此您首先需要将它们的 BOM 导入构建文件。以下是 Maven 的示例:
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-bom</artifactId>
<version>1.47.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
首先,您需要通过以下方式引入从 Micrometer 到 OpenTelemetry 的桥接:io.micrometer:micrometer-tracing-bridge-otel,以便 Micrometer 推送到该 SDK。接下来,引入以下依赖项,以便 Spring Boot 可以配置导出器:
-
io.opentelemetry:opentelemetry-sdk -
io.opentelemetry:opentelemetry-exporter-common -
io.opentelemetry:opentelemetry-exporter-otlp -
io.opentelemetry:opentelemetry-exporter-sender-jdk
对于 Dash0,应用程序配置将类似于:
management.otlp.tracing.export.enabled=true
management.otlp.tracing.endpoint=https://ingress.eu-west-1.aws.dash0.com/v1/traces
management.otlp.tracing.headers.Authorization=Bearer auth_XX_your_token_XX
management.otlp.metrics.export.step=30s
management.otlp.metrics.export.enabled=true
management.otlp.metrics.export.url=https://ingress.eu-west-1.aws.dash0.com/v1/metrics
management.otlp.metrics.export.headers.Authorization=Bearer auth_XX_your_token_XX
我们的电影应用程序的追踪记录如下:
自定义身份验证
Neo4j JDBC 驱动程序除了 JDBC 标准身份验证方式(例如使用 java.sql.DriverManager#getConnection(java.lang.String, java.lang.String, java.lang.String) 或 java.sql.Driver#connect 进行服务器身份验证)之外,还提供了使用自定义身份验证提供程序的功能。
身份验证提供程序在 Java 中是指 Supplier<org.neo4j.jdbc.authn.api.Authentication>。Authentication 实例是提供给 Neo4j 服务器的任意身份验证的抽象表示,可以包含主体及其凭据(用户名和密码)、令牌或其他确认身份的方式。
该接口本身提供以下工厂方法:
Authentication#usernameAndPassword-
创建简单的基于用户名和密码的身份验证,这正是默认方法在内部已使用的内容。
Authentication#none-
禁用身份验证
Authentication#bearer-
创建基于持有者令牌的身份验证
后者允许设置令牌的过期日期。如果令牌过期,则在过期日期之后,会要求提供程序获取新令牌。这允许配置 Neo4j JDBC 驱动程序,使其可以从 SSO 提供程序(如 Keycloak 或 Okta)获取令牌,并使用这些令牌针对 Neo4j 进行身份验证,并在它们过期时自动刷新。
此外,如果上述工厂方法不够用,还可以实现 CustomAuthentication 和 ExpiringAuthentication 两个接口。
有几种方法可以传递身份验证提供程序:
-
创建一个包含自定义身份验证提供程序和实现
AuthenticationSupplierFactory的小型工厂的库:它将自动加载到 JDBC 驱动程序中,并在 JDBC 属性authn.supplier设置为工厂名称时使用。 -
在驱动程序本身上,当解包为
Neo4jDriver时,可以使用driver#connect(String, Properties, Supplier)。给定的提供程序将优先于properties参数中的任何身份验证信息。 -
使用
driver#setAuthenticationSupplier(Supplier)在驱动程序的特定实例上设置提供程序实例。 -
使用
Neo4jDriver#registerAuthenticationSupplier注册全局提供程序实例,该实例将用于所有由DriverManager生成的连接。 -
Neo4jDriver上作为静态方法存在的便捷构建器方法(允许从环境配置驱动程序,请参阅 通过环境变量获取连接)也允许指定提供程序。
优先级如下:
-
如果传递给
connect的有提供程序,它将优先于驱动程序本地配置的提供程序、全局提供程序以及任何属性。 -
如果在 JDBC 属性中有名为
authn.supplier的属性,并且提供了具有该名称的身份验证提供程序工厂,则将从该工厂获取并使用提供程序。 -
每个驱动程序配置的提供程序或全局提供程序优先于任何属性。
-
当没有显式或隐式身份验证提供程序时,将按照 JDBC 标准使用属性。
驱动程序仅在尚未检索到令牌时或第一个令牌过期时调用提供程序。任何身份验证提供程序实现都可以安全地假定第二次调用令牌意味着第一个令牌已过期,因此提供程序不需要管理状态。
| 底层的 Bolt 协议和 Neo4j 服务器可能无法与寿命极短的身份验证令牌(即寿命在 5 秒以下)可靠配合。此限制将来可能会取消,但截至 6.6.0 版本,您可能需要避免使用寿命极短的令牌。 |
Keycloak 示例
以下类显示了一个基于 Keycloak 的 AuthzClient 的示例。提供程序将从 Keycloak 实例获取令牌,并将它们的过期日期与 JWT 令牌一起存储。最初请求令牌时将始终返回一个全新的令牌,后续每次调用都将使用客户端刷新该令牌。
无需将以下代码复制到您自己的应用程序中。我们确实在可选的 org.neo4j:neo4j-jdbc-authn-kc 模块中提供了此身份验证提供程序。该提供程序是我们 semver API 合约的一部分,可以直接使用,也可以通过服务加载器机制使用,这在以下文档中也有演示。 |
KCAuthenticationSupplier,作为在必要时刷新令牌的身份验证提供程序示例import java.io.IOException;
import java.io.UncheckedIOException;
import java.time.Instant;
import java.util.Base64;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import com.fasterxml.jackson.jr.ob.JSON;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.Configuration;
import org.keycloak.authorization.client.util.Http;
import org.keycloak.representations.AccessTokenResponse;
import org.neo4j.jdbc.authn.spi.Authentication;
public final class KCAuthenticationSupplier implements Supplier<Authentication> {
public static Supplier<Authentication> of(String user, String password, Configuration configuration) {
return new KCAuthenticationSupplier(user, password, configuration);
}
private final String username;
private final String password;
private final Configuration cfg;
private final AuthzClient authzClient;
private final Http http;
private final String url;
private final AtomicReference<TokensAndExpirationTime> currentToken = new AtomicReference<>();
KCAuthenticationSupplier(String user, String password, Configuration cfg) {
this.username = user;
this.password = password;
this.cfg = cfg;
this.authzClient = AuthzClient.create(cfg);
this.url = "%s/realms/%s/protocol/openid-connect/token".formatted(cfg.getAuthServerUrl(), cfg.getRealm());
this.http = new Http(cfg, cfg.getClientCredentialsProvider());
}
@Override
public Authentication get() {
return this.currentToken.updateAndGet(previous -> {
if (previous == null) {
return get0();
}
return refresh0(previous.refreshToken());
}).toAuthentication();
}
TokensAndExpirationTime get0() {
try {
return TokensAndExpirationTime.of(this.authzClient.obtainAccessToken(this.username, this.password));
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
TokensAndExpirationTime refresh0(String refreshToken) {
try {
return TokensAndExpirationTime.of(this.http.<AccessTokenResponse>post(this.url)
.authentication()
.client()
.form()
.param("grant_type", "refresh_token")
.param("refresh_token", refreshToken)
.param("client_id", this.cfg.getResource())
.param("client_secret", (String) this.cfg.getCredentials().get("secret"))
.response()
.json(AccessTokenResponse.class)
.execute());
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
record TokensAndExpirationTime(String accessToken, Instant expiresAt, String refreshToken) {
static final Base64.Decoder DECODER = Base64.getDecoder();
static TokensAndExpirationTime of(AccessTokenResponse accessTokenResponse) throws IOException {
var chunks = accessTokenResponse.getToken().split("\\.");
var payload = JSON.std.mapFrom(DECODER.decode(chunks[1]));
var exp = Instant.ofEpochSecond(((Number) payload.get("exp")).longValue());
return new TokensAndExpirationTime(accessTokenResponse.getToken(), exp,
accessTokenResponse.getRefreshToken());
}
Authentication toAuthentication() {
return Authentication.bearer(this.accessToken, this.expiresAt);
}
}
}
同样,要使用上述提供程序,请将以下依赖项添加到您的类路径或模块路径中,不要复制这些代码,它们仅作为您如何编写自己的身份验证提供程序的示例显示在这里。
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-jdbc-authn-kc</artifactId>
<version>6.9.1</version>
</dependency>
现在使用提供程序的最简单方法是通过内置加载器,这样您可以完全遵循 JDBC API:
authn.supplier 配置选项使用自定义身份验证提供程序@Test
void authenticationSupplierFactoriesShouldWork() throws SQLException {
var properties = new Properties();
properties.put("enableSQLTranslation", "true");
properties.put("user", "william.foster");
properties.put("password", "d-fens");
properties.put("authn.supplier", "kc"); (1)
properties.put("authn.kc.authServerUrl", "https://:%d".formatted(KEYCLOAK.getHttpPort())); (2)
properties.put("authn.kc.realm", "neo4j-sso-test");
properties.put("authn.kc.clientId", "neo4j-jdbc-driver");
properties.put("authn.kc.clientSecret", "QcWXnTg8qJpVMnIvm8Ev8gp1PqJitZu4");
try (var connection = DriverManager.getConnection(neo4jUri, properties); (3)
var stmt = connection.createStatement();
var rs = stmt.executeQuery("SELECT 1")) {
assertThat(rs.next()).isTrue();
assertThat(rs.getInt(1)).isOne();
}
}
| 1 | 指示您要使用 Keycloak 身份验证提供程序(在我们的分发中命名为 kc) |
| 2 | 此属性及后续属性将传递给身份验证系统,以便它可以使用 Keycloak 创建身份验证 |
| 3 | 这是一个纯 JDBC 规范调用 |
如果您不想使用身份验证提供程序工厂的自动加载器,或者您编写了一个完全自定义的身份验证提供程序且不想提供 services 和相应的模块描述,还有其他几种使用身份验证提供程序的方法。
最符合 JDBC 的方式是为其注册全局驱动程序,如下所示:
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.function.Supplier;
import org.keycloak.authorization.client.Configuration;
import org.neo4j.jdbc.authn.kc.KCAuthenticationSupplier;
import org.neo4j.jdbc.authn.spi.Authentication;
import org.neo4j.jdbc.Neo4jDriver;
class Example {
public static void main(String...a) throws Exception {
Supplier<Authentication> authenticationSupplier = KCAuthenticationSupplier.of("user", "pass", new Configuration());
Neo4jDriver.registerAuthenticationSupplier(authenticationSupplier);
Connection connection = DriverManager.getConnection("neo4j+s://xyz.databases.neo4j.io");
}
}
这里的优点是它确保了所有通过 DriverManager 以符合 JDBC 标准的方式使用 Neo4j JDBC 驱动程序的应用程序都将自动使用该身份验证提供程序。因此,上述设置很可能是与 Spring Boot 等框架集成的最简单方法,您可以通过 org.springframework.beans.factory.InitializingBean 配置全局提供程序。或者使用驱动程序构建器或手动创建驱动程序实例。
Neo4jDriver 的显式实例,以便接收身份验证提供程序的 connect 重载可见import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
import java.util.function.Supplier;
import org.keycloak.authorization.client.Configuration;
import org.neo4j.jdbc.authn.kc.KCAuthenticationSupplier;
import org.neo4j.jdbc.authn.spi.Authentication;
import org.neo4j.jdbc.Neo4jDriver;
class Example {
public static void main(String...a) throws Exception {
Supplier<Authentication> authenticationSupplier = KCAuthenticationSupplier.of("user", "pass", new Configuration());
Neo4jDriver.registerAuthenticationSupplier(authenticationSupplier);
Neo4jDriver driver = new Neo4jDriver();
Connection connection = driver.connect("neo4j+s://xyz.databases.neo4j.io", new Properties(), authenticationSupplier);
}
}
对于您可以完全控制创建连接的任何应用程序,此替代方案是可行的。
| 如果您需要针对 Keycloak 进行开发,Neo4j JDBC 驱动程序提供了您所需的一切,您可以直接开始使用。只需包含 Maven 模块,并应用通过 JDBC 属性配置提供程序的推荐方式。仅在有其他身份验证需求时才编写自定义身份验证提供程序。 |