使用Spring Boot连接到Azure Database for PostgreSQL

首先

引发我写这篇文章的契机是这里的微软官方页面。

在 Azure 用于 PostgreSQL 的数据库中,使用 Spring Data JDBC。

这个页面上关于 PostgreSQL 认证和 Azure AD 认证的构建方法混合在一起,非常难以理解。而且无论是哪种情况,按照页面上的说明实施仍然会出现问题。在 GitHub 上公开的示例程序也无法运行。虽然我打算提交 Pull Request 并耐心地进行修复,但我不知道何时才能得到反馈。因此,我将在2023/8之前确保记下确切可行的操作方法。

以下的说明是个人根据Microsoft官方网页补充的内容。

环境

    • openjdk 17.0.8 2023-07-18 LTS

 

    • Apache Maven 3.9.4

 

    pgAdmin 4

在Mac环境下已经确认过了。虽然在Windows环境下没有确认过,但应该没有特别需要更改的地方。

如果您在设置相同的环境时不想在本地安装Java环境和版本管理,我建议您使用VSCode的Remote Container(DevContainer)功能。如果您选择使用它,请使用以下的devcontainer.json文件。

如果您在环境中不需要 Azure CLI,请删除下一行。但是,如果要测试Azure AD身份验证(不使用密码),使用Azure CLI会更方便。

“ghcr.io/devcontainers/features/azure-cli:1”: {}
“ghcr.io/devcontainers/features/azure-cli:1″:{}

// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/java
{
	"name": "Java",
	// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
	"image": "mcr.microsoft.com/devcontainers/java:1-17-bullseye",
	"features": {
		"ghcr.io/devcontainers/features/java:1": {
			"version": "none",
			"installMaven": "true",
			"installGradle": "false"
		},
		"ghcr.io/devcontainers/features/azure-cli:1": {}
	},
	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	// "forwardPorts": [],
	// Use 'postCreateCommand' to run commands after the container is created.
	// "postCreateCommand": "java -version",
	// Configure tool-specific properties.
	"customizations": {
		"vscode": {
			"extensions": [
				"vscjava.vscode-java-pack",
				"vmware.vscode-boot-dev-pack",
				"pkief.material-icon-theme"
			]
		}
	}
	// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
	// "remoteUser": "root"
}

创建 Azure 数据库 for PostgreSQL

在Azure数据库中,可以选择Single Server和Flexible Server两种类型的PostgreSQL。务必选择Flexible Server。Single Server计划在2025年3月28日停用。请查阅此处以了解更多详细信息。

身份验证的类型 de

在Azure数据库中的PostgreSQL,您可以在创建时选择三种不同的认证类型。

    • PostgreSQL の認証のみ

 

    • Azure Active Directory 認証のみ

 

    PostgreSQL と Azure Active Directory の認証

这次我们选择使用最新版本的PostgreSQL和Azure Active Directory进行身份验证。为了说明使用Spring Boot如何进行设置,在每个身份验证方案中,请先进行Azure AD管理员的配置并选择您自己。(请注意,身份验证类型在创建后可以更改)

image.png

在上图的屏幕截图中,Azure AD管理员的设置未被选择,但实际上请确保选择自己。

设置Azure数据库的PostgreSQL防火墙

Azure Database for PostgreSQL有防火墙功能,必须明确允许IP地址才能访问。为了从本地运行的Spring Boot访问Azure Database for PostgreSQL,需要将本地计算机的全局IP地址注册到防火墙上。

在Azure Portal中打开Azure Database for PostgreSQL,并选择左侧菜单中的网络选项。在右侧屏幕的底部找到“添加当前客户端IP地址”的选项并点击,然后点击屏幕顶部的保存图标。

image.png

创建数据库

我会创建一个数据库作为这次的连接。从Azure门户创建数据库是最简单的。选择左侧菜单的数据库,然后点击右侧屏幕顶部的添加按钮来创建数据库。我创建了一个名为testdb的数据库。

image.png

使用Spring Boot从PostgreSQL进行身份验证访问

首先是最基本的PostgreSQL认证。这是微软官方页面中作为密码(认证)的指示。请注意根据安全考虑,微软不推荐使用该认证方法。

创建普通用户

在Microsoft的官方网页上,我们从创建普通用户而不是管理员用户开始进行操作,但这个步骤并非必须。我认为这是为了防止管理员用户密码泄露而从安全角度出发首先创建普通用户,但这个步骤会导致在出现问题时难以确定问题所在。最好的方法是首先使用管理员用户进行连接尝试,然后再创建普通用户并切换连接信息。

由于Microsoft官方页面的信息存在不完善之处,我会在接下来的内容中解释一般用户的创建方式以及权限的授予。

使用DB工具连接到Azure数据库的PostgreSQL。

如果在本地安装了PostgreSQL,则会同时安装一款名为psql的命令行工具,可以使用该工具连接到Azure Database for PostgreSQL。然而,如果不需要准备本地数据库,则可以选择仅安装psql,或使用类似pgAdmin的工具。

由于我喜欢使用像pgAdmin这样的图形用户界面工具处理数据库,所以我选择使用pgAdmin。

 

使用 pgAdmin 进行连接时,请使用在创建 Azure 数据库 for PostgreSQL 时指定的用户ID/密码进行连接。

为了做测试,制作一张桌子。

打开Microsoft官方页面,将其作为参考。然后打开pgAdmin查询工具,对之前创建的数据库运行以下SQL语句。

DROP TABLE IF EXISTS todo;
CREATE TABLE todo (id SERIAL PRIMARY KEY, description VARCHAR(255), details VARCHAR(4096), done BOOLEAN);
image.png

创建一个Spring Boot应用程序

使用Spring Initializr

使用VSCode的Spring扩展,就不需要在浏览器中使用Spring Initializr,但为了方便理解使用了哪些依赖,我在这里附上Spring Initializr页面的截图。

请务必添加以下三个依存关系,其他则可自选。

    • Spring Web

 

    • Spring Data JDBC

 

    PostgreSQL Driver
image.png

在Microsoft官方页面上,我们需要将Spring Cloud Azure的模块添加到pom.xml文件中,但对于PostgreSQL认证来说,这是不必要的。

在application.properties文件中写入配置

请打开 application.properties 文件,并进行以下设置。请根据自己的环境进行四处修改。当然,删除前后的 <> 符号。

    • your_postgresql_name

 

    • dbname(urlの後ろに付いているのでお見逃しないように)

 

    • your_postgresql_username

 

    your_postgresql_password
logging.level.org.springframework.jdbc.core=DEBUG

spring.datasource.url=jdbc:postgresql://<your_postgresql_name>.postgres.database.azure.com:5432/<dbname>?sslmode=require
spring.datasource.username=<your_postgresql_username>
spring.datasource.password=<your_postgresql_password>
spring.datasource.driver-class-name=org.postgresql.Driver

实现DB访问

在Microsoft官方页面上,提到了GitHub的URL作为示例实现的公开位置。可以直接使用该GitHub上的实现。

以下的三个文件是在 src/main/java/com/example/demo 中创建的。

image.png
package com.example.demo;

import org.springframework.data.annotation.Id;

public class Todo {

    public Todo() {
    }

    public Todo(String description, String details, boolean done) {
        this.description = description;
        this.details = details;
        this.done = done;
    }

    @Id
    private Long id;

    private String description;

    private String details;

    private boolean done;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getDetails() {
        return details;
    }

    public void setDetails(String details) {
        this.details = details;
    }

    public boolean isDone() {
        return done;
    }

    public void setDone(boolean done) {
        this.done = done;
    }
}
package com.example.demo;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/")
public class TodoController {

    private final TodoRepository todoRepository;

    public TodoController(TodoRepository todoRepository) {
        this.todoRepository = todoRepository;
    }

    @PostMapping("/")
    @ResponseStatus(HttpStatus.CREATED)
    public Todo createTodo(@RequestBody Todo todo) {
        return todoRepository.save(todo);
    }

    @GetMapping("/")
    public Iterable<Todo> getTodos() {
        return todoRepository.findAll();
    }
}
package com.example.demo;

import org.springframework.data.repository.CrudRepository;

public interface TodoRepository extends CrudRepository<Todo, Long> {
}

确认

下载dependency。

./mvnw clean install

执行Spring Boot应用程序。

./mvnw spring-boot:run

当访问 http://localhost:8080 时,将显示以下结果,确认已成功连接到 Azure Database for PostgreSQL。

[
    {
        "id": 0,
        "description": "テスト",
        "details": "詳細",
        "done": false
    }
]

使用 Azure Active Directory 身份验证从 Spring Boot 访问

在Microsoft官方页面上,提到了一种被称为”无密码(推荐)”的方法。通过使用Azure AD,无需写入任何密码,从而降低了泄漏事故的发生可能性。

创建普通用户

即使不使用密码,也可以像密码验证一样,在Microsoft官方页面上创建普通用户而开始操作。但这个步骤不是必需的。首先,在创建Azure Database for PostgreSQL时,您应该已经将自己指定为Azure AD管理员(如上所述的步骤)。一旦确认了自己作为管理员能够登录,就可以将其他Azure AD用户设置为普通用户,并确保他们能够登录。

创建普通用户的方法在Microsoft官方页面中有说明,但使用这种方法可能会出现问题(我没有成功)。在下面的步骤中,我将介绍逐步创建的方法,请需要的人参考那里。

授予Azure AD用户(自己)权限。

在创建Azure Database for PostgreSQL时指定的Azure AD管理员用户,由于默认情况下没有访问数据库表的权限,因此需要授予其权限。在授予权限时,请使用PostgreSQL身份验证(使用用户名和密码),并使用pgAdmin进行登录。

登录后,打开登录/群组角色。然后,您应该能够看到与Azure AD用户主体名相同的用户已被创建。请复制该用户名。

image.png

打开查询工具,运行以下SQL语句来授予权限。请将DBName和YOUR_USER_PRINCIPAL_NAME更改为您自己的。(删除尖括号的前后内容)

GRANT ALL PRIVILEGES ON DATABASE <DBName> TO "<YOUR_USER_PRINCIPAL_NAME>";
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "<YOUR_USER_PRINCIPAL_NAME>";

制作Spring Boot应用程序

使用Spring Initializr创建的方法与在上面提到的用密码进行身份验证的方法相同。

在application.properties文件中写入配置

打开 application.properties 文件,并进行以下设置。请根据您的环境修改三个位置。当然,删除 <> 前后的内容。

    • your_postgresql_name

 

    • dbname(urlの後ろに付いているのでお見逃しないように)

 

    your_postgresql_username
logging.level.org.springframework.jdbc.core=DEBUG

spring.datasource.url=jdbc:postgresql://<your_postgresql_name>.postgres.database.azure.com:5432/<dbname>?sslmode=require
spring.datasource.username=<your_postgresql_username>
spring.datasource.azure.passwordless-enabled=true

添加用于 Azure AD 认证的依赖

请注意,您需要向pom.xml文件中添加两个内容,但是请注意它们需要添加到不同的位置。

spring-cloud-azure-starter-jdbc-postgresql
应当放置在 Project/dependencies 的子目录下。

<dependency>
	<groupId>com.azure.spring</groupId>
	<artifactId>spring-cloud-azure-starter-jdbc-postgresql</artifactId>
</dependency>

第二个 spring-cloud-azure-dependencies 应该直接在项目下列出。版本号是5.5.0,但应该设置为最新版本。

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>com.azure.spring</groupId>
			<artifactId>spring-cloud-azure-dependencies</artifactId>
			<version>5.5.0</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

为了谨慎起见,我会附上已添加的 pom.xml 文件。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.1.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
		<spring-cloud-azure.version>5.5.0</spring-cloud-azure.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jdbc</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.azure.spring</groupId>
			<artifactId>spring-cloud-azure-starter-jdbc-postgresql</artifactId>
		</dependency>

	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>com.azure.spring</groupId>
				<artifactId>spring-cloud-azure-dependencies</artifactId>
				<version>5.5.0</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

确认

请使用密码进行认证时,确认实施与认证一致。请实施Todo、TodoController和TodoRepository。

接下来,您需要在执行环境中使用Azure CLI进行登录。请确保使用创建Azure Database for PostgreSQL时指定的帐户登录。

az login

只需使用已登录状态来执行Spring Boot应用程序,它将自动获取登录的令牌信息并进行登录,请尝试一下。这是因为Spring Cloud Azure使用了DefaultAzureCredential来进行登录。

DefaultAzureCredential 通过按优先顺序获取运行时的登录凭据。首先检索环境变量,然后是托管标识,接着是 IntelliJ、VSCode,最后是 Azure CLI 的登录凭据。因此,如果使用此源代码部署到 Azure,则只需将登录所需的凭据作为环境变量设置在 Azure 侧,系统会自动使用它们进行登录(建议使用托管标识或服务连接器)。

请查看 Microsoft 官方页面了解有关 DefaultAzureCredential 的详细信息。

(PostgreSQL附加说明) 创建普通用户的认证方式

image.png

在打开的查询工具中输入以下SQL。在此,我们使用名为user1的用户,并为testdb数据库及其内部的表赋予了全部权限。

CREATE USER "user1" WITH LOGIN PASSWORD 'YOUR_PASSWORD';
GRANT ALL PRIVILEGES ON DATABASE testdb TO "user1";
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "user1";

请注意,由于Microsoft官方页面未提供表格授权功能,所以无法实现此操作。

创建Azure AD通用用户的方式(附赠)

查看Microsoft官方页面,里面说如果用Azure CLI输入下面的命令就可以了。然而,这种方法往往行不通。如果这个命令能够运行,那么就不需要参考以下步骤了。在我的情况下,它没有正常工作。

az connection create postgres-flexible \
       --resource-group <your_resource_group_name> \
       --connection postgres_conn \
       --target-resource-group <your_resource_group_name> \
       --server postgresqlflexibletest \
       --database demo \
       --user-account \
       --query authInfo.userName \
       --output tsv

首先,您需要使用在创建Azure Database for PostgreSQL时指定的Azure AD管理员身份连接到Azure Database for PostgreSQL。如果不使用该用户进行连接,将无法创建Azure AD认证的普通用户。接下来,我们将介绍如何使用该用户连接到Azure Database for PostgreSQL。

使用pgAdmin进行连接。

获取令牌

当连接到Azure数据库以使用Azure AD时,您需要获取用于连接到Azure Database for PostgreSQL的访问令牌,相当于密码。您可以使用以下命令获取该令牌。

az account get-access-token --resource-type oss-rdbms

在显示的Json中,accessToken键的值是Token。我会将其记录在某个地方。

获取用户主体名称

用户主体名称是在Azure AD中唯一识别用户的内容。请务必确认它不同于登录ID。上述介绍了如何从pgAdmin连接打开用户/组角色并显示用户列表以获取用户主体名称的方法,还有另一种方法。在Azure门户中打开Azure AD,并选择左侧菜单中的”用户”。搜索并找到您在创建Azure Database for PostgreSQL时指定的用户,并点击它,就可以看到用户主体名称的记录。

image.png

使用 pgAdmin 进行连接

创建新的服务器连接信息。在此过程中,将“立即连接”设置为关闭。

image.png

设置用户名时,请使用 Azure AD 管理员的用户主体名称。请务必通过上述方法参考 Azure AD 并进行确认。由于用户主体名称不一定与登录时的电子邮件地址相同,请注意。

image.png

保存后,连接时会弹出密码输入提示。只需输入之前获取到的令牌(Token),即可完成连接。

创建一个普通用户

创建一个针对 Azure Database for PostgreSQL 中 Azure AD 认证的常规用户,是指下列工作:

    1. 在 Azure AD 中创建一个常规用户或使用现有用户

 

    在 PostgreSQL 中创建一个与常规用户的用户主体名称相同的用户,并将 Azure AD 的常规用户和 PostgreSQL 的用户关联起来

这个任务并不是很复杂,但刚开始时可能会有些难理解。我们一步一步来做吧。

在 Azure AD 上创建用于测试的用户

如果您将现有用户注册为常规用户,则无需执行此操作。如果您想在 Azure AD 中创建常规用户进行测试,则需要具备创建权限。如果有创建权限,请打开 Azure 门户并创建用户。如果没有创建权限,则可以使用服务主体替代。

可以通过Azure AD的”应用程序注册”来创建服务主体,但这样比较麻烦,因此我们将使用Azure CLI来创建。通过在这里创建的服务主体,可以在Azure AD的”应用程序注册”中打开并点击”拥有的应用程序”来查看。如果不再需要,从这里删除将会很容易。

image.png

使用以下命令创建服务主体。您可以自由选择通过–name选项来命名。由于服务主体需要访问Azure Database for PostgreSQL,因此在创建时我已经授予了Contributor角色。在这个例子中,这是在订阅级别上进行的,所以范围相当大。当然,从安全角度考虑,我们应该进一步缩小范围并授予比Contributor角色更小的权限,这是当然的。

请确保即使是现有的 Azure AD 用户也能够在进行 Azure AD 认证的情况下访问 Azure Database for PostgreSQL,并拥有相应的访问权限。

az ad sp create-for-rbac \
--name postgresql-test \
--role Contributor \
--scopes /subscriptions/00000000-0000-0000-0000-000000000000 

执行该命令后,您将获得appId和password的返回结果,请记下来。

创建一个在PostgreSQL中拥有与Azure AD用户关联的通常与用户主体名称相同的用户。

只需要一個選項,以下是用中文對上述內容進行改述:

在使用 Azure AD 管理者帳戶連接到 pgAdmin 的情況下,只需執行一次 SQL 查詢即可完成此操作。打開查詢工具並對 postgres 數據庫發送以下 SQL 代碼。你可能會認為它應該是創建語句而不是選擇語句,但實際上這是正確的。

第一个参数需要设置Azure AD用户主体名称。如果创建了服务主体,则为指定的服务主体名称。

select * from pgaadauth_create_principal('postgresql-test', false, false);

当发出SQL查询时,将确认Azure AD中是否存在相应的用户(服务主体),并获取租户ID等信息并存储在内部。

等待查询正常完成后,请确认它已正确创建。然后执行下一个查询。

select * from pgaadauth_list_principals(false);
image.png

再之后,也请您作为用户重新加载用户/组角色。

image.png

如果不再需要Azure AD认证,只需删除在此PostgreSQL内部创建的用户即可。

如果不再需要Azure AD认证,只要删除在这个PostgreSQL内部创建的用户就可以了。

顺便说一句,为什么要使用SELECT语句来创建用户呢?我认为如果仔细看FROM子句,会发现字符串pgaadauth_create_principal和create。看起来这是一个用户定义函数,并且使用SELECT语句调用该函数。所以,尽管是SELECT语句,它却变成了一个(微妙的)奇怪的SQL语句,用于创建用户。

确认

确认已创建的服务主体或现有的 Azure AD 用户是否可以使用 Azure AD 身份验证登录到 pgAdmin。

服务主体登录时,与Azure AD管理员登录时一样,需要获取令牌。对于服务主体,可以按照以下步骤获取令牌。

az logout
az login --service-principal \
-u "<appId/ClientId/ObjectId>" \
-p "<password>" \
-t "<tenantId>"

如果指定-u选项,则应该返回创作时返回的服务主体的appId(也称为ClientID),如果是Azure AD用户,则为Object ID。这有点复杂。请将Azure AD的租户ID设置为tenantId。

如果服务主体无法登录,则可能是因为未授予权限。请手动授予权限。

当成功登录后,使用以下命令获取令牌。

az account get-access-token \
--resource-type oss-rdbms \
--output tsv \
--query accessToken

只要获取了令牌,其余都很简单。在创建pgAdmin新的服务器连接时,将”立即连接”选项关闭,并设置用户名为用户主体名称或服务主体名称,保存并连接后,将弹出密码对话框,然后粘贴令牌即可登录。

附加信息:使用psql通过Azure AD认证登录。

我没有在Windows上尝试过,但在Mac上,按照Microsoft官方提供的方法无法登录。

如果已经设置了由az login登录的用户可以成为Azure AD认证用户,则可以使用以下命令登录:

PGPASSWORD=`az account get-access-token --resource-type oss-rdbms --output tsv --query accessToken` \
psql --host=<your_postgresql_servername>.postgres.database.azure.com \
--user=<your_user_principal_name> dbname=<dbname> \
--port=5432

看起来很复杂,但是下一个部分也可以通过获取到的令牌来替换并且能够正常工作。

az 账户 获取访问令牌 –资源类型 oss-rdbms –输出 tsv –查询访问令牌

总之,只需要在 PGPASSWORD= 后面写下 Token,并连续写下 psql 的登录选项即可。

广告
将在 10 秒后关闭
bannerAds