You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Tauri+Rust实现多邮箱登录邮件发送的认证失败问题

问题:Tauri+Lettre对接Outlook STARTTLS时,前端传递的凭证认证失败

硬编码邮箱凭证可正常发送邮件,但通过React前端输入传递的凭证触发5.7.3 Authentication unsuccessful错误,需支持多用户使用不同邮箱登录发邮件。

复现代码

React前端(App.jsx)

import React, {useState} from 'react';
import { invoke } from '@tauri-apps/api/tauri';

function startpage() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  function loginOutlook() {
    const emailString = JSON.stringify(email);
    const passwordString = JSON.stringify(password);
    
    invoke('take2_send_test_email_with_login_info', {invoke_message: emailString, invoke_message2: passwordString});

  }
  return (
    <div className="container">
      <h2>Please enter your Outlook login info here</h2>
      <label>
        Enter Email: 
        <input 
          name="email" 
          type="text"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
      </label>

      <label>
        Enter Password: 
        <input 
          type="password" 
          name="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
       />
      </label>
      
      <button
        onClick={
          () => {
            loginOutlook()
          }
        }
      >
        Send Test Email
      </button>

      <a href="/fileimport">
      <button>
        Next
      </button>
      </a>
    </div>
  )
}

export default startpage

Rust后端代码

use lettre::message::header::ContentType;
use lettre::transport::smtp::authentication::Credentials;
use lettre::{
  Message, SmtpTransport, Transport,
};

use serde::{Serialize, Deserialize};

async fn take2_send_test_email_with_login_info(invoke_message: String, invoke_message2: String){
  
  println!("Take 2 Send Test email");
  let login_info = EmailLoginInfo { 
    email_string: String::from(invoke_message), 
    password_string: String::from(invoke_message2)
  };

  let serialized = serde_json::to_string(&login_info).unwrap();
  let deserialized: EmailLoginInfo = serde_json::from_str(&serialized).unwrap();

  let deserialized_email = deserialized.email_string;
  let deserialized_password = deserialized.password_string;

  let email = Message::builder()
  .from("Test Outlook From <testOutlookFrom@example.com>".parse().unwrap())
  .to("Test Outlook From <testOutlookTo@example.com>".parse().unwrap())
  .subject("Rust")
  .header(ContentType::TEXT_PLAIN)
  .body(String::from("Test"))
  .unwrap();

  let creds = Credentials::new(deserialized_email.to_owned(),deserialized_password.to_owned());
  //What works:  
  //let creds = Credentials::new("smtpEmail".to_owned(),"smtpPassword".to_owned());
  // Open a remote connection to Outlook servers
  let mailer = SmtpTransport::starttls_relay("smtp.office365.com")
    .unwrap()
    .credentials(creds)
    .build();

  // Send the email
  match mailer.send(&email) {
    Ok(_) => println!("Email sent successfully!"),
    Err(e) => println!("Could not send email: {e:?}"),
  }
}

报错信息

Could not send email: lettre::transport::smtp::Error { kind: Permanent(Code { severity: PermanentNegativeCompletion, category: Unspecified3, detail: Five }), source: "5.7.3 Authentication unsuccessful [MW4PR04CA0316.namprd04.prod.outlook.com 2023-09-11T15:05:52.851Z 08DBB23E809652C4]" }

解决方法

问题根源

前端使用JSON.stringify()处理邮箱和密码,导致传递给后端的字符串包含额外双引号(比如用户输入abc@outlook.com,传递后变成"abc@outlook.com"),后端直接用该字符串作为凭证,自然认证失败。

修改步骤

  1. 前端移除不必要的JSON序列化
    直接将原始输入值传给Tauri的invoke,Tauri会自动处理参数序列化,无需手动调用JSON.stringify:

    function loginOutlook() {
      // 去掉JSON.stringify,直接传递原始输入值
      invoke('take2_send_test_email_with_login_info', {invoke_message: email, invoke_message2: password});
    }
    
  2. 后端简化代码,移除冗余序列化反序列化
    直接使用传入的字符串作为凭证,跳过无意义的序列化循环:

    // 补上之前代码遗漏的结构体定义
    #[derive(Serialize, Deserialize)]
    struct EmailLoginInfo {
        email_string: String,
        password_string: String,
    }
    
    async fn take2_send_test_email_with_login_info(invoke_message: String, invoke_message2: String){
      println!("Take 2 Send Test email");
      // 直接使用传入的参数创建凭证
      let creds = Credentials::new(invoke_message, invoke_message2);
    
      let email = Message::builder()
      .from("Test Outlook From <testOutlookFrom@example.com>".parse().unwrap())
      .to("Test Outlook From <testOutlookTo@example.com>".parse().unwrap())
      .subject("Rust")
      .header(ContentType::TEXT_PLAIN)
      .body(String::from("Test"))
      .unwrap();
    
      let mailer = SmtpTransport::starttls_relay("smtp.office365.com")
        .unwrap()
        .credentials(creds)
        .build();
    
      match mailer.send(&email) {
        Ok(_) => println!("Email sent successfully!"),
        Err(e) => println!("Could not send email: {e:?}"),
      }
    }
    

可选优化:用结构体统一接收参数

为代码可读性,可让Tauri直接将参数反序列化为结构体:

  1. 前端传递对象:
    function loginOutlook() {
      invoke('take2_send_test_email_with_login_info', {
        login_info: { email: email, password: password }
      });
    }
    
  2. 后端修改函数参数:
    #[derive(Serialize, Deserialize)]
    struct LoginInfo {
        email: String,
        password: String,
    }
    
    async fn take2_send_test_email_with_login_info(login_info: LoginInfo){
      let creds = Credentials::new(login_info.email, login_info.password);
      // 剩余代码不变
    }
    

内容的提问来源于stack exchange,提问作者dhiltzman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 07:00:22