如何在非响应式Spring Boot中用RestTemplate+OBO实现AAD微服务调用
基于RestTemplate的Azure AD On-Behalf-Of(OBO)流程实现方案
1. 添加项目依赖
确保Maven或Gradle中引入必要的依赖,用于Azure AD身份验证及RestTemplate调用:
<!-- Spring Security OAuth2 核心依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> <!-- MSAL4J 用于处理Azure AD OBO流程 --> <dependency> <groupId>com.microsoft.azure</groupId> <artifactId>msal4j</artifactId> <version>1.23.0</version> </dependency>
2. 配置Azure AD参数
在application.yml中配置微服务A的Azure AD应用信息及目标服务B的权限范围:
spring: security: oauth2: client: registration: azure-ad-obo: client-id: ${MICRO_A_CLIENT_ID} # 微服务A的Azure AD客户端ID client-secret: ${MICRO_A_CLIENT_SECRET} # 微服务A的客户端密钥 authorization-grant-type: authorization_code redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" scope: openid, profile, api://{MICRO_B_CLIENT_ID}/.default # 微服务B的API权限范围 provider: azure-ad-obo: issuer-uri: https://login.microsoftonline.com/{AZURE_TENANT_ID}/v2.0 # 替换为实际租户ID
3. 实现OBO令牌获取服务
创建服务类,从当前请求上下文提取前端令牌,通过OBO流程获取访问微服务B的令牌:
import com.microsoft.aad.msal4j.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal; import org.springframework.stereotype.Service; import java.util.Collections; import java.util.concurrent.CompletableFuture; @Service public class OboTokenService { @Value("${spring.security.oauth2.client.registration.azure-ad-obo.client-id}") private String clientId; @Value("${spring.security.oauth2.client.registration.azure-ad-obo.client-secret}") private String clientSecret; @Value("${spring.security.oauth2.client.provider.azure-ad-obo.issuer-uri}") private String authorityUri; @Value("${spring.security.oauth2.client.registration.azure-ad-obo.scope}") private String targetScope; public String getServiceBAccessToken() throws Exception { // 从Spring Security上下文获取前端传入的用户访问令牌 OAuth2AuthenticatedPrincipal principal = (OAuth2AuthenticatedPrincipal) SecurityContextHolder.getContext() .getAuthentication().getPrincipal(); String userAccessToken = principal.getAttribute("access_token"); // 初始化ConfidentialClientApplication ConfidentialClientApplication client = ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.createFromSecret(clientSecret)) .authority(authorityUri) .build(); // 构建OBO请求参数 OnBehalfOfParameters oboParams = OnBehalfOfParameters.builder(Collections.singleton(targetScope), new UserAssertion(userAccessToken)) .build(); // 同步获取令牌(非响应式场景下使用get()阻塞获取结果) CompletableFuture<IAuthenticationResult> future = client.acquireToken(oboParams); IAuthenticationResult result = future.get(); return result.accessToken(); } }
4. 用RestTemplate调用微服务B
创建调用服务,注入RestTemplate和OBO令牌服务,携带令牌发起请求:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @Service public class MicroServiceBClient { @Autowired private RestTemplate restTemplate; @Autowired private OboTokenService oboTokenService; public ResponseEntity<String> invokeServiceB(String serviceBEndpoint) throws Exception { // 获取OBO令牌 String oboAccessToken = oboTokenService.getServiceBAccessToken(); // 构建请求头,携带Bearer令牌 HttpHeaders headers = new HttpHeaders(); headers.setBearerAuth(oboAccessToken); // 使用RestTemplate发起HTTP请求 return restTemplate.exchange( serviceBEndpoint, HttpMethod.GET, null, String.class, headers ); } }
5. 注册RestTemplate Bean
在配置类中声明RestTemplate实例,供Spring容器管理:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
关键注意事项
- 确保微服务A的Azure AD应用已添加并获批对微服务B应用的委派权限(需Azure AD管理员同意)
- 前端传入的令牌必须包含
scp声明,且权限范围与配置的scope匹配 - 可扩展令牌服务添加缓存逻辑,避免重复发起OBO令牌请求,提升性能
内容的提问来源于stack exchange,提问作者Mikita Zhuchkevich
相关产品推荐
相关产品推荐

