动态

详情 返回 返回

Java數據庫應用原型 - 动态 详情

這是一個Java 數據庫應用原型,使用 Spring Boot 和容器進行測試、Keycloak 提供安全、PostgreSQL 提供數據持久化的,帶有 REST 和安全功能。

在工作中開發時,我多次需要一個簡單應用的模板,以便基於此模板開始為手頭的項目添加特定代碼。
在本文中,我將創建一個簡單的 Java 應用程序,它連接到數據庫,暴露一些 REST 端點,並使用基於角色的訪問來保護這些端點。
目的是擁有一個最小化且功能齊全的應用程序,然後可以針對特定任務進行定製。
對於數據庫,我們將使用 PostgreSQL;對於安全,我們將使用 Keycloak,兩者都通過容器部署。在開發過程中,我使用 podman 來測試容器是否正確創建(作為 docker 的替代品——它們在大多數情況下可以互換)作為一次學習體驗。
應用程序本身是使用 Spring Boot 框架開發的,並使用 Flyway 進行數據庫版本管理。
所有這些技術都是 Java EE 領域業界標準,在項目中被使用的可能性很高。

我們構建原型的核心需求是一個圖書館應用程序,它暴露 REST 端點,允許創建作者、書籍以及它們之間的關係。這將使我們能夠實現一個多對多關係,然後可以將其擴展用於任何可以想象的目的。
完整可用的應用程序可以在 https://github.com/ghalldev/db_proto 找到。
本文中的代碼片段取自該代碼庫

在創建容器之前,請確保使用您偏好的值定義以下環境變量(教程中故意省略了它們,以避免傳播多個用户使用的默認值):

DOCKER_POSTGRES_PASSWORD
DOCKER_KEYCLOAK_ADMIN_PASSWORD
DOCKER_GH_USER1_PASSWORD

配置 PostgreSQL:

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:
首先是容器的創建並啓動:

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

在容器啓動並運行後,我們可以繼續創建領域、用户和角色(這些命令必須在正在運行的容器內部執行):

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 可以使用的東西:

@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 註解來限制訪問:

@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 示例):

export SPRING_DATASOURCE_PASSWORD=$DOCKER_POSTGRES_PASSWORD

現在,如果一切正常啓動並運行,我們可以使用 curl 來測試我們的應用程序(以下所有命令均為 Linux shell 命令)。

使用之前創建的用户 gh_user1 登錄並提取身份驗證令牌:

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 角色是否有效):

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

檢索庫中的所有作者(這將測試 viewer 角色是否有效):

curl -X GET -H "Authorization: Bearer $KEYCLOAK_ACCESS_TOKEN" 'localhost:8090/library/author'

至此,您應該擁有了創建自己的 Java 應用程序所需的一切,可以根據需要對其進行擴展和配置。


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

user avatar journey_64224c9377fd5 头像 u_16769727 头像 lvlaotou 头像 nianqingyouweidenangua 头像 chenjiabing666 头像 tuhooo 头像 javaedge 头像 daimajiangxin 头像 ruozxby 头像 swifter 头像 beishangdeyadan 头像 enaium 头像
点赞 28 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.