EWS java apis使用Oauth2

时间:2015-02-10 05:11:38

标签: java oauth ms-office exchangewebservices

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

3 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:1)

鉴于在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)) {
        EwsUtils.listInboxMessages(service, senderAddress);
    }

    // send a message
    System.out.println("sending a message");
    try (ExchangeService service = getAuthenticatedService(token, senderAddress, traceListener)) {
        EwsUtils.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();
        }
    });
}

答案 2 :(得分:0)

我知道,这个问题已经很老了,但答案和答案相同。评论今天仍然帮助我。所以,我想简要介绍一下:

在此拉取请求中:https://github.com/OfficeDev/ews-java-api/pull/321 标题验证已被删除,如接受答案的评论中所述。

所以,它足以通过

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

不要设置任何其他凭据。

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

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服务器运行旧版本,此解决方案可以检索令牌并将其发送到服务器。