KeyCloak Java 服务帐户创建用户

问题描述 投票:0回答:1

我是 Keycloak 的新手,并试图了解如何使用 Keycloack 管理客户端

正确注册用户

文档没有具体的示例,并且缺少大量屏幕截图,从而导致了歧义。

我发现了两个基于 JAVA 的示例,它们提供了有关 API 调用如何工作的一些见解:herehere 但是 我不断遇到 jakarta.ws.rs.NotFoundException:每次我尝试创建用户或查看领域表示时,都找不到 HTTP 404。 关于为什么会发生这种情况有任何帮助吗?

我创建了一个客户端凭据客户端并分配了以下角色以便能够管理用户(创建、删除、修改)。

enter image description here

我能够使用 POSTMAN 中的客户端凭据流从令牌端点接收令牌 enter image description here

下面您将找到 Java 调用

private Keycloak getAdminKeycloak() {
        this.base = environment.getProperty(MyConstants.KEYCLOAK_TOKEN_END_POINT);
        this.realm = environment.getProperty(MyConstants.KEYCLOAK_REALM);
        this.username = environment.getProperty(MyConstants.KEYCLOAK_SERVER_API_CLIENTID);
        this.password = environment.getProperty(MyConstants.KEYCLOAK_SERVICE_API_SECRET);
        return KeycloakBuilder.builder().serverUrl(base)
                .realm(realm)
                .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
                .clientId(username)
                .clientSecret(password).build();
    }

    @Override
    public ResponseEntity<String> registerUser(User user) {
        
        Keycloak keycloak = getAdminKeycloak();
        // set user representation
        UserRepresentation newuser = new UserRepresentation();
        newuser.setEmail(user.getEmail());
        newuser.setFirstName(user.getFirstName());
        newuser.setLastName(user.getLastName());
        newuser.setEnabled(true);
        // Get realm
        RealmResource realmResource = keycloak.realm(realm);
        System.out.print(realmResource.toRepresentation().toString());
        UsersResource usersResource = realmResource.users();
        ArrayList<UserRepresentation> users = (ArrayList<UserRepresentation>) usersResource.list();
        // create user
        Response response = usersResource.create(newuser);
        String userID = CreatedResponseUtil.getCreatedId(response);
        if (userID == null || userID.isEmpty()) {
            return new ResponseEntity<>(MyConstants.ERROR_OCCURED, HttpStatus.METHOD_FAILURE);
        }
        // set credentials
        CredentialRepresentation passwordCred = new CredentialRepresentation();
        passwordCred.setTemporary(false);
        passwordCred.setType(CredentialRepresentation.PASSWORD);
        passwordCred.setValue(user.getPassword());
        UserResource userResource = usersResource.get(userID);
        userResource.resetPassword(passwordCred);

        return new ResponseEntity<>(MyConstants.REGISTRATION_COMPLETE, HttpStatus.OK);
    }
security oauth-2.0 keycloak keycloak-rest-api
1个回答
0
投票

要求

Maven 3.9.9 和 Java 17

enter image description here 有关 keycloak docker 启动的更多详细信息,请参见 here

Keycloak 使用 docker compose 和版本 23.0.7

docker-compose.yml

version: '3.6'

services:
  keycloak_web:
    image: quay.io/keycloak/keycloak:23.0.7
    container_name: keycloak_web
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://keycloakdb:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: password

      KC_HOSTNAME: localhost
      KC_HOSTNAME_PORT: 8080
      KC_HOSTNAME_STRICT: false
      KC_HOSTNAME_STRICT_HTTPS: false

      KC_LOG_LEVEL: info
      KC_METRICS_ENABLED: true
      KC_HEALTH_ENABLED: true
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
    command: start-dev
    depends_on:
      - keycloakdb
    ports:
      - 8080:8080

  keycloakdb:
    image: postgres:15
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password

volumes:
  postgres_data:

enter image description here

设置客户端 ID/密码和管理员用户角色

enter image description here

enter image description here

enter image description here

更多详细信息在这里

获得了主令牌

通过curl测试客户端令牌

curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=my-client" \
  -d "client_secret=hzkBBj8MY8hDaqHblrSkKbaDQvUnhbyb" \
  -d "grant_type=client_credentials"

enter image description here

Java代码

文件树

C:\Users\benchvue\temp\6>tree /F
Folder PATH listing
Volume serial number is 8837-26D8
C:.
│   docker-compose.yml
│   pom.xml
│
└───src
    └───main
        ├───java
        │   └───com
        │       └───example
        │               KeycloakUserRegistrationApplication.java
        │               KeycloakUserService.java
        │               User.java
        │
        └───resources
                application.properties

主要代码

KeycloakUserRegistrationApplication.java

package com.example;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;

@SpringBootApplication
public class KeycloakUserRegistrationApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(KeycloakUserRegistrationApplication.class)
                .properties("server.port=8081")
                .run(args);
    }

    @Bean
    public CommandLineRunner run(KeycloakUserService keycloakUserService) {
        return args -> {
            User newUser = new User();
            newUser.setUsername("testuser");  // Setting the required username
            newUser.setEmail("[email protected]");
            newUser.setFirstName("John");
            newUser.setLastName("Kim");
            newUser.setPassword("password123");  // Example password, adjust as needed

            keycloakUserService.registerUser(newUser);
        };
    }
}

KeycloakUserService.java

package com.example;

import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.ws.rs.core.Response;

@Service
public class KeycloakUserService {

    @Autowired
    private Environment environment;

    private Keycloak getAdminKeycloak() {
        String serverUrl = environment.getProperty("keycloak.server-url");
        String masterRealm = environment.getProperty("keycloak.master-realm");
        String clientId = environment.getProperty("keycloak.master-client-id");
        String clientSecret = environment.getProperty("keycloak.master-client-secret");
        
        System.out.println("Server URL: " + serverUrl);
        System.out.println("Master Realm: " + masterRealm);
        System.out.println("Client ID: " + clientId);
        System.out.println("Client Secret: " + clientSecret);

        // Using the password grant type for admin client
        return KeycloakBuilder.builder()
                .serverUrl(serverUrl)
                .realm(masterRealm)
                .grantType(OAuth2Constants.PASSWORD)
                .clientId("admin-cli")
                .username("admin")  // Ensure this matches the username in Keycloak
                .password("admin")  // Ensure this matches the password in Keycloak
                .build();
    }

    private Keycloak getClientKeycloak() {
        String serverUrl = environment.getProperty("keycloak.server-url");
        String targetRealm = environment.getProperty("keycloak.target-realm");
        String clientId = environment.getProperty("keycloak.client-id");
        String clientSecret = environment.getProperty("keycloak.client-secret");

        System.out.println("Target Realm Keycloak Config:");
        System.out.println("serverUrl: " + serverUrl);
        System.out.println("targetRealm: " + targetRealm);
        System.out.println("clientId: " + clientId);
        System.out.println("clientSecret: " + clientSecret);

        // Using client_credentials for non-admin clients
        Keycloak keycloak = KeycloakBuilder.builder()
                .serverUrl(serverUrl)
                .realm(targetRealm)
                .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
                .clientId(clientId)
                .clientSecret(clientSecret)
                .build();

        try {
            AccessTokenResponse tokenResponse = keycloak.tokenManager().getAccessToken();
            System.out.println("Access Token for client: " + tokenResponse.getToken());
        } catch (Exception e) {
            System.out.println("Failed to retrieve client access token: " + e.getMessage());
        }

        return keycloak;
    }

    public ResponseEntity<String> registerUser(User user) {
        Keycloak keycloak = getAdminKeycloak();  // Using admin Keycloak instance
        String targetRealm = environment.getProperty("keycloak.target-realm");
    
        System.out.println("Using target realm: " + targetRealm);
        System.out.println("Creating user with email: " + user.getEmail());
    
        UserRepresentation newUser = new UserRepresentation();
        newUser.setUsername(user.getUsername());  // Ensure username is set here
        newUser.setEmail(user.getEmail());
        newUser.setFirstName(user.getFirstName());
        newUser.setLastName(user.getLastName());
        newUser.setEnabled(true);
    
        RealmResource realmResource = keycloak.realm(targetRealm);
        UsersResource usersResource = realmResource.users();
    
        Response response = usersResource.create(newUser);
        int status = response.getStatus();
        System.out.println("Response status: " + status);
    
        if (status == 201) {
            return new ResponseEntity<>("User registration completed successfully.", HttpStatus.CREATED);
        } else {
            String errorResponse = response.readEntity(String.class);
            System.out.println("Response: " + errorResponse);
            return new ResponseEntity<>("An error occurred while creating the user: " + errorResponse, HttpStatus.BAD_REQUEST);
        }
    }
    
}

应用程序.属性

# Keycloak server URL

keycloak.server-url=http://localhost:8080
keycloak.master-realm=master
keycloak.master-client-id=admin
keycloak.master-client-secret=admin
keycloak.target-realm=my-realm
keycloak.client-id=my-client
keycloak.client-secret=hzkBBj8MY8hDaqHblrSkKbaDQvUnhbyb


# Messages for registration status
registration.complete=User registration completed successfully.
error.occurred=An error occurred while creating the user.

用户.java

package com.example;

public class User {
    private String username;
    private String email;
    private String password;
    private String firstName;
    private String lastName;

    // Getter and setter for username
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    // Getter and setter for email
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    // Getter and setter for password
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    // Getter and setter for firstName
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    // Getter and setter for lastName
    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

pom.xml

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>keycloak-user-registration</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <java.version>17</java.version>
        <spring-boot.version>3.1.0</spring-boot.version>
        <keycloak.version>23.0.7</keycloak.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>

        <!-- Spring Boot Starter Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>

        <!-- Keycloak Admin Client (downgraded to 18.0.0) -->
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-admin-client</artifactId>
            <version>18.0.0</version>
        </dependency>

        <!-- Javax JAX-RS API (for javax.ws.rs.core) -->
        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>2.1.1</version>
        </dependency>

        <!-- Logback for logging compatibility -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.7</version>
        </dependency>

        <!-- Lombok (Optional for Getter/Setter) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>

        <!-- Testing Dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>${spring-boot.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.14.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.14.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.14.3</version>
        </dependency>

    </dependencies>




    <build>
        <plugins>
            <!-- Spring Boot Maven Plugin -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.example.KeycloakUserRegistrationApplication</mainClass>
                </configuration>
            </plugin>

            <!-- Compiler Plugin for Java 17 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

编译并运行

mvn clena compile
mvn clean package
java -jar target/keycloak-user-registration-1.0-SNAPSHOT.jar

enter image description here

enter image description here

enter image description here

结果 - 用户创建

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.