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

Spring Boot后端如何利用Azure AD令牌调用Microsoft Graph?

能否直接用Web App传来的Azure AD令牌调用Microsoft Graph?解决方案详解

一、不能直接使用的原因

  • 令牌受众不匹配:Web App传给你API的令牌,其aud(受众)字段是你的API应用ID URI(比如api://your-api-client-id),而Microsoft Graph要求令牌的aud必须是https://graph.microsoft.com,否则会直接拒绝请求。
  • 权限范围不匹配:该令牌的权限是针对你的API配置的,不会包含调用Microsoft Graph所需的权限(比如User.Read),即使你的Azure AD应用已配置Graph权限,原令牌也不会自动携带这些权限范围。

二、正确解决方案:使用On-Behalf-Of(OBO)流程获取Graph令牌

因为你只能拿到用户的访问令牌,所以需要通过OBO流程代表用户向Azure AD请求专门用于调用Microsoft Graph的访问令牌。以下是Spring Boot+Java的具体实现步骤:

1. 前置Azure AD配置

  • 确保你的后端API应用已在Azure AD注册,并添加Microsoft Graph的Delegated权限(比如User.Read,用于读取用户基本信息),完成管理员同意。
  • 确保Web App注册的应用已被授予调用你API的Delegated权限,API应用默认支持OBO流程,无需额外配置。

2. 项目依赖

在pom.xml中添加Azure AD和Microsoft Graph SDK依赖:

<dependency>
    <groupId>com.azure.spring</groupId>
    <artifactId>azure-spring-boot-starter-active-directory</artifactId>
    <version>4.14.0</version>
</dependency>
<dependency>
    <groupId>com.microsoft.graph</groupId>
    <artifactId>microsoft-graph</artifactId>
    <version>6.0.0</version>
</dependency>

3. 配置文件(application.yml)

填入你的Azure AD租户ID、API应用的客户端ID和密钥:

spring:
  cloud:
    azure:
      active-directory:
        tenant-id: ${AZURE_TENANT_ID}
        client-id: ${AZURE_CLIENT_ID}
        client-secret: ${AZURE_CLIENT_SECRET}
        app-id-uri: ${YOUR_API_APP_ID_URI} # 你的API的受众,例如api://xxxx-xxxx-xxxx-xxxx

4. 代码实现OBO流程并调用Graph

import com.azure.core.credential.AccessToken;
import com.azure.identity.OnBehalfOfCredential;
import com.azure.identity.OnBehalfOfCredentialBuilder;
import com.microsoft.graph.models.User;
import com.microsoft.graph.requests.GraphServiceClient;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;

@RestController
public class UserExtraInfoController {

    private final String tenantId = System.getenv("AZURE_TENANT_ID");
    private final String clientId = System.getenv("AZURE_CLIENT_ID");
    private final String clientSecret = System.getenv("AZURE_CLIENT_SECRET");
    // 定义需要的Graph权限范围
    private final String[] graphScopes = {"https://graph.microsoft.com/User.Read"};

    @GetMapping("/api/user/extra-info")
    public CompletableFuture<User> fetchExtraUserInfo(@RequestHeader("Authorization") String authHeader) {
        // 提取前端传入的访问令牌(去除Bearer前缀)
        String userAccessToken = authHeader.replaceFirst("Bearer ", "");

        // 初始化OBO凭证,用于交换Graph令牌
        OnBehalfOfCredential oboCredential = new OnBehalfOfCredentialBuilder()
                .tenantId(tenantId)
                .clientId(clientId)
                .clientSecret(clientSecret)
                .userAssertion(userAccessToken)
                .build();

        // 请求Graph访问令牌
        return oboCredential.getToken(new com.azure.core.credential.TokenRequestContext().addScopes(graphScopes))
                .thenApply(this::callMicrosoftGraph);
    }

    private User callMicrosoftGraph(AccessToken graphToken) {
        // 配置Graph客户端,注入令牌
        OkHttpClient httpClient = new OkHttpClient.Builder()
                .addInterceptor(chain -> {
                    Request authenticatedRequest = chain.request().newBuilder()
                            .header("Authorization", "Bearer " + graphToken.getToken())
                            .build();
                    return chain.proceed(authenticatedRequest);
                })
                .build();

        GraphServiceClient<okhttp3.Request> graphClient = GraphServiceClient.builder()
                .httpClient(httpClient)
                .buildClient();

        // 调用Graph API获取用户信息(这里以获取指定字段为例)
        return graphClient.me()
                .buildRequest()
                .select("id,displayName,mail,userPrincipalName,department")
                .get();
    }
}

5. 注意事项

  • 确保前端传入的是**访问令牌(Access Token)**而非ID Token,ID Token无法用于OBO流程交换。
  • 可以根据令牌的expiresOn时间缓存Graph令牌,避免重复发起OBO请求,提升性能。
  • 处理令牌过期、权限不足等异常情况,捕获Azure Identity SDK抛出的认证异常,返回合适的HTTP状态码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 11:34:57