使用OAuth2 和Graph实现发送邮件功能

之前系统里发送邮件使用的是SMTP+账号密码的方式,但是近期公司通知这种方式不安全,需要使用OAuth2的方式进行替换,研究了一下,记录下操作步骤。当前使用了两种方式,但是第一种方式暂时无法添加邮件中的附件,PS:两种方式均需登录AAD注册APP,然后使用生成的APPID和tenantID以及生成的密码

开始之前,先看一下下文中Delegated权限和Application权限的区别:
使用 OAuth 对 EWS 应用程序进行身份验证 | Microsoft Learn

一、注册AppID等信息

1、申请AppID,tenantID操作步骤

管理员在AAD(Azure Active Directory)进行App 注册。注册完毕之后会生产Application (client) ID(APPID)和一个Directory (tenant) ID (租户ID)

 2、申请secrets

按照如下图步骤,

注意:

        1、在选择过期日期时,默认为6个月,可手动修改为24个月

        2、生产的Value值只会在首次添加完成之后出现,所以添加完成之后,将步骤2中Value值保存下来

3、添加授权权限

 点击左侧的API Permission 链接,添加如下的权限

  注意:如果你是管理员,那么你可以添加Application类型的权限,否则你只能添加Delegation类型的权限

4、Config文件配置第一步骤中生成的三个ID

<!-- The application ID from your app registration -->
<add key="appId" value="{{YOUR APP ID}}" />
<!-- The tenant ID copied from your app registration -->
<add key="tenantId" value="{{YOUR TENANTID}}"/>
<!-- The application's client secret from your app registration. Needed for application permission access -->
<add key="clientSecret" value="{{YOUR SECRET ID}}"/>

二、代码执行逻辑 

1、使用OAuth2方式代码:

 public async Task Test()
 {
     string clientId = "{{YOUR APP ID}}";
     string clientSecret = "{{YOUR SECRET ID}}";
     string tenantId = "{{YOUR TENANTID}}";
     string accessToken = await GetAccessToken(clientId, clientSecret, tenantId);

     string emailEndpoint = "https://graph.microsoft.com/v1.0/users/{{ YOUR USER EMAIL  }}/sendMail";
     string emailContent = "{{ Email Content }}";
     string recipientEmail = "{{ RecipientEmail }}";
     string senderEmail = "{{ Sender Email }}";
     string subject = "{{ Email Subject }}";

     await SendEmail(accessToken, emailEndpoint, emailContent, recipientEmail, senderEmail, subject);
 }

 private async Task<string> GetAccessToken(string clientId, string clientSecret, string tenantId)
 {
     string tokenEndpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token";
     string scope = "https://graph.microsoft.com/.default";

     using (HttpClient client = new HttpClient())
     {
         var requestContent = new FormUrlEncodedContent(new[]
         {
         new KeyValuePair<string, string>("grant_type", "client_credentials"),
         new KeyValuePair<string, string>("client_id", clientId),
         new KeyValuePair<string, string>("client_secret", clientSecret),
         new KeyValuePair<string, string>("scope", scope)
     });

         var response = await client.PostAsync(tokenEndpoint, requestContent);
         var responseContent = await response.Content.ReadAsStringAsync();

         TokenHelper tokenHelper = new TokenHelper();
         if (responseContent == null)
         {
             return "";
         }
         else
         {
             tokenHelper = JsonConvert.DeserializeObject<TokenHelper>(responseContent ?? responseContent);
         }
         return tokenHelper.access_token;
     }
 }

 private async Task SendEmail(string accessToken, string emailEndpoint, string emailContent, string recipientEmail, string senderEmail, string subject)
 {
     using (HttpClient client = new HttpClient())
     {
         client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
     
         List<Microsoft.Graph.Models.Attachment> attachments = new List<Microsoft.Graph.Models.Attachment>();
         FileAttachment fileAttachment = new FileAttachment();
         fileAttachment.Name = "Test Attachment1";
         fileAttachment.OdataType = "#Microsoft.graph.fileAttachment";
         fileAttachment.ContentType = "text/plain";
         attachments.Add(fileAttachment);
         var email = new
         {
             message = new
             {
                 subject = subject,
                 body = new { contentType = "Text", content = emailContent },
                 toRecipients = new[] { new { emailAddress = new { address = recipientEmail } } },
                 CcRecipients = new[] { new { emailAddress = new { address = recipientEmail } } },
                 BccRecipients = new[] { new { emailAddress = new { address = recipientEmail } } },
                 from = new { emailAddress = new { address = senderEmail } },
                 Attachments = attachments
             }
         };

         var requestContent = new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json");

         var response = await client.PostAsync(emailEndpoint, requestContent);
         var responseContent = await response.Content.ReadAsStringAsync();

     }
 }

使用这一种方式添加邮件中附件时,需要附件格式为FileAttachment,但是进行发送请求验证时,一直提示“{"error":{"code":"RequestBodyRead","message":"The property 'ContentBytes' does not exist on type 'microsoft.graph.attachment'. Make sure to only use property names that are defined by the type or mark the type as open type."}}”,但是“ContentBytes”属性又是FileAttachment required属性,因此就陷入了死循环中。有大佬知道咋回事儿的,可以告诉小弟一声。

2、使用Oauth2方式测试

2、使用Graph方式: 

 public async Task Test2()
 {
     string clientId = "{{YOUR APP ID}}"; // GET FROM AAD
     string clientSecret = "{{YOUR SECRET ID}}"; // GET FROM AAD
     string tenantId = "{{YOUR TENANTID}}"; // GET FROM AAD

     var credential = new ClientSecretCredential(tenantId, AppId, clientSecret);
     var graphClient = new GraphServiceClient(credential);

     //模拟发送邮件中附件地址
     var attachments = new List<string>
             {
                 @"C:\Files\新建 Microsoft Word 文档.docx",
                 @"C:\Files\新建 文本文档.txt",
                 @"C:\Files\123123.xlsx"
             };

     List<Attachment> Graphattachments= new List<Attachment>();
     foreach (var filePath in attachments)
     {
         string fileName = Path.GetFileName(filePath);
         byte[] fileBytes = File.ReadAllBytes(filePath);

         Graphattachments.Add(new FileAttachment
         {
             Name = fileName,
             ContentBytes = fileBytes,
             ContentType = GetMimeType(fileName),
             OdataType = "#microsoft.graph.fileAttachment"
         });
     }

     var requestBody = new SendMailPostRequestBody
     {
         Message = new Message
         {
             Subject = "Meet for lunch?",
             Body = new ItemBody
             {
                 ContentType = BodyType.Text,
                 Content = "{{ Email Content }}",
             },
             ToRecipients = new List<Recipient>
                 {
                     new Recipient
                     {
                         EmailAddress = new EmailAddress
                         {
                             Address = "{{ Recipients Email }}",
                         },
                     },
                 },
             CcRecipients = new List<Recipient>
             {
                 new Recipient
                 {
                     EmailAddress = new EmailAddress
                     {
                         Address = "{{ CC Recipients }}",
                     },
                 },
             },
            BccRecipients = new List<Recipient>
            {
                new Recipient
                {
                    EmailAddress = new EmailAddress
                    {
                    Address = "{{ BCC Recipients }}",
                    },
                },
            },
             Attachments = Graphattachments
         },
         SaveToSentItems = false,
     };
     await graphClient.Users["{{ YOUR Sender Email URL }}"].SendMail.PostAsync(requestBody);
 }
/// <summary>
/// 验证文件类型
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
 private string GetMimeType(string filePath)
 {
     string ext = Path.GetExtension(filePath).ToLower();
     return ext switch
     {
         ".txt" => "text/plain",
         ".pdf" => "application/pdf",
         ".jpg" => "image/jpeg",
         _ => "application/octet-stream"
     };
 }

2、使用Graph方式测试:

 

总结:以上内容如果大家需要使用相关代码,可联系我。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值