在前面的文章中,我们都是把登录邮箱是必需的用户名、密码预先地写在代码中进行编译,不能在邮件发送程序运行时再给定。假如我们修改了邮箱登录密码,那么原来编译好的文件就不能再用了,这样子做的缺点很明显:邮箱登录时的用户身份认证不灵活。
在 JavaMail 中,可以通过 extends Authenticator 抽象类,在子类中覆盖父类中的 getPasswordAuthentication() 方法,就可以实现以不同的方式来进行登录邮箱时的用户身份认证。JavaMail 中的这种设计是使用了策略模式(Strategy),具体的请参看相关文章。
在我们的代码中,我们设计了一种简单的 SimpleAuthenticator 子类通过构造函数传入用户名和密码,而另一种 GUIAuthenticator 子类则使用 GUI 界面的形式进行身份认证,它可以使得程序在运行时弹出输入对话框来让用户提交用户名和密码,具体的请看下面运行图。
代码实现:
- import java.util.Date;
- import java.util.Properties;
- import java.util.StringTokenizer;
- import javax.mail.Authenticator;
- import javax.mail.Transport;
- import javax.mail.Message.RecipientType;
- import javax.mail.PasswordAuthentication;
- import javax.mail.Session;
- import javax.mail.internet.InternetAddress;
- import javax.mail.internet.MimeMessage;
- import javax.swing.JOptionPane;
- // 普通的通过构造函数传入身份验证信息的 Authenticator 子类
- class SimpleAuthenticator extends Authenticator {
- private String user;
- private String pwd;
- public SimpleAuthenticator(String user, String pwd) {
- this.user = user;
- this.pwd = pwd;
- }
- @Override
- protected PasswordAuthentication getPasswordAuthentication() {
- return new PasswordAuthentication(user, pwd);
- }
- }
- // 具有弹出输入对话框的 Authenticator 子类
- class GUIAuthenticator extends Authenticator {
- @Override
- protected PasswordAuthentication getPasswordAuthentication() {
- String user;
- String pwd;
- // 弹出输入对话框
- String result = JOptionPane.showInputDialog("请输入用户名和密码,以','隔开!");
- StringTokenizer st = new StringTokenizer(result, ",");
- user = st.nextToken();
- pwd = st.nextToken();
- return new PasswordAuthentication(user, pwd);
- }
- }
- /**
- * 使用Authenticator子类进行用户身份认证
- * @author haolloyin
- */
- public class UseAuthenticatorSender {
- private String smtpServer = "smtp.126.com";
- private String from = "testhao@126.com";
- private String to = "test_hao@sina.cn";
- private String subject = "使用Authenticator子类进行用户身份认证";
- private String body = "使用Authenticator子类进行用户身份认证的测试!!!";
- public void sendMails(Authenticator auth) throws Exception {
- // 设置协议、是否身份验证、服务器等信息
- Properties props = new Properties();
- props.setProperty("mail.transport.protocol", "smtp");
- props.setProperty("mail.smtp.auth", "true");
- props.setProperty("mail.host", smtpServer);
- // 通过传入的参数获得Authenticator子类对象
- Session session = Session.getInstance(props, auth);
- session.setDebug(true);
- MimeMessage msg = new MimeMessage(session);
- msg.setFrom(new InternetAddress(from));
- msg.setRecipient(RecipientType.BCC, new InternetAddress(to));
- msg.setSubject(subject);
- msg.setSentDate(new Date());
- msg.setText(body);
- msg.saveChanges();
- /*
- * 由于Session对象中注册了Authenticator子类对象,因此可以直接
- * 从该Authenticator子类对象中获取登录的相关信息,故直接使用
- * Transport 抽象类中静态 send() 方法来简化代码编写
- */
- Transport.send(msg);
- }
- // 测试
- public static void main(String[] args) throws Exception {
- UseAuthenticatorSender sender = new UseAuthenticatorSender();
- // 这里体现了使用策略模式的好处,客户端可以选择使用
- // 具体的哪一种身份验证方式来提交信息
- // Authenticator auth = new SimpleAuthenticator("testhao", "123456");
- Authenticator auth = new GUIAuthenticator();
- sender.sendMails(auth);
- }
- }
两种测试结果(只需要在main()方法中注释掉其中的一行则可):
1、使用 SimpleAuthenticator 子类:
2、使用 GUIAuthenticator 子类:
注:程序在运行到用户身份验证时会自动弹出下面的输入对话框,由于上面的代码中的 Session 对象启动了 JavaMail API 调试功能,所以这个现象可以很明显地从控制台中得知。
通过上面使用 Authenticator 的具体子类来实现不同的身份验证方式,我们大概地了解到策略模式的好处,我们还可以在设计一个从网页中或者数据库表中获取登录信息的 Authenticator 子类,而客户端在使用时却可以不受影响。
小结:
Authenticator 类的这种结构设计是设计模式运用在实际应用中的体现,从 JavaMail 的帮助文档中可以看到 Authenticator 类中的其他方法都是 protected final 修饰的,这与策略模式(Strategy)的设计一样。