最近我正在做一个Android实验,我想使用SMTP服务器通过android应用程序通过身份验证和加密来发送电子邮件。
好吧, 我发现Android上的javax.mail并不是一个很好的选择 ,因为它取决于awt类(我猜是传统); 有些人试图对其进行调整,以使您不需要整个awt软件包 ,但我在此方面收效甚微。 更不用说那些人几年前自己重构了Android的javax.mail,没有任何维护。
我想到的另一个选择是重新使用Apache Commons Net : 由于该社区向原始SMTP客户端添加了SMTPSClient和AuthenticatingSMTPClient ( 并为SSL和身份验证应用了我的一点补丁 ),因此您可以将该库嵌入Android应用程序(无需传递依赖项)以在安全层上使用身份验证发送邮件。 ( 这篇文章实际上激发了我的灵感 ,但是它使用的是Apache Commons Net的旧版本,而使用3.3则不再需要这样做)
SMTP身份验证和具有Commons Net的STARTTLS
通常,用于此问题的端口是25或备用587端口:您通过普通连接连接到SMTP服务器,要求提供可用的命令,如果支持STARTTLS,则使用它,其余的通信将被加密。
让我们以gmail为例,因为smtp.gmail.com支持身份验证和STARTTLS
public void sendEmail() throws Exception {
String hostname = "smtp.gmail.com";
int port = 587;
String password = "gmailpassword";
String login = "account@gmail.com";
String from = login;
String subject = "subject" ;
String text = "message";
AuthenticatingSMTPClient client = new AuthenticatingSMTPClient();
try {
String to = "recipient@email.com";
// optionally set a timeout to have a faster feedback on errors
client.setDefaultTimeout(10 * 1000);
// you connect to the SMTP server
client.connect(hostname, port);
// you say ehlo and you specify the host you are connecting from, could be anything
client.ehlo("localhost");
// if your host accepts STARTTLS, we're good everything will be encrypted, otherwise we're done here
if (client.execTLS()) {
client.auth(AuthenticatingSMTPClient.AUTH_METHOD.LOGIN, login, password);
checkReply(client);
client.setSender(from);
checkReply(client);
client.addRecipient(to);
checkReply(client);
Writer writer = client.sendMessageData();
if (writer != null) {
SimpleSMTPHeader header = new SimpleSMTPHeader(from, to, subject);
writer.write(header.toString());
writer.write(text);
writer.close();
if(!client.completePendingCommand()) {// failure
throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString());
}
} else {
throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString());
}
} else {
throw new Exception("STARTTLS was not accepted "+ client.getReply() + client.getReplyString());
}
} catch (Exception e) {
throw e;
} finally {
client.logout();
client.disconnect();
}
}
private static void checkReply(SMTPClient sc) throws Exception {
if (SMTPReply.isNegativeTransient(sc.getReplyCode())) {
throw new Exception("Transient SMTP error " + sc.getReply() + sc.getReplyString());
} else if (SMTPReply.isNegativePermanent(sc.getReplyCode())) {
throw new Exception("Permanent SMTP error " + sc.getReply() + sc.getReplyString());
}
这里没有什么可添加的,当然,如果您使用自己的异常类,则可以优化异常处理。
带有Commons Net的SMTP身份验证和SSL
某些SMTP服务器配置为仅接受“ a到z SSL”:您必须在向服务器发出任何命令之前确保通信的安全; 通常使用的端口是465。
让我们以LaPoste.net示例(法语帖子提供的免费电子邮件帐户)为例:
public void sendEmail() throws Exception {
String hostname = "smtp.laposte.net";
int port = 465;
String password = "password";
String login = "firstname.lastname";
String from = login + "@laposte.net";
String subject = "subject" ;
String text = "message";
// this is the important part : you tell your client to connect using SSL right away
AuthenticatingSMTPClient client = new AuthenticatingSMTPClient("TLS",true);
try {
String to = "anthony.dahanne@gmail.com";
// optionally set a timeout to have a faster feedback on errors
client.setDefaultTimeout(10 * 1000);
client.connect(hostname, port);
client.ehlo("localhost");
client.auth(AuthenticatingSMTPClient.AUTH_METHOD.LOGIN, login, password);
checkReply(client);
client.setSender(from);
checkReply(client);
client.addRecipient(to);
checkReply(client);
Writer writer = client.sendMessageData();
if (writer != null) {
SimpleSMTPHeader header = new SimpleSMTPHeader(from, to, subject);
writer.write(header.toString());
writer.write(text);
writer.close();
if(!client.completePendingCommand()) {// failure
throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString());
}
} else {
throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString());
}
} catch (Exception e) {
throw e;
} finally {
client.logout();
client.disconnect();
}
我在这里没有重复checkReply()方法,因为两个代码段都相同。 您会注意到,立即使用SSL意味着您不必检查execTls()响应(实际上,如果这样做,它将无法正常工作)。
包起来
就是这样 如果要使这些示例在您的环境中工作,则可以将apache commons net 3.3 jar添加到您的类路径中
如果您使用的是Maven,请添加依赖项:
<dependency>
<groupid>commons-net</groupid>
<artifactid>commons-net</artifactid>
<version>3.3</version>
</dependency>
如果您将Gradle用于Android项目,则还可以使用以下build.gradle文件:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.4.2'
}
}
apply plugin: 'android'
repositories {
mavenCentral()
}
dependencies {
compile fileTree(dir: 'libs', include: '*.jar'), 'commons-net:commons-net:3.3'
}
android {
compileSdkVersion 17
buildToolsVersion "17.0.0"
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
instrumentTest.setRoot('tests')
}
}
请享用 !