【问题标题】:EWS java apis using Oauth2使用 Oauth2 的 EWS java api
【发布时间】:2015-04-10 01:33:33
【问题描述】:

我想为我的应用使用 Oauth2 身份验证。我想使用 EWS Java api 从 O365 获取数据。可能吗? 文档http://blogs.msdn.com/b/exchangedev/archive/2014/09/24/10510847.aspx 谈论为 REST api 获取 oauth 令牌我是否应该使用相同的文档来获取令牌以用于 EWS Web 服务? 任何人都可以分享任何使用 java 执行此操作的代码示例。

【问题讨论】:

    标签: java oauth ms-office exchangewebservices


    【解决方案1】:

    这是可能的。您必须以与 REST 相同的方式注册您的应用程序,但您需要指定特殊的 EWS 权限“通过 EWS 完全访问用户邮箱”。您需要执行 OAuth 流程来检索访问令牌,然后将其包含在 EWS 请求的授权标头中。我没有适合您的 Java 示例,但这些是所需的基本步骤。

    【讨论】:

    • EWS Java API 存在于 github.com/OfficeDev/ews-java-api 没有 Microsoft.Exchange.WebServices.Data.OAuthCredentials 类,因为它仅支持 ExchangeVersion.Exchange2010_SP2。因此,为了将 EWS api 与 Java 和 Oauth 身份验证一起使用。你能建议我应该如何进行吗??
    • 尝试通过 ExchangeService 上的 httpHeaders 直接设置 Authorize 标头。格式为“Bearer ”。
    • 我试过 service.getHttpHeaders().put("Authorization", "Bearer ");但它给了我错误说“请求失败。HTTP 标头 'Authorization=Bearer ' 是不允许的。只允许带有 'X-' 前缀的 HTTP 标头。”
    • 啊,我明白了。是的,查看 Java API 的源代码,它只允许设置 X-标头(请参阅 ExchangeServiceBase.java 中的 CannotAddRequestHeader)。您可能需要修改源代码才能使其正常工作。
    • 非常感谢杰森!有效!我只是将 ExchangeServiceBase.java 类中的 validate() 方法代码注释掉了。
    【解决方案2】:

    我知道,这个问题已经很老了,但是今天的答案和 cmets 仍然帮助了我。所以,我想简单地总结一下:

    在这个拉取请求中:https://github.com/OfficeDev/ews-java-api/pull/321 如已接受答案的 cmets 中所述,已删除标头验证。

    所以,通过设置令牌就足够了

    ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
    service.getHttpHeaders().put("Authorization", "Bearer " + officeToken);
    

    不要设置任何额外的凭据。

    为了完整性:在我的场景中,officeToken 是通过 Office JavaScript api 在客户端检索的

    Office.initialize = function() {
        $(document).ready(function (){
            Office.context.mailbox.getCallbackTokenAsync(function(result) {
                result.value; // is the officeToken of above
                // do s.th. with the officeToken; e.g. send it to the server
            });
        });
    });
    

    在服务器上,我们现在可以获取邮件的内容。在最新版本的 Office JavaScript Api 中,也可以直接在客户端中执行此操作。但是,您的 Exchange Api 版本必须是 1.3。因此,如果您的 Exchange 服务器运行的是旧版本,这种检索令牌并将其发送到服务器的解决方案非常有用。

    【讨论】:

      【解决方案3】:

      鉴于在 EWS 中使用基本身份验证将在 2020 年 10 月停止工作 (source),我走上了让我的应用改为使用 OAuth 令牌身份验证的道路。

      正如 Jason Johnson 所述,您需要允许 Azure AD 应用程序“通过 EWS 完全访问用户邮箱”。正如您可以想象的那样,这会产生安全问题,因为应用程序可以访问和修改该租户中任何人的邮箱。小心使用!

      免责声明 - adal4j 不再受支持,虽然此解决方案有效,但请注意 adal4j 库有一个错误,该错误会错误 在AdalCallable.java 中记录错误。这个fork 修补了这个问题,但没有公开 工件可用,因此您需要自己编译它。另一种选择可能是尝试更多 最新的msal4j 但是我还没有用那个测试这个解决方案 图书馆。

      这是我使用的 maven 依赖项,我排除了 slf4j,因为我在 glassfish 中遇到了类加载器冲突,所以排除是可选的:

          <dependency>
              <groupId>com.microsoft.ews-java-api</groupId>
              <artifactId>ews-java-api</artifactId>
              <version>2.0</version>
          </dependency>
          <dependency>
              <groupId>com.microsoft.azure</groupId>
              <artifactId>adal4j</artifactId>
              <version>1.6.4</version>
              <exclusions>
                  <exclusion>
                      <groupId>org.slf4j</groupId>
                      <artifactId>slf4j-api</artifactId>
                  </exclusion>
              </exclusions>
          </dependency>
          <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>
              <version>1.7.21</version>
              <scope>test</scope>
          </dependency>
      

      这里是令牌提供者:

      import java.net.MalformedURLException;
      import java.net.URI;
      import java.net.URISyntaxException;
      import java.time.Duration;
      import java.util.concurrent.ExecutionException;
      import java.util.concurrent.Future;
      import java.util.concurrent.TimeUnit;
      import java.util.concurrent.TimeoutException;
      
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      import org.apache.log4j.Logger;
      
      import com.microsoft.aad.adal4j.AuthenticationCallback;
      import com.microsoft.aad.adal4j.AuthenticationContext;
      import com.microsoft.aad.adal4j.AuthenticationResult;
      import com.microsoft.aad.adal4j.ClientCredential;
      
      import microsoft.exchange.webservices.data.core.ExchangeService;
      import microsoft.exchange.webservices.data.core.WebProxy;
      import microsoft.exchange.webservices.data.core.enumeration.misc.ConnectingIdType;
      import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
      import microsoft.exchange.webservices.data.misc.ImpersonatedUserId;
      
      /**
       * Used to obtain an access token for use in an EWS application. Caches the
       * token and refreshes it 5mins prior to expiration.
       * 
       * @author Stephen O'Hair
       *
       */
      public final class MsEwsTokenProvider {
      
          private static final Logger log = Logger.getLogger(MsEwsTokenProvider.class);
      
          private static final String EWS_URL = "https://outlook.office365.com/EWS/Exchange.asmx";
          private static final String RESOUCE = "https://outlook.office365.com";
          private static final String TENANT_NAME = "enter your tenant name here";
          private static final String AUTHORITY = "https://login.microsoftonline.com/" + TENANT_NAME;
          private static final long REFRESH_BEFORE_EXPIRY_MS = Duration.ofMinutes(5).toMillis();
      
          private static long expiryTimeMs;
          private static String accessToken;
      
          /**
           * Takes an OAuth2 token and configures an {@link ExchangeService}.
           * 
           * @param token
           * @param senderAddr
           * @param traceListener
           * @param mailboxAddr
           * @return a configured and authenticated {@link ExchangeService}
           * @throws URISyntaxException
           * @throws Exception
           */
          public static ExchangeService getAuthenticatedService(String token, String senderAddr, 
                  TraceListener traceListener) throws URISyntaxException, Exception {
              ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
              service.setTraceListener(traceListener);
              service.getHttpHeaders().put("Authorization", "Bearer " + token);
              service.getHttpHeaders().put("X-AnchorMailbox", senderAddr);
              //service.setWebProxy(new WebProxy(proxyHost, proxyPort));
              service.setUrl(new URI(EWS_URL));
              service.setImpersonatedUserId(new ImpersonatedUserId(ConnectingIdType.PrincipalName, senderAddr));
              return service;
          }
      
          /**
           * Simple way to get an access token using the Azure Active Directory Library.
           * 
           * Authenticates at : https://login.microsoftonline.com/
           * 
           * @param clientId
           *            - client id of the AzureAD application
           * @param clientSecret
           *            - client secret of the AzureAD application
           * @param service
           *            - managed executor service
           * 
           * @return provisioned access token
           * @throws MalformedURLException
           * @throws InterruptedException
           * @throws ExecutionException
           * @throws TimeoutException
           */
          public static synchronized String getAccesToken(String clientId, String clientSecret, ManagedExecutorService service)
                  throws MalformedURLException, InterruptedException, ExecutionException, TimeoutException {
      
              long now = System.currentTimeMillis();
              if (accessToken != null && now < expiryTimeMs - REFRESH_BEFORE_EXPIRY_MS) {
      
                  AuthenticationContext context = new AuthenticationContext(AUTHORITY, false, service);
                  AuthenticationCallback<AuthenticationResult> callback = new AuthenticationCallback<AuthenticationResult>() {
      
                      @Override
                      public void onSuccess(AuthenticationResult result) {
                          log.info("received token");
                      }
      
                      @Override
                      public void onFailure(Throwable exc) {
                          throw new RuntimeException(exc);
                      }
                  };
      
                  log.info("requesting token");
                  Future<AuthenticationResult> future = context.acquireToken(RESOUCE,
                          new ClientCredential(clientId, clientSecret), callback);
      
                  // wait for access token
                  AuthenticationResult result = future.get(30, TimeUnit.SECONDS);
      
                  // cache token and expiration
                  accessToken = result.getAccessToken();
                  expiryTimeMs = result.getExpiresAfter();
              }
      
              return accessToken;
          }
      }
      

      这是一个使用上述令牌提供程序类列出收件箱中的消息并发送电子邮件的示例:

      import java.net.MalformedURLException;
      import java.net.URI;
      import java.net.URISyntaxException;
      import java.util.concurrent.ExecutionException;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      import java.util.concurrent.Future;
      
      import com.microsoft.aad.adal4j.AuthenticationCallback;
      import com.microsoft.aad.adal4j.AuthenticationContext;
      import com.microsoft.aad.adal4j.AuthenticationResult;
      import com.microsoft.aad.adal4j.ClientCredential;
      
      import microsoft.exchange.webservices.data.core.ExchangeService;
      import microsoft.exchange.webservices.data.core.WebProxy;
      import microsoft.exchange.webservices.data.core.enumeration.misc.ConnectingIdType;
      import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
      import microsoft.exchange.webservices.data.misc.ImpersonatedUserId;
      /**
       * Entry point.
       * 
       * @param args
       * @throws Exception
       */
      public static void main(String[] args) throws Exception {
      
          // Pro tip: make sure to set your proxy configuration here if needed 
          // and exclude outlook.office365.com from proxy SSL inspection.
      
          String clientId = "your AzureAD application client id";
          String clientSecret = "your AzureAD application client secret";
          String tenantName = "your tenant";
          String recipientAddr = "recipient@yourdomain.com";
          String senderAddress = "yourO365@mailbox.com";
      
          TraceListener traceListener = new ITraceListener() {
      
              @Override
              public void trace(String traceType, String traceMessage) {
                  // TODO log it, do whatever...
      
              }
          };
      
          // I used a ManagedExecutorService provided by glassfish but you can 
          // use an ExecutorService and manage it yourself.
          String token = MsEwsTokenProvider.getAccesToken(clientId, clientSecret, service);
          // don't log this in production!
          System.out.println("token=" + token);
      
          // test mailbox read access
          System.out.println("geting emails");
          try (ExchangeService service = MsEwsTokenProvider.getAuthenticatedService(token, senderAddress)) {
             listInboxMessages(service, senderAddress);
          }
      
          // send a message
          System.out.println("sending a message");
          try (ExchangeService service = getAuthenticatedService(token, senderAddress, traceListener)) {
             sendTestMessage(service, recipientAddr, senderAddress);
          }
      
          System.out.println("finished");
      }
      
      public static void sendTestMessage(ExchangeService service, String recipientAddr, String senderAddr)
              throws Exception {
          EmailMessage msg = new EmailMessage(service);
          msg.setSubject("Hello world!");
          msg.setBody(MessageBody.getMessageBodyFromText("Sent using the EWS Java API."));
          msg.getToRecipients().add(recipientAddr);
          msg.send();
          msg.setSender(new EmailAddress(senderAddr));
      }
      
      public static void listInboxMessages(ExchangeService service, String mailboxAddr) throws Exception {
          ItemView view = new ItemView(50);
          Mailbox mb = new Mailbox(mailboxAddr);
          FolderId folder = new FolderId(WellKnownFolderName.Inbox, mb);
          FindItemsResults<Item> result = service.findItems(folder, view);
          result.forEach(i -> {
              try {
                  System.out.println("subject=" + i.getSubject());
              } catch (ServiceLocalException e) {
                  e.printStackTrace();
              }
          });
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-05-05
        • 1970-01-01
        • 2012-03-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-28
        • 1970-01-01
        相关资源
        最近更新 更多