如何配置和连接Azure通信服务和Laravel?

问题描述 投票:0回答:1

我正在为 Laravel 集成 Azure 通信服务电子邮件,当然,我已经创建了帐户、添加了域、验证了 SPF 和 DKIM,之后添加到我的 config/services.php 中

'azure' => [
        'endpoint' => env('AZURE_ENDPOINT'),
        'accessKey' => env('AZURE_ACCESS_KEY'),
    ]

能够使用我添加到 .env 中的凭据,在本例中是这样的示例:

#Configuration using Azure Communications Services for Email
AZURE_ENDPOINT=https://laravel.unitedstates.communication.azure.com
AZURE_ACCESS_KEY=Ie2zAasb56t4ht86u7axRLh

在此之后,我为连接创建了一个服务

<?php

namespace App\Services;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class AzureEmailService
{
    protected $client;
    protected $endpoint;
    protected $accessKey;

    public function __construct()
    {
        $this->client = new Client();
        $this->endpoint = config('services.azure.endpoint');
        $this->accessKey = config('services.azure.accessKey');
    }

    public function sendEmail($to, $subject, $body)
    {
        $url = "{$this->endpoint}/emails:send?api-version=2021-10-01-preview";
        //dd($url);

        $headers = [
            'Authorization' => 'Bearer ' . $this->accessKey,
            'Content-Type' => 'application/json',
        ];

        $payload = [
            'senderAddress' => '[email protected]', //dirección de envío
            'recipients' => [
                'to' => [
                    ['address' => $to], // la API
                ],
            ],
            'content' => [
                'subject' => $subject,
                'plainText' => $body, // Cambia aquí si quiero usar HTML
                ],
        ];

        try {
            $response = $this->client->post($url, [
                'headers' => $headers,
                'json' => $payload,
            ]);

            return json_decode($response->getBody()->getContents());

        } catch (RequestException $e) {
            \Log::error('Error al enviar el email: ', ['error' => $e->getMessage()]);
            throw new \Exception("Error al enviar el email", 500);
        }

    }
}

此后我创建了一个 Livewire 组件:

class SendEmailForm extends Component
{
    public $email;
    public $subject;
    public $body;

    //validaciones basicas
    protected $rules = [
        'email' => 'required|email',
        'subject' => 'required|string|max:255',
        'body' => 'required|string',
    ];

    // Método para enviar el email
    public function sendEmail(AzureEmailService $azureEmailService)
    {
        // Validamos el formulario antes de enviar
        $this->validate();

        try {
            // Llamamos al servicio Azure para enviar el email
            $azureEmailService->sendEmail($this->email, $this->subject, $this->body);

            // Si el email se envía correctamente, mostramos un mensaje de éxito
            session()->flash('success', 'Email enviado correctamente.');
        } catch (\Exception $e) {
            // Si hay un error, lo mostramos
            session()->flash('error', 'Error al enviar el email: ' . $e->getMessage());
        }
    }

    public function render()
    {
        return view('livewire.send-email-form');
    }
}

将其连接到包含用于发送电子邮件的表单的视图

<div class="max-w-xl mx-auto p-6 bg-white shadow-md rounded-md">
    @if (session()->has('success'))
        <div class="text-green-500 mb-4">
            {{ session('success') }}
        </div>
    @endif

    @if (session()->has('error'))
        <div class="text-red-500 mb-4">
            {{ session('error') }}
        </div>
    @endif

    <form wire:submit.prevent="sendEmail">
        <div class="mb-4">
            <label for="email" class="block text-gray-700">Email:</label>
            <input type="email" id="email" wire:model="email" class="w-full border-gray-300 rounded-md shadow-sm" required>
            @error('email') <span class="text-red-500">{{ $message }}</span> @enderror
        </div>

        <div class="mb-4">
            <label for="subject" class="block text-gray-700">Asunto:</label>
            <input type="text" id="subject" wire:model="subject" class="w-full border-gray-300 rounded-md shadow-sm" required>
            @error('subject') <span class="text-red-500">{{ $message }}</span> @enderror
        </div>

        <div class="mb-4">
            <label for="body" class="block text-gray-700">Mensaje:</label>
            <textarea id="body" wire:model="body" class="w-full border-gray-300 rounded-md shadow-sm" rows="4" required></textarea>
            @error('body') <span class="text-red-500">{{ $message }}</span> @enderror
        </div>

        <button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
            Enviar Email
        </button>
    </form>
</div>

但是发送时,我收到此错误:

[2024-10-18 06:22:19] local.ERROR: Error al enviar el email:  {"error":"Client error: `POST https://laravel.unitedstates.communication.azure.com/emails:send?api-version=2021-10-01-preview` resulted in a `401 Unauthorized` response:
{\"error\":{\"code\":\"Denied\",\"message\":\"Denied by the resource provider.\"}}
"} 

显然该错误表明我没有授权,但我不明白我是否应该在此过程中配置其他内容,是否在访问之前创建令牌或类似的东西......这些情况的文档不是很清楚,在这种情况下,我的连接是通过 cURL 但其他方式是使用包或 SDK,因此由于这个原因,信息对我来说不是很清楚......

如何正确连接?或者我跳过了某些步骤?

我希望我可以解决问题,以纠正使用Azure API发送电子邮件的正确配置

php laravel azure symfony email
1个回答
0
投票

您应该从 Azure Active Directory 获取 OAuth 2.0 令牌并将其用作不记名令牌,而不是使用

Authorization
标头中的访问密钥。

  • 转到 Azure 门户,在“Azure Entra”下注册一个应用程序(称为 Azure Active Directory ->“应用程序注册”),然后获取
    Client ID
    Tenant ID
    Client Secret

enter image description here

  • 使用
    Guzzle
    等库或 Laravel 的
    league/oauth2-client
    包来获取令牌。

Guzzle 请求获取令牌:

namespace App\Services;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class AzureEmailService
{
    protected $client;
    protected $endpoint;
    protected $clientId;
    protected $clientSecret;
    protected $tenantId;

    public function __construct()
    {
        $this->client = new Client();
        $this->endpoint = config('services.azure.endpoint');
        $this->clientId = env('AZURE_CLIENT_ID');
        $this->clientSecret = env('AZURE_CLIENT_SECRET');
        $this->tenantId = env('AZURE_TENANT_ID');
    }

    // Function to get the OAuth 2.0 token from Azure AD
    protected function getAccessToken()
    {
        $url = 'https://login.microsoftonline.com/' . $this->tenantId . '/oauth2/v2.0/token';

        try {
            $response = $this->client->post($url, [
                'form_params' => [
                    'grant_type' => 'client_credentials',
                    'client_id' => $this->clientId,
                    'client_secret' => $this->clientSecret,
                    'scope' => 'https://communication.azure.com/.default',
                ],
            ]);

            $data = json_decode($response->getBody(), true);

            return $data['access_token'];

        } catch (RequestException $e) {
            \Log::error('Failed to get Azure OAuth Token: ', ['error' => $e->getMessage()]);
            throw new \Exception("Failed to get access token from Azure", 500);
        }
    }

    // Function to send the email
    public function sendEmail($to, $subject, $body)
    {
        $accessToken = $this->getAccessToken(); // Obtain access token

        $url = "{$this->endpoint}/emails:send?api-version=2021-10-01-preview";

        $headers = [
            'Authorization' => 'Bearer ' . $accessToken,
            'Content-Type' => 'application/json',
        ];

        $payload = [
            'senderAddress' => '[email protected]',
            'recipients' => [
                'to' => [
                    ['address' => $to],
                ],
            ],
            'content' => [
                'subject' => $subject,
                'plainText' => $body,
            ],
        ];

        try {
            $response = $this->client->post($url, [
                'headers' => $headers,
                'json' => $payload,
            ]);

            return json_decode($response->getBody()->getContents());

        } catch (RequestException $e) {
            \Log::error('Error sending email: ', ['error' => $e->getMessage()]);
            throw new \Exception("Error sending email", 500);
        }
    }
}

认证成功。

结果:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.