基于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"),后端直接用该字符串作为凭证,自然认证失败。
修改步骤
前端移除不必要的JSON序列化
直接将原始输入值传给Tauri的invoke,Tauri会自动处理参数序列化,无需手动调用JSON.stringify:function loginOutlook() { // 去掉JSON.stringify,直接传递原始输入值 invoke('take2_send_test_email_with_login_info', {invoke_message: email, invoke_message2: password}); }后端简化代码,移除冗余序列化反序列化
直接使用传入的字符串作为凭证,跳过无意义的序列化循环:// 补上之前代码遗漏的结构体定义 #[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直接将参数反序列化为结构体:
- 前端传递对象:
function loginOutlook() { invoke('take2_send_test_email_with_login_info', { login_info: { email: email, password: password } }); } - 后端修改函数参数:
#[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
相关产品推荐
相关产品推荐

