摘自:http://txdz.blog.163.com/blog/static/6252249200611321327459/
2003年4月发表于《现代计算机》杂志(专业版,总第159期)
基于INDY控件实现SMTP身份验证
唐**
[摘要] 本文介绍了在C++ Builder 6.0中,如何利用Indy的TIdSMTP控件实现SMTP服务器的身份验证。
[关键字] Indy;TIdSMTP控件;身份验证
Authenticating Base on Indy Components
Tang Xiaodong 1, Kang Li 1, Wang Zhenghua 2
(1.China Aerodynamics Research and Development Center, Mianyang, Sichuan 621000, China)
(2. Dept. Of Computer, National University of Defense Technology,ChangSha,410073)
Abstract: This paper introduces how to accomplish verifying user’s identification via SMTP server by Indy’s TIdSMTP component in C ++ Builder .
Keywords: Indy; TIdSMTP Components; Authentication
1 Indy概述
现在许多邮件服务器都具有SMTP身份验证的功能,但在C++ Builder 6.0以前的版本中,收发电子邮件是通过TNMSMTP和TNMPOP3两个控件实现的。其中TNMSMTP控件没有提供SMTP服务器身份验证功能,这给制作邮件客户端程序带来不便。
从Borland C++Builder 6.0及DELPHI 6.0开始,Insprise公司开始将Indy控件加入到控件页上。Indy是一套开放源码的Internet组件,可以在Indy的网站http://www.nevrona.com/indy/上下载源代码。Indy以前的名字叫Winshoes,直到8.0后才正式改名为Indy,而且从8.0开始正式支持Linux,目前最新的版本是Indy 10。在C++Builder中Indy 8.0占去了三个控件页:Indy Clients、Indy Servers及Indy Misc。其中的TIdSMTP控件除了遵从Simple Mail Transfer Protocol(RFC 821)和SMTP Service Extensions(RFC 1869),还遵从SMTP Service Extension for Authentication(RFC 2554),故它能实现身份验证与回执请求。它与Borland产品以前的TNMSMTP控件相比差别明显,下面进行详细介绍。
2 SMTP服务器的身份验证
怎样通过需要身份验证的SMTP Server发送电子邮件呢?回答这一问题前,我们来了解一下身份验证的由来。最初的RFC 821制定于1982年,第三方SMTP服务器和网关必须在主机之间转发信息且在源SMTP服务器和目的SMTP服务器间充当中介。由于Internet的开放,必然要冒着地址被滥用来发垃圾邮件的风险。通常,拒绝垃圾邮件有三种办法:第一种是IP绑定,SMTP服务器只接收指定IP地址发来的邮件;第二种是用户先得登录到POP3服务器,然后尽快发邮件。第三种方法是扩展SMTP协议,以便能使用各种不同的SASL(Simple Authentication and Security)机制,确保用户在能发出邮件前已经过身份验证。Indy即采用第三种方式。在Indy 8.0和9.0中,其TIdSMTP控件实现了一种称为LOGIN的SASL机制。TIdSMTP控件与身份验证有关的属性和方法见表1:
属性或方法 | 描 述 |
Host | SMTP服务器名,如smtp.sohu.com |
Port | SMTP所使用端口,默认为25 |
UserId | 邮件账号 |
Password | 邮件密码 |
AuthenticationType | 身份验证类型(atLogin、atNone) |
AuthSchemesSupported | 指示是否支持身份验证及验证方式 |
Authenticate()方法 | 根据账号密码进行身份验证 |
Connect()方法 | 连接SMTP服务器 |
Disconnect()方法 | 断开SMTP服务器 |
Connected()方法 | 判断是否与SMTP服务器连接 |
Disconnected()方法 | 判断是否与SMTP服务器断开 |
Verify(AnsiString uid)方法 | 判断SMTP服务器是否存在某个UserId |
Send(TidMessage msg)方法 | 发送电子邮件 |
表1 TIdSMTP控件的属性与方法
2.1 身份验证的实现
一种方法是,直接通过设置TIdSMTP的UserName 、password属性为用户邮件账号、密码,将AuthenticationType设置为atLogin,然后调用Authenticate方法即可。
IdSMTP1->UserId = “abcd”;// 假定邮件账号是abcd
IdSMTP1->Password = “123456”;// 假定口令是123456
IdSMTP1->AuthenticationType = atLogin;
bool auth;
auth = IdSMTP1->Authenticate(); //验证,anth为true表成功
if(auth) ShowMessage(IdSMTP1->UserId+"身份验证通过!");
else ShowMessage(IdSMTP1->UserId+"身份验证失败!");
另一种情况,如果在没有验证之前就使用Send方法发送邮件时,Send方法将自动调用Authenticate方法,将编码过的UserId和Password发给SMTP服务器。当SMTP服务器支持身份验证时,以下语句可以在发信时自动验证:
IdSMTP1->UserId = SmtpServerUser;
IdSMTP1->Password = SmtpServerPassword;
IdSMTP1->AuthenticationType = atLogin;
IdSMTP1->Send(IdMessage1);
2.2 检测SMTP服务器是否支持身份验证
目前国内许多SMTP服务器均支持身份验证功能,但还有一些服务器没有此项功能。Indy 提供了对SMTP服务器是否提供LOGIN身份验证支持的检测, 当通过Connect()方法与SMTP服务器连接后,由AuthSchemesSupported指示这一特性。如下所示:
IdSMTP1->AuthenticationType = atNone; // 先设置为无身份验证能力
IdSMTP1->Connect(); //连接SMTP Server
if(IdSMTP1->AuthSchemesSupported->IndexOf(“LOGIN”)>-1)
// 若支持身份验证
ShowMessage(IdSMTP1->Host+"支持身份验证!");
else
ShowMessage(IdSMTP1->Host+"不支持身份验证!");
2.3 服务器的账号核实
由Verify方法验证一个账号或别名是SMTP服务器的用户邮箱,不需要知道邮箱密码。这与登录SMTP服务器时的验证是不同的。需要说明的是,这一方法在Indy 9.0中才开始提供。其声明为:
function Verify(AUserName: String): String; virtual;
如果成功,Verify方法将返回完整的用户标识名和E-Mail地址。TNMSMTP控件的Verify方法也是这个功能,只是返回值是布尔型。
3 身份验证的应用实例
在C++Builder6.0中,创建一个新的工程:加入TIdSMTP控件IdSMTP1、TIdMessage控件实例IdMessage1、4个TBitBtn控件的Caption分别是检查服务器验证能力、身份验证、发信、退出,分别对应检查SMTP服务器是否支持身份验证、根据账号及口令验证身份、发送电子邮件、退出四个模块。窗体设计如图1所示:
下面是范例程序的部分源代码:
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
// 检查SMTP服务器是否支持身份验证功能
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
IdSMTP1->Host = Edit1->Text ;
IdSMTP1->Port = StrToInt ( Edit2->Text );
IdSMTP1->AuthenticationType = atNone; // 先设置为无身份验证能力
if(!IdSMTP1->Connected()) IdSMTP1->Connect(); //连接SMTP Server
try{
if(IdSMTP1->AuthSchemesSupported->IndexOf("LOGIN")>-1)
// 若支持身份验证
{
IdSMTP1->AuthenticationType = atLogin; // 设置身份验证类型
AuthType->ItemIndex = 1;
Edit12->Text = "SMTP Support Authentication";
}
else {
AuthType->ItemIndex = 0;
Edit12->Text = "SMTP No Authentication";
}
}
__finally {
IdSMTP1->Disconnect();
}
}
// 根据账号及口令验证身份
void __fastcall TForm1::BitBtn2Click(TObject *Sender)
{
if(AuthType->ItemIndex==-1) //若没有检查是否支持身份验证
BitBtn1Click(NULL);
IdSMTP1->UserId = Edit5->Text ; //账号
IdSMTP1->Password = Edit6->Text ; //口令
if(AuthType->ItemIndex!=1)
{
IdSMTP1->AuthenticationType = atNone;
ShowMessage("Not Surport Authentication !");
return;
}
IdSMTP1->AuthenticationType = atLogin;
bool auth;
if(IdSMTP1->Connected()) IdSMTP1->Disconnect();
IdSMTP1->Connect(); // 连接SMTP服务器
auth = IdSMTP1->Authenticate(); //验证
if(auth) // auth为true, 表成功
ShowMessage("Authentication Success!");
else
ShowMessage("Authentication Fail!");
IdSMTP1->Disconnect() ;
}
// 发送电子邮件
void __fastcall TForm1::BitBtn3Click(TObject *Sender)
{
if(AuthType->ItemIndex==-1) //若没有检查是否支持身份验证
BitBtn1Click(NULL);
IdSMTP1->UserId = Edit5->Text ; //账号
IdSMTP1->Password = Edit6->Text ; //口令
IdSMTP1->AuthenticationType = atNone;
if(AuthType->ItemIndex==1)
IdSMTP1->AuthenticationType = atLogin;
IdMessage1->From->Text = Edit7->Text; //发件人地址
IdMessage1->Recipients->EMailAddresses = Edit8->Text; //收件人地址
IdMessage1->CCList->EMailAddresses = Edit9->Text; //抄送收件人地址
IdMessage1->BccList->EMailAddresses = Edit10->Text; //暗送收件人地址
IdMessage1->Subject = Edit11->Text; //邮件主题
IdMessage1->Body->Assign(Memo1->Lines); //邮件内容
if(ReturnReciept->Checked) // 设定回执接收者为发件人
IdMessage1->ReceiptRecipient->Text = IdMessage1->From->Text;
else // 表示不需回执
IdMessage1->ReceiptRecipient->Text = "";
if(IdSMTP1->Connected()) IdSMTP1->Disconnect();
IdSMTP1->Connect(); // 连接SMTP服务器
try {
IdSMTP1->Send(IdMessage1); // 发送
} catch(...) { }
ShowMessage("Mail has been sent. ");
IdSMTP1->Disconnect() ;
}
void __fastcall TForm1::BitBtn4Click(TObject *Sender)
{
Close();// 退出范例程序
}
4 结语
需要说明的是,C++ Builder 6.0及DELPHI 6.0才开始提供Indy控件。对于C++ Builder 5.x / DELPHI5.x及以下版本,可以到Indy的网站下载其源代码,自行安装Package。这里就不再赘述。
虽然Indy 8.0所提供的身份验证能力,能满足一般邮件服务器身份验证的需要,但它仅实现了简单的LOGIN验证机制,即以用户名和密码进行验证,不支持CRAM-MD5, DIGEST-MD5, KERBEROS_V4, GSSAPI, SKEY和TLS等。由于SASL LOGIN没有加密机制,现在已有CRAM-MD5和DIGEST-MD5这两类提供较好加密性能的SASL验证机制。Indy 10中也准备提供一个新的Indy SASL控件页,并由TIdSASLList实现这些验证机制。
参考文献:
【1】 http://www.nevrona.com/indy/
【2】 http://www.armware.dk/rfc/
【3】 Borland C++ Builder 5 高级开发技术 李冬、王宏 中国水利出版社 2000.7
【4】 清宏计算机工作室 C++Builder 编程技巧 机械工业出版社 2001.1
没有评论:
发表评论