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

如何用技术用户身份认证API调用(Spring Security+Keycloak)

解决方案:使用技术用户身份调用API

要实现以技术用户身份发起API请求,核心是通过Spring Security的OAuth2AuthorizedClientManager获取技术用户的访问令牌,并通过请求拦截器将令牌添加到请求头中,与当前登录用户的上下文隔离。

步骤1:修正Keycloak客户端配置

首先检查并修正你的YAML配置,确保技术用户的客户端信息正确(password模式需要区分客户端ID/密钥和技术用户账号/密码):

spring:
  security:
    oauth2:
      client:
        provider:
          keycloak:
            authorization-uri: ${keycloak.base_url}/auth
            token-uri: ${keycloak.base_url}/token
            jwk-set-uri: ${keycloak.base_url}/certs
        registration:
          # 用户登录用的授权码模式客户端
          user:
            provider: keycloak
            client-id : ${keycloak.client.id}
            client-secret: ${keycloak.client.secret}
            authorization-grant-type: authorization_code
            redirectUri: '{baseUrl}/login/oauth2/code/{registrationId}'
            scope:
              - openid
              - roles
              - institution
          # 技术用户用的密码模式客户端
          tech:
            provider: keycloak
            client-id : ${keycloak.tech.client-id}  # Keycloak中创建的客户端ID
            client-secret: ${keycloak.tech.client-secret}  # 客户端的密钥
            authorization-grant-type: password
            scope:  # 按需添加技术用户所需的权限范围
              - openid
              - roles

在配置文件(如application.properties)中补充技术用户的账号密码:

keycloak.tech.username=你的技术用户账号
keycloak.tech.password=你的技术用户密码

步骤2:配置技术用户的OAuth2授权客户端管理器

创建OAuth2AuthorizedClientManager Bean,专门用于获取技术用户的授权令牌,自动处理令牌获取和刷新:

import org.springframework.security.oauth2.client.*;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class TechUserOAuth2Config {

    @Bean
    public OAuth2AuthorizedClientManager techUserAuthorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientService authorizedClientService,
            @Value("${keycloak.tech.username}") String techUsername,
            @Value("${keycloak.tech.password}") String techPassword) {
        
        DefaultOAuth2AuthorizedClientManager manager = 
            new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientService);
        
        // 设置密码模式所需的用户凭证
        manager.setContextAttributesMapper(contextAttributes -> {
            Map<String, Object> attrs = new HashMap<>();
            attrs.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, techUsername);
            attrs.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, techPassword);
            return attrs;
        });
        
        return manager;
    }
}

步骤3:创建技术用户认证拦截器

编写请求拦截器,将技术用户的访问令牌添加到API请求的Authorization头中:

import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Collections;

@Component
public class TechUserAuthInterceptor implements ClientHttpRequestInterceptor {

    private final OAuth2AuthorizedClientManager techUserAuthorizedClientManager;

    public TechUserAuthInterceptor(OAuth2AuthorizedClientManager techUserAuthorizedClientManager) {
        this.techUserAuthorizedClientManager = techUserAuthorizedClientManager;
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        // 构建授权请求,指定技术用户的客户端注册ID为"tech"
        OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
                .withClientRegistrationId("tech")
                .principal(new AnonymousAuthenticationToken("tech", "tech-service", Collections.emptyList()))
                .build();
        
        // 获取授权客户端(自动处理令牌获取/刷新)
        OAuth2AuthorizedClient authorizedClient = techUserAuthorizedClientManager.authorize(authorizeRequest);
        
        if (authorizedClient == null || authorizedClient.getAccessToken() == null) {
            throw new IllegalStateException("无法获取技术用户的访问令牌");
        }

        // 添加Bearer令牌到请求头
        request.getHeaders().setBearerAuth(authorizedClient.getAccessToken().getTokenValue());
        return execution.execute(request, body);
    }
}

步骤4:创建技术用户专用的RestClient

单独创建一个RestClient Bean,绑定上述拦截器,确保与用户登录上下文隔离:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.apache.http.impl.client.HttpClients;

@Configuration
public class TechRestClientConfig {

    @Bean
    public RestClient techUserRestClient(TechUserAuthInterceptor techAuthInterceptor, @Value("${keycloak.api.base_url}") String apiUrl) {
        return RestClient.builder()
                .messageConverters(converters -> {
                    converters.clear();
                    converters.add(new MappingJackson2HttpMessageConverter());
                })
                .defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
                .requestFactory(new HttpComponentsClientHttpRequestFactory(HttpClients.createDefault()))
                .baseUrl(apiUrl)
                .requestInterceptor(techAuthInterceptor) // 绑定技术用户认证拦截器
                .build();
    }
}

步骤5:使用技术用户RestClient调用API

在需要以技术用户身份调用API的服务类中,注入techUserRestClient即可:

import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
import org.springframework.http.MediaType;

@Service
public class TechApiService {

    private final RestClient techUserRestClient;

    public TechApiService(RestClient techUserRestClient) {
        this.techUserRestClient = techUserRestClient;
    }

    public ResultClass callTechApi() {
        return techUserRestClient.get()
                .uri("/somePath")
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .body(ResultClass.class);
    }
}

关键说明

  • 技术用户的认证流程与当前登录用户完全隔离,不会干扰SecurityContext中的用户信息。
  • DefaultOAuth2AuthorizedClientManager会自动处理令牌的过期刷新,无需手动维护令牌生命周期。
  • 确保Keycloak中已为技术用户分配足够的权限,允许调用目标API(包括Keycloak自身服务)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 03:24:52