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
相关产品推荐
相关产品推荐

