Java数据库应用原型

这是一个Java 数据库应用原型,使用 Spring Boot 和容器进行测试、Keycloak 提供安全、PostgreSQL 提供数据持久化的,带有 REST 和安全功能。

在工作中开发时,我多次需要一个简单应用的模板,以便基于此模板开始为手头的项目添加特定代码。

在本文中,我将创建一个简单的 Java 应用程序,它连接到数据库,暴露一些 REST 端点,并使用基于角色的访问来保护这些端点。

目的是拥有一个最小化且功能齐全的应用程序,然后可以针对特定任务进行定制。

对于数据库,我们将使用 PostgreSQL;对于安全,我们将使用 Keycloak,两者都通过容器部署。在开发过程中,我使用 podman 来测试容器是否正确创建(作为 docker 的替代品------它们在大多数情况下可以互换)作为一次学习体验。

应用程序本身是使用 Spring Boot 框架开发的,并使用 Flyway 进行数据库版本管理。

所有这些技术都是 Java EE 领域业界标准,在项目中被使用的可能性很高。

我们构建原型的核心需求是一个图书馆应用程序,它暴露 REST 端点,允许创建作者、书籍以及它们之间的关系。这将使我们能够实现一个多对多关系,然后可以将其扩展用于任何可以想象的目的。

完整可用的应用程序可以在 github.com/ghalldev/db... 找到。

本文中的代码片段取自该代码库

在创建容器之前,请确保使用您偏好的值定义以下环境变量(教程中故意省略了它们,以避免传播多个用户使用的默认值):

bash 复制代码
DOCKER_POSTGRES_PASSWORD

DOCKER_KEYCLOAK_ADMIN_PASSWORD

DOCKER_GH_USER1_PASSWORD

配置 PostgreSQL:

bash 复制代码
docker container create --name gh_postgres --env POSTGRES_PASSWORD=$DOCKER_POSTGRES_PASSWORD --env POSTGRES_USER=gh_pguser --env POSTGRES_INITDB_ARGS=--auth=scram-sha-256 --publish 5432:5432 postgres:17.5-alpine3.22

docker container start gh_postgres

配置 Keycloak:

首先是容器的创建并启动:

bash 复制代码
docker container create --name gh_keycloak --env DOCKER_GH_USER1_PASSWORD=$DOCKER_GH_USER1_PASSWORD --env KC_BOOTSTRAP_ADMIN_USERNAME=gh_admin --env KC_BOOTSTRAP_ADMIN_PASSWORD=$DOCKER_KEYCLOAK_ADMIN_PASSWORD --publish 8080:8080 --publish 8443:8443 --publish 9000:9000 keycloak/keycloak:26.3 start-dev

docker container start gh_keycloak

在容器启动并运行后,我们可以继续创建领域、用户和角色(这些命令必须在正在运行的容器内部执行):

bash 复制代码
cd $HOME/bin

./kcadm.sh config credentials --server http://localhost:8080 --realm master --user gh_admin --password $KC_BOOTSTRAP_ADMIN_PASSWORD

./kcadm.sh create realms -s realm=gh_realm -s enabled=true

./kcadm.sh create users -s username=gh_user1 -s email="gh_user1@email.com" -s firstName="gh_user1firstName" -s lastName="gh_user1lastName" -s emailVerified=true -s enabled=true -r gh_realm

./kcadm.sh set-password -r gh_realm --username gh_user1 --new-password $DOCKER_GH_USER1_PASSWORD

./kcadm.sh create roles -r gh_realm -s name=viewer -s 'description=Realm role to be used for read-only features'

./kcadm.sh add-roles --uusername gh_user1 --rolename viewer -r gh_realm

./kcadm.sh create roles -r gh_realm -s name=creator -s 'description=Realm role to be used for create/update features'

./kcadm.sh add-roles --uusername gh_user1 --rolename creator -r gh_realm

ID_ACCOUNT_CONSOLE=$(./kcadm.sh get clients -r gh_realm --fields id,clientId | grep -B 1 '"clientId" : "account-console"' | grep -oP '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}')

./kcadm.sh update clients/$ID_ACCOUNT_CONSOLE -r gh_realm -s 'fullScopeAllowed=true' -s 'directAccessGrantsEnabled=true'

用户 gh_user1 在领域 gh_realm 中被创建,并拥有 viewercreator 角色。

您可能已经注意到,我们没有创建新的客户端,而是使用了 Keycloak 自带的一个默认客户端:account-console。这是为了方便起见,在实际场景中,您会创建一个特定的客户端,然后将其更新为具有 fullScopeAllowed(这会导致领域角色被添加到令牌中------默认情况下不添加)和 directAccessGrantsEnabled(允许通过 Keycloak 的 openid-connect/token 端点生成令牌,在我们的例子中使用 curl)。

创建的角色随后可以在 Java 应用程序内部使用,以根据我们约定的契约来限制对某些功能的访问------viewer 只能访问只读操作,而 creator 可以执行创建、更新和删除操作。当然,同样地,可以根据任何原因创建各种角色,只要约定的契约被明确定义并被所有人理解。

角色还可以进一步添加到组中,但本教程不包含这部分内容。

但是,在实际使用这些角色之前,我们必须告诉 Java 应用程序如何提取角色------这是必须的,因为 Keycloak 将角色添加到 JWT 的方式是其特有的,所以我们必须编写一段自定义代码,将其转换为 Spring Security 可以使用的东西:

java 复制代码
@Bean

public JwtAuthenticationConverter jwtAuthenticationConverter() {

// 遵循与 org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter 相同的模式

Converter<Jwt, Collection<GrantedAuthority>> keycloakRolesConverter = new Converter<>() {

private static final String DEFAULT_AUTHORITY_PREFIX = "ROLE_";

//https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java#L901

private static final String KEYCLOAK_REALM_ACCESS_CLAIM_NAME = "realm_access";

private static final String KEYCLOAK_REALM_ACCESS_ROLES = "roles";

  


@Override

public Collection<GrantedAuthority> convert(Jwt source) {

Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();

Map<String, List<String>> realmAccess = source.getClaim(KEYCLOAK_REALM_ACCESS_CLAIM_NAME);

if (realmAccess == null) {

logger.warn("No " + KEYCLOAK_REALM_ACCESS_CLAIM_NAME + " present in the JWT");

return grantedAuthorities;

}

List<String> roles = realmAccess.get(KEYCLOAK_REALM_ACCESS_ROLES);

if (roles == null) {

logger.warn("No " + KEYCLOAK_REALM_ACCESS_ROLES + " present in the JWT");

return grantedAuthorities;

}

roles.forEach(

role -> grantedAuthorities.add(new SimpleGrantedAuthority(DEFAULT_AUTHORITY_PREFIX + role)));

  


return grantedAuthorities;

}

  


};

JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();

jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(keycloakRolesConverter);

  


return jwtAuthenticationConverter;

}

AppConfiguration 类中还完成了其他重要配置,例如启用方法安全性和禁用 CSRF。

现在我们可以在 REST 控制器中使用 @org.springframework.security.access.prepost.PreAuthorize 注解来限制访问:

java 复制代码
@PostMapping("/author")

@PreAuthorize("hasRole('creator')")

public void addAuthor(@RequestParam String name, @RequestParam String address) {

authorService.add(new AuthorDto(name, address));

}

  


@GetMapping("/author")

@PreAuthorize("hasRole('viewer')")

public String getAuthors() {

return authorService.allInfo();

}

通过这种方式,只有成功通过身份验证且拥有 hasRole 中列出的角色的用户才能调用端点,否则他们将收到 HTTP 403 Forbidden 错误。

在容器启动并配置完成后,Java 应用程序可以启动了,但在启动之前需要添加数据库密码------这可以通过环境变量完成(下面是一个 Linux shell 示例):

bash 复制代码
export SPRING_DATASOURCE_PASSWORD=$DOCKER_POSTGRES_PASSWORD

现在,如果一切正常启动并运行,我们可以使用 curl 来测试我们的应用程序(以下所有命令均为 Linux shell 命令)。

使用之前创建的用户 gh_user1 登录并提取身份验证令牌:

bash 复制代码
KEYCLOAK_ACCESS_TOKEN=$(curl -d 'client_id=account-console' -d 'username=gh_user1' -d "password=$DOCKER_GH_USER1_PASSWORD" -d 'grant_type=password' 'http://localhost:8080/realms/gh_realm/protocol/openid-connect/token' | grep -oP '"access_token":"\K[^"]*')

创建一个新作者(这将测试 creator 角色是否有效):

bash 复制代码
curl -X POST --data-raw 'name="GH_name1"&address="GH_address1"' -H "Authorization: Bearer $KEYCLOAK_ACCESS_TOKEN" 'localhost:8090/library/author'

检索库中的所有作者(这将测试 viewer 角色是否有效):

bash 复制代码
curl -X GET -H "Authorization: Bearer $KEYCLOAK_ACCESS_TOKEN" 'localhost:8090/library/author'

至此,您应该拥有了创建自己的 Java 应用程序所需的一切,可以根据需要对其进行扩展和配置。


【注】本文译自:Java Spring Boot Template With PostgreSQL, Keycloak Securit

相关推荐
侠客行03179 小时前
Mybatis连接池实现及池化模式
java·mybatis·源码阅读
蛇皮划水怪9 小时前
深入浅出LangChain4J
java·langchain·llm
老毛肚10 小时前
MyBatis体系结构与工作原理 上篇
java·mybatis
风流倜傥唐伯虎11 小时前
Spring Boot Jar包生产级启停脚本
java·运维·spring boot
Yvonne爱编码11 小时前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
Re.不晚11 小时前
JAVA进阶之路——无奖问答挑战1
java·开发语言
你这个代码我看不懂11 小时前
@ConditionalOnProperty不直接使用松绑定规则
java·开发语言
fuquxiaoguang11 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析
琹箐11 小时前
最大堆和最小堆 实现思路
java·开发语言·算法
__WanG12 小时前
JavaTuples 库分析
java