求助:基于Microsoft Graph API构建多租户Java OAuth2.0客户端
多租户Microsoft Graph Java客户端实现指南(支持全MS身份类型)
一、Azure AD应用注册核心配置(多租户关键)
- 注册应用时,账号类型必须选「任何组织目录中的账户和个人Microsoft账户(例如Skype、Xbox)」,这是让应用兼容工作/学校账户+个人账户的核心前提。
- 配置重定向URI:选「Web」类型,填你的Java应用回调地址(比如
http://localhost:8080/login/oauth2/code/microsoft)。 - 添加委派权限:
- 必须加的:
Calendars.Read(读取日历)、offline_access(获取刷新令牌,维持长期访问)、User.Read(基础用户信息);如果需要修改日历就加Calendars.ReadWrite。 - 注意:部分高权限需要租户管理员同意,普通权限用户自行同意即可。
- 必须加的:
- 生成并保存客户端密钥:在「证书和密码」页面创建客户端密码,务必记录好密码值(只显示一次)。
二、Java项目依赖配置(Maven示例)
直接把以下依赖加到pom.xml里,用最新稳定版即可:
<dependencies> <!-- Microsoft Graph SDK核心 --> <dependency> <groupId>com.microsoft.graph</groupId> <artifactId>microsoft-graph</artifactId> <version>6.3.0</version> </dependency> <!-- Azure身份验证工具 --> <dependency> <groupId>com.azure</groupId> <artifactId>azure-identity</artifactId> <version>1.12.0</version> </dependency> <!-- 如果用Spring Boot,加这个简化OAuth2流程 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> </dependencies>
三、Authorization Code Flow 多租户适配实现
1. Spring Boot OAuth2客户端配置
在application.yml里配置多租户相关端点,核心是用common代替特定租户ID:
spring: security: oauth2: client: registration: microsoft: client-id: 你的应用ID client-secret: 你的客户端密钥 scope: Calendars.Read, offline_access, User.Read authorization-grant-type: authorization_code redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" provider: microsoft: authorization-uri: https://login.microsoftonline.com/common/oauth2/v2.0/authorize token-uri: https://login.microsoftonline.com/common/oauth2/v2.0/token user-info-uri: https://graph.microsoft.com/v1.0/me user-name-attribute: id
common端点是关键,它能接受所有类型的MS身份登录。
2. 构建GraphClient实例
从OAuth2回调中拿到授权码后,用Azure Identity构建凭证,再创建Graph客户端:
import com.microsoft.graph.requests.extensions.GraphServiceClient; import com.azure.identity.AuthorizationCodeCredential; import com.azure.identity.AuthorizationCodeCredentialBuilder; import okhttp3.Request; // 从回调请求中获取授权码 String authCode = 回调请求中的授权码参数; String redirectUri = "http://localhost:8080/login/oauth2/code/microsoft"; // 构建多租户凭证 AuthorizationCodeCredential credential = new AuthorizationCodeCredentialBuilder() .clientId("你的应用ID") .clientSecret("你的客户端密钥") .authorizationCode(authCode) .redirectUrl(redirectUri) .tenantId("common") .build(); // 创建Graph服务客户端 GraphServiceClient<Request> graphClient = GraphServiceClient.builder() .authenticationProvider(request -> { String accessToken = credential.getToken().block().getToken(); request.addHeader("Authorization", "Bearer " + accessToken); return request; }) .buildClient();
四、获取用户日历数据
直接调用Graph API拉取日历事件:
import com.microsoft.graph.models.extensions.Event; import com.microsoft.graph.requests.extensions.IGraphServiceEventsCollectionPage; // 获取当前用户的日历事件,可按需添加筛选、排序 IGraphServiceEventsCollectionPage events = graphClient.me().events() .buildRequest() .select("subject,start,end,location") .get(); // 遍历所有事件(含分页) while (events != null) { for (Event event : events.getCurrentPage()) { System.out.println("事件主题: " + event.subject); System.out.println("开始时间: " + event.start.dateTime); System.out.println("结束时间: " + event.end.dateTime); } events = events.getNextPage() != null ? events.getNextPage().get() : null; }
五、订阅日历新增事件通知
要接收通知,你的回调端点必须是HTTPS(本地测试可以用ngrok暴露HTTPS地址):
1. 创建订阅
import com.microsoft.graph.models.extensions.Subscription; import java.time.OffsetDateTime; Subscription subscription = new Subscription(); subscription.changeType = "created"; // 只监听新增事件 subscription.notificationUrl = "https://你的HTTPS端点/webhook/microsoft"; subscription.resource = "/me/events"; subscription.expirationDateTimeOffset = OffsetDateTime.now().plusDays(3); // 订阅最长3天,到期需续期 subscription.clientState = "随机加密字符串"; // 用于验证通知真实性 Subscription createdSub = graphClient.subscriptions() .buildRequest() .post(subscription); // 保存订阅ID和clientState,后续验证和续期要用 System.out.println("订阅ID: " + createdSub.id);
2. 完成订阅验证
微软会在创建订阅时发送验证请求到你的端点,直接返回验证令牌即可:
// Spring控制器示例 @PostMapping("/webhook/microsoft") public ResponseEntity<String> verifySubscription(@RequestParam("validationToken") String validationToken) { return ResponseEntity.ok(validationToken); }
3. 处理事件通知
当有新事件添加时,微软会POST通知到你的端点,解析并处理:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonNode; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @PostMapping("/webhook/microsoft") public ResponseEntity<Void> handleCalendarNotification(@RequestBody String payload) { ObjectMapper mapper = new ObjectMapper(); try { JsonNode root = mapper.readTree(payload); String receivedClientState = root.get("value").get(0).get("clientState").asText(); // 验证clientState是否和创建订阅时一致,防止伪造通知 if (!receivedClientState.equals("你设置的随机字符串")) { return ResponseEntity.badRequest().build(); } // 从通知中拿到事件资源地址,调用API获取详情 String eventUrl = root.get("value").get(0).get("resource").asText(); Event newEvent = graphClient.customRequest(eventUrl, Event.class) .buildRequest() .get(); // 这里写你的业务处理逻辑 System.out.println("新增日历事件: " + newEvent.subject); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.badRequest().build(); } return ResponseEntity.ok().build(); }
六、多租户常见注意事项
- 权限同意:工作/学校账户的部分高权限需要租户管理员批准,个人账户用户可自行同意。
- 令牌续期:借助
offline_access权限获取的刷新令牌,在访问令牌过期时自动获取新令牌,维持会话。 - 本地测试:必须用HTTPS端点接收通知,ngrok是最方便的本地测试工具。
- 订阅续期:订阅最长有效期3天,需要定时续期才能持续接收通知。
内容的提问来源于stack exchange,提问作者Vyassa
相关产品推荐
相关产品推荐

