本文主要介绍如何使用EWS托管API摸拟一个用户获取邮箱的信息,和如何使用流式订阅的方式获取邮箱通知信息。该方案只适用于Exchange 2010 SP1以上,Exchange 2013;
一、摸拟邮件用户登陆。
有些时候我们总不喜欢通过在应用程序中做一个弹出式的窗口,让用户输入邮箱的用户和密码才能获取此用户的邮箱信息;而且密码用户是随时自己可以更改的。弹出式的体验实在让用户觉得很差,我们的团队之前就是用这些方式做一些邮箱功能,有很多客户都说体验很差。这两天通过细读MSDN中的EWS API,不出我所料:像Win32通过Token摸拟Window用户一样,Exchange同样也能摸拟所有邮箱用户,以下是主要步骤。
1、新建一个普通的邮箱帐号作为服务帐号,本次使用的帐号是“management@toso.com”
2、打开Exchange命令窗口并输入以下命令:
New-ManagementRoleAssignment –Name:impersonationAssignmentName –Role:ApplicationImpersonation –User:management@toso.com
2、新建一条策略,基中1000表示取多可以同时摸拟10000个用户,ITUserPolicy表示的是策略的名字(值得注意的是后面的参数:-ThrottlingPolicyScope Regular,我在Exchange2010试过好像不支持:
Exchange2013:
New-ThrottlingPolicy -Name ITUserPolicy -EwsMaxConcurrency 10000 -ThrottlingPolicyScope Regular
Exchange 2010 SP1
New-ThrottlingPolicy -Name ITUserPolicy -EwsMaxConcurrency 10000
3、获取策略的GUID
Get-ThrottlingPolicy | format-list
然后复制对应ITUserPolic的GUID值 2a846f2b-75b1-4f26-be25-8f444e8a505c
4、将策略应用于基中management@toso.com中
$a = Get-ThrottlingPolicyAssociation management@toso.com
$a | Set-ThrottlingPolicyAssociation -ThrottlingPolicy 2a846f2b-75b1-4f26-be25-8f444e8a505c
好的到现在为此,我的命令己经打完,接着我们的代码应该是这么写:
其中m_ServerUser为management@toso.com
m_Password是这个服务帐号的密码;
m_Domain是域名;
m_ExchangeMachine是EWS服务器。
strEmail是被摸拟的用户。
<p> string url = "<a target=_blank href="https://{0}/ews/exchange.asmx">https://{0}/ews/exchange.asmx</a>";
url = string.Format(url, m_ExchangeMachine);
ExchangeService _ExchangeService = new ExchangeService(ExchangeVersion.Exchange2010_SP1)
{
Credentials = new NetworkCredential(m_ServerUser, m_Password, m_Domain),
Url = new Uri(url)
};</p><p><span style="color:#003300;">_ExchangeService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, strEmail);</span></p>
二、邮件提醒
在Exchange 2007到Exchange 2010中,己经存在了两个邮箱订阅方式,分别是Pull Notification 和Push Noitification 这两种方式都有其中的优点和弱点,首先拿Push Notification来说,优点就是实时(Real Time)但缺点就需要我们提供一个端口,Exchange通过这个端口发送通知,Pull Notification不需要客户端(相对Exchange是服务)开通端口,但需要你定时去访问Exchange,失去实时性,到了Exchange 2010 Sp1,微软向我们提供了一个集两种方法优点的新的方案,流订阅方法(Streaming Subscription),以下是主要的源码片断:
//创建订阅
RuleCollection ruleCollection = _ExchangeService.GetInboxRules();
List<FolderId> lstfolder = new List<FolderId>();
lstfolder.Add(new FolderId(WellKnownFolderName.Inbox));
foreach (Rule rule in ruleCollection)
{
if (rule.Actions != null && rule.Actions.MoveToFolder != null && rule.Actions.MoveToFolder.UniqueId != null)//过滤垃圾邮件规则
lstfolder.Add(rule.Actions.MoveToFolder.UniqueId);
}
var subscription = _ExchangeService.SubscribeToStreamingNotifications(lstfolder.ToArray(), EventType.NewMail);
其中 CreateStreamingSubscription方法:
var connection = CreateStreamingSubscription(_ExchangeService, subscription);
private StreamingSubscriptionConnection CreateStreamingSubscription(ExchangeService service,
StreamingSubscription subscription)
{
var connection = new StreamingSubscriptionConnection(service, 30);
connection.AddSubscription(subscription);
connection.OnNotificationEvent += OnNotificationEvent;
connection.OnSubscriptionError += OnSubscriptionError;
connection.OnDisconnect += OnDisconnect;
connection.Open();
return connection;
}
其实到现在为止,我己经介绍了机制,如果觉得烦的话,下面的内容可以不需要再看了;
以下是贴出我项目中的一些代码。
整个封装类的源码
//新
[XmlRoot("EmailNotification")]
[Serializable]
public class EmailNotification
{
//邮件的主题
[XmlElement("Subject")]
public string Subject;
//邮件的创建者或发送者
[XmlElement("Creator")]
public string Creator;
//邮件内容的链接
[XmlElement("Link")]
public string Link;
[XmlElement("CreatTime")]
public string CreatTime;
[XmlElement("Receipt")]
public string Receipt;
}
public class EmailNotificationEventArgs : EventArgs
{
public EmailNotification Notification;
}
public class ExchSubscriptionClient
{
public delegate void (object sender, EmailNotificationEventArgs args);
private class ExchClient
{
public ExchangeService _exch;
public StreamingSubscriptionConnection conn;
public StreamingSubscription subscription;
public string user;
}
private Dictionary<string, ExchClient> m_lstEs = new Dictionary<string, ExchClient>();
private Dictionary<StreamingSubscriptionConnection, ExchClient> m_conn_Ex = new Dictionary<StreamingSubscriptionConnection, ExchClient>();
private static ExchSubscriptionClient g_instance = null;
private string m_ServerUser;
private string m_Password;
private string m_Domain;
private string m_ExchangeMachine;
private bool m_IsInit;
private object locker = new object();
private ExchSubscriptionClient()
{
m_IsInit = false;
}
public static ExchSubscriptionClient Instance()
{
if (g_instance == null)
{
g_instance = new ExchSubscriptionClient();
}
return g_instance;
}
public event EmailNotificationDelegate NewEmailEvent;
public bool IsInit()
{
return m_IsInit;
}
public void Init(string serverUser, string password, string domain, string exchFqdn)
{
m_ServerUser = serverUser;
m_Password = password;
m_Domain = domain;
m_ExchangeMachine = exchFqdn;
m_IsInit = true;
}
public void AddUser(string strEmail)
{
lock (locker)
{
try
{
if (m_lstEs.ContainsKey(strEmail))
{
return;
}
string url = "https://{0}/ews/exchange.asmx";
url = string.Format(url, m_ExchangeMachine);
ExchangeService _ExchangeService = new ExchangeService(ExchangeVersion.Exchange2010_SP1)
{
Credentials = new NetworkCredential(m_ServerUser, m_Password, m_Domain),
Url = new Uri(url)
};
_ExchangeService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, strEmail);
//创建订阅
RuleCollection ruleCollection = _ExchangeService.GetInboxRules();
List<FolderId> lstfolder = new List<FolderId>();
lstfolder.Add(new FolderId(WellKnownFolderName.Inbox));
foreach (Rule rule in ruleCollection)
{
if (rule.Actions != null && rule.Actions.MoveToFolder != null && rule.Actions.MoveToFolder.UniqueId != null)//过滤垃圾邮件规则
lstfolder.Add(rule.Actions.MoveToFolder.UniqueId);
}
var subscription = _ExchangeService.SubscribeToStreamingNotifications(lstfolder.ToArray(), EventType.NewMail);
var connection = CreateStreamingSubscription(_ExchangeService, subscription);
ExchClient client = new ExchClient();
client._exch = _ExchangeService;
client.conn = connection;
client.user = strEmail;
client.subscription = subscription;
m_lstEs.Add(strEmail, client);
m_conn_Ex.Add(connection, client);
}
catch (Exception ex)
{
}
}
}
public void RemoveUser(string strEmail)
{
lock (locker)
{
ExchClient client = m_lstEs[strEmail];
if (client != null)
{
try
{
client.subscription.Unsubscribe();
client.conn.Close();
m_conn_Ex.Remove(client.conn);
m_lstEs.Remove(strEmail);
}
catch
{
}
}
}
}
private StreamingSubscriptionConnection CreateStreamingSubscription(ExchangeService service,
StreamingSubscription subscription)
{
var connection = new StreamingSubscriptionConnection(service, 30);
connection.AddSubscription(subscription);
connection.OnNotificationEvent += OnNotificationEvent;
connection.OnSubscriptionError += OnSubscriptionError;
connection.OnDisconnect += OnDisconnect;
connection.Open();
return connection;
}
private void OnDisconnect(object sender, SubscriptionErrorEventArgs args)
{
// Cast the sender as a StreamingSubscriptionConnection object.
var connection = (StreamingSubscriptionConnection)sender;
try
{
connection.Open();
}
catch
{
lock (locker)
{
string strEmail = m_conn_Ex[connection].user;
m_conn_Ex.Remove(connection);
m_lstEs.Remove(strEmail);
}
}
}
private void OnNotificationEvent(object sender, NotificationEventArgs args)
{
if (NewEmailEvent == null)
return;
// Extract the item ids for all NewMail Events in the list.
var newMails = from e in args.Events.OfType<ItemEvent>()
where e.EventType == EventType.NewMail
select e.ItemId;
// Note: For the sake of simplicity, error handling is ommited here.
// Just assume everything went fine
StreamingSubscriptionConnection conn = sender as StreamingSubscriptionConnection;
ExchangeService es = null;
ExchClient client = m_conn_Ex[conn];
if (client != null)
es = client._exch;
if (es == null)
return;
var response = es.BindToItems(newMails, new PropertySet(BasePropertySet.IdOnly, ItemSchema.DateTimeReceived, ItemSchema.Subject, ItemSchema.LastModifiedName, ItemSchema.WebClientReadFormQueryString));
var items = response.Select(itemResponse => itemResponse.Item);
foreach (var item in items)
{
EmailNotificationEventArgs e = new EmailNotificationEventArgs();
EmailNotification emailNotification = new EmailNotification();
//string owaUrl = "https://{0}/owa/";
//owaUrl = string.Format(owaUrl, ExchSubscriptionClient.Instance().m_ExchangeMachine);
emailNotification.Subject = string.IsNullOrEmpty(item.Subject) ? "无标题邮件" : item.Subject;
//emailNotification.Link = owaUrl + item.WebClientReadFormQueryString;
emailNotification.Link = item.WebClientReadFormQueryString;
emailNotification.CreatTime = item.DateTimeReceived.ToString();
emailNotification.Creator = item.LastModifiedName;
emailNotification.Receipt = + client.user;
e.Notification = emailNotification;
NewEmailEvent(this, e);
}
}
private void OnSubscriptionError(object sender, SubscriptionErrorEventArgs args)
{
// Handle error conditions.
var e = args.Exception;
//写入日志
}
客户代码(以上类的客户)
ExchSubscriptionClient es=ExchSubscriptionClient.Instance();
if (!es.IsInit())
{
es.Init(ConfigHelper.ServerUser, ConfigHelper.Password, ConfigHelper.Domain, ConfigHelper.ExchangeFQDN);
es.NewEmailEvent += new ExchSubscriptionClient.EmailNotificationDelegate(es_NewEmailEvent);
}
es.AddUser(hejg@toso.com);