【问题标题】:How to integrate a Spring Boot Java app with Outlook calendar for MS personal account users?如何将 Spring Boot Java 应用程序与 MS 个人帐户用户的 Outlook 日历集成?
【发布时间】:2022-12-15 10:38:24
【问题描述】:

想法是构建一个 Spring Boot Java 应用程序,我们可以使用它获取 MS Outlook 个人帐户持有人的日历数据,比如即将发生的事件。根据初步研究,我发现 MS-Graph API 是相同的答案,我开始使用 this 教程作为启动代码。

我的 application.yml 文件如下所示:

 spring:
      cloud:
        azure:
          active-directory:
            enabled: true
            profile:
              tenant-id: common
            credential:
              client-id: <from azure portal>
              client-secret: <from azure portal>
            authorization-clients:
              graph:
                scopes:
                 - https://graph.microsoft.com/User.Read
                 - https://graph.microsoft.com/Calendars.Read
                 - https://graph.microsoft.com/Calendars.ReadWrite
                 - https://graph.microsoft.com/Analytics.Read

tenant-id 设置为通用,因为我想与任何个人 outlook 帐户持有人联系。在 Azure 门户上,支持的帐户类型设置为 -“任何组织目录中的帐户(任何 Azure AD 目录 - 多租户)和个人 Microsoft 帐户(例如 Skype、Xbox)”enter image description here

为了测试我是否可以访问 MS-Graph Calendar API,我添加了下面提到的代码(来自here):

EventCollectionPage events = graphClient.me().calendar().events()
    .buildRequest()
    .get();

我可以连接到 Outlook 日历并获取我自己帐户的事件,这些事件恰好与访问 Azure 门户的帐户相同。因此,我的个人帐户 abcXyz@outlook.com 也是应用程序注册的租户的管理员帐户。

但是,当我尝试连接到另一个由我创建的个人 outlook 帐户时,我在相关日历中添加了一些事件以进行测试,但我失败了。这个新的 outlook 个人帐户肯定不在应用程序注册的租户中,我不太确定个人 MS 帐户进入哪个租户,以及如何向他们添加服务主体。

在为 Azure 门户上的所有帐户配置应用程序注册并在我的 application.yml 文件中设置 tenant-id 后,我希望连接所有注册 Java 应用程序但遇到以下错误的个人帐户。似乎 Microsoft 身份平台不允许其他用户将其日历连接到我的 Java 应用程序,因为我的应用程序的服务主体未存储在个人帐户持有人的租户中。但是怎么做呢? 我解决问题的方法是正确的还是我应该而不是利用 Spring 库(OAuth2Client,AzureActiveDirectory)更喜欢自己构建 OAuth2.0 客户端,自己到达 /authorize 和 /token 端点,尽管 MS 不建议这样做提到here?

java.lang.IllegalArgumentException: Missing attribute 'name' in attributes
at org.springframework.security.oauth2.core.user.DefaultOAuth2User.<init>(DefaultOAuth2User.java:72) ~[spring-security-oauth2-core-5.6.7.jar:5.6.7]
at org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser.<init>(DefaultOidcUser.java:93) ~[spring-security-oauth2-core-5.6.7.jar:5.6.7]
at org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser.<init>(DefaultOidcUser.java:67) ~[spring-security-oauth2-core-5.6.7.jar:5.6.7]
at com.azure.spring.cloud.autoconfigure.aad.implementation.webapp.AadOAuth2UserService.loadUser(AadOAuth2UserService.java:134) ~[spring-cloud-azure-autoconfigure-4.3.0.jar:4.3.0]
at com.azure.spring.cloud.autoconfigure.aad.implementation.webapp.AadOAuth2UserService.loadUser(AadOAuth2UserService.java:52) ~[spring-cloud-azure-autoconfigure-4.3.0.jar:4.3.0]
at org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider.authenticate(OidcAuthorizationCodeAuthenticationProvider.java:156) ~[spring-security-oauth2-client-5.6.7.jar:5.6.7]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182) ~[spring-security-core-5.6.7.jar:5.6.7]
at org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.attemptAuthentication(OAuth2LoginAuthenticationFilter.java:195) ~[spring-security-oauth2-client-5.6.7.jar:5.6.7]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:223) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:213) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:178) ~[spring-security-oauth2-client-5.6.7.jar:5.6.7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.22.jar:5.3.22]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.22.jar:5.3.22]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.22.jar:5.3.22]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.22.jar:5.3.22]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221) ~[spring-security-web-5.6.7.jar:5.6.7]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186) ~[spring-security-web-5.6.7.jar:5.6.7]

【问题讨论】:

    标签: spring-boot microsoft-graph-api microsoft-graph-calendar microsoft-identity-platform spring-cloud-azure


    【解决方案1】:

    spring-cloud-azure-starter-active-directory 旨在支持Work/School account。是否支持Personal account目前还不确定。它在这个GitHub issue 中被跟踪。

    Work/School account和Personal account的区别,请参考this page。

    【讨论】:

    • 谢谢如君的回复!我发现 MS 具有用户位于租户中的概念,对于在租户中注册的 oauth2 应用程序与另一个租户的用户交谈,我们需要以下内容:1. 这些租户的管理员明确添加服务主体(a JSON) 在这些租户中。 2. 我需要让我的 oauth2 客户端应用程序成为多租户应用程序。现在我不明白谁是微软个人账户的管理员。请检查:stackoverflow.com/questions/70355146/…
    • 在上面的链接中我们可以看到这个家伙正在处理oauth2的授权码授予流程的授权码。 MS 不推荐这种低级实现,Spring 教程也不是这样设计的,但我找不到原始问题的任何解决方案,这个链接(在上面的评论中)让我想到构建一个 oauth2 客户端而是一个低水平。不过,仍在寻找合适的答案,感谢您再次回复!
    【解决方案2】:

    使用此示例了解如何呼叫graph

    个人是其个人帐户的管理员。当他们登录您的应用程序时,将有一个他们需要同意才能使用您的应用程序的操作列表。在您的情况下,其中之一类似于“阅读日历”。然后,此人会授予您的应用程序读取他们的 Outlook 日历的能力。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-02-26
      • 2011-05-18
      • 2021-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-24
      • 1970-01-01
      相关资源
      最近更新 更多