You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 3中无需令牌实现Keycloak访客注册的新方法咨询

无令牌访客通过Spring Boot向Keycloak注册用户的替代方案

针对旧org.keycloak依赖被弃用的情况,推荐直接调用Keycloak Admin REST API实现需求,无需依赖废弃包,步骤如下:

一、依赖准备

无需引入旧的org.keycloak依赖,只需Spring Web/Webflux基础依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 若使用非阻塞的WebClient,添加此依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

二、配置Keycloak参数

在application.yml中配置管理员认证信息与Keycloak地址:

keycloak:
  server-url: http://localhost:8080/auth
  realm: your-realm
  client-id: admin-cli
  admin-username: your-admin-username
  admin-password: your-admin-password

注:admin-cli是Keycloak默认管理员客户端,确保其Access Type为public(若设为confidential,需额外配置client-secret并在令牌请求中添加该参数)

三、获取管理员令牌

Keycloak Admin API需要管理员权限的Bearer令牌,通过密码模式获取:

令牌获取工具类(WebClient实现)

import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.util.Map;

@Component
public class KeycloakTokenProvider {

    private final WebClient webClient;
    private final String serverUrl;
    private final String realm;
    private final String clientId;
    private final String adminUsername;
    private final String adminPassword;

    public KeycloakTokenProvider(WebClient.Builder webClientBuilder, org.springframework.core.env.Environment env) {
        this.serverUrl = env.getProperty("keycloak.server-url");
        this.realm = env.getProperty("keycloak.realm");
        this.clientId = env.getProperty("keycloak.client-id");
        this.adminUsername = env.getProperty("keycloak.admin-username");
        this.adminPassword = env.getProperty("keycloak.admin-password");
        this.webClient = webClientBuilder.baseUrl(serverUrl).build();
    }

    public Mono<String> getAdminToken() {
        return webClient.post()
                .uri("/realms/{realm}/protocol/openid-connect/token", realm)
                .body(BodyInserters.fromFormData(Map.of(
                        "grant_type", "password",
                        "client_id", clientId,
                        "username", adminUsername,
                        "password", adminPassword
                )))
                .retrieve()
                .bodyToMono(Map.class)
                .map(res -> (String) res.get("access_token"));
    }
}

四、实现用户注册逻辑

1. 定义注册请求DTO

public class RegisterUserRequest {
    private String username;
    private String email;
    private String password;
    private boolean enabled = true;

    // Getters & Setters
}

2. 用户注册服务类

import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.util.Map;

@Service
public class KeycloakUserService {

    private final WebClient webClient;
    private final KeycloakTokenProvider tokenProvider;
    private final String serverUrl;
    private final String realm;

    public KeycloakUserService(WebClient.Builder webClientBuilder, KeycloakTokenProvider tokenProvider, org.springframework.core.env.Environment env) {
        this.serverUrl = env.getProperty("keycloak.server-url");
        this.realm = env.getProperty("keycloak.realm");
        this.webClient = webClientBuilder.baseUrl(serverUrl).build();
        this.tokenProvider = tokenProvider;
    }

    public Mono<Void> registerUser(RegisterUserRequest request) {
        return tokenProvider.getAdminToken()
                .flatMap(token -> webClient.post()
                        .uri("/admin/realms/{realm}/users", realm)
                        .header("Authorization", "Bearer " + token)
                        .body(BodyInserters.fromValue(Map.of(
                                "username", request.getUsername(),
                                "email", request.getEmail(),
                                "enabled", request.isEnabled(),
                                "credentials", Map.of(
                                        "type", "password",
                                        "value", request.getPassword(),
                                        "temporary", false
                                )
                        )))
                        .retrieve()
                        .bodyToMono(Void.class));
    }
}

3. 开放注册接口(无需认证)

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
public class PublicRegistrationController {

    private final KeycloakUserService userService;

    public PublicRegistrationController(KeycloakUserService userService) {
        this.userService = userService;
    }

    @PostMapping("/public/register")
    public Mono<Void> registerUser(@RequestBody RegisterUserRequest request) {
        return userService.registerUser(request);
    }
}

五、Spring Security放行配置

若应用启用了Spring Security,需确保注册接口无需认证:

WebFlux环境配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http
                .authorizeExchange(exchanges -> exchanges
                        .pathMatchers("/public/register").permitAll()
                        .anyExchange().authenticated()
                )
                .build();
    }
}

Servlet环境配置(若使用RestTemplate)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/register").permitAll()
                .anyRequest().authenticated()
        );
        return http.build();
    }
}

验证

发送POST请求到/public/register,请求体示例:

{
    "username": "new-user",
    "email": "user@example.com",
    "password": "P@ssw0rd123"
}

登录Keycloak控制台,检查对应Realm下是否新增该用户。


内容的提问来源于stack exchange,提问作者JustBeginner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 10:13:18