Java Web:邮件发送

邮件发送

一、介绍

1、传输协议

  • SMTP协议 (Simple Mail Transfer Protocol)

    1. 属于TCP/IP协议族。
    2. 控制信件的中转方式,帮助每台计算机在发送或中转信件时找到下一个目的地。
    3. SMTP服务器是遵循SMTP协议的发送邮件服务器。
  • POP3协议 (Post Office Protocol - Version 3)

    1. 属于TCP/IP协议族。
    2. 用于远程管理在服务器上的电子邮件,帮助用户登录到邮件服务器上、取邮件、删邮件等。
    3. POP服务器是遵循POP协议的邮件接收服务器。

2、邮件服务器

要在网络上实现邮件功能,要使用专门的邮件服务器。

  • SMTP服务器 :一般是smtp.xxx.com。如163邮箱smtp.163.com,QQ邮箱smtp.qq.com。
  • POP服务器:一般是pop.xxx.com。如163邮箱pop.163.com,QQ邮箱pop.qq.com。

3、原理图

  1. A通过SMTP协议连接到SMTP服务器,发送邮件到网易邮箱的SMTP服务器;
  2. 网易邮箱的SMTP服务器,通过SMTP协议将邮件中转到QQ邮箱的SMTP服务器;
  3. QQ邮箱的SMTP服务器,将接收到的邮件存储在B的邮箱账号bbbb@qq.com的邮箱空间中;
  4. B通过POP3协议连接到POP服务器,请求收取邮件;
  5. POP服务器从B的邮箱账号的邮箱空间中取出邮件;
  6. POP服务器将取出来的邮件给B。

二、Java邮件发送

1、组件

  • JavaMailAPI:mail.jar
  • Java Activation Framework:activation.jar

2、实用类介绍

MailSSLSocketFactory类:设置SSL加密

// SSL加密:QQ邮箱需要设置,通过以下4行代码
MailSSLSocketFactory mssf = new MailSSLSocketFactory();
mssf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", mssf);

Session类

// 获取实例对象
Session getDefaultInstance(Properties props)

Session getDefaultInstance(Properties props, Authenticator authenticator)
    
// 开启Debug模式
void setDebug(boolean debug)
    
// 获取Transport对象
Transport getTransport()

Transport类

// 连接到邮件服务器
void connect(String host, String user, String password)
    
// 发送邮件
void sendMessage(Message var1, Address[] var2)
    
// 关闭连接
void close()

MimePart接口实现类:MimeMessage类

// 设置发件人
void setFrom(Address address)
    
// 设置收件人
void setRecipient(Message.RecipientType type, Address address)
    
// 设置主题
void setSubject(String subject)
    
// 设置内容
void setContent(Object o, String type)

// 保存修改:添加图片、附件时需要
void saveChanges()

复杂邮件还需要以下2个类

MimePart接口实现类:MimeBodyPart类

复杂邮件的组成部分

// 文本:设置内容
void setContent(Object o, String type)

// 图片、附件:设置数据处理
void setDataHandler(DataHandler dh)
    
// 图片:设置CID,在文本中通过src引用
void setContentID(String cid)

// 附件:设置附件名
void setFileName(String filename)

MimePart接口实现类:MimeMultiPart类

组合多个代表MIME消息的MimeBodyPart对象。

// 添加MIME消息
void addBodyPart(BodyPart part)
    
// 描述数据关系:alternative/related/mixed
void setSubType(String subtype)

图示

3、实现步骤

普通邮件和复杂邮件的实现步骤相同,区别是邮件内容的不同

  • 简单邮件:纯文本邮件,不含附件和图片;

  • 复杂邮件:非纯文本邮件,包含附件和图片。

步骤

  1. 创建Session对象:
    • 获取实例时,需要传入Properties参数;
    • 用于定义邮件服务器所需的网络连接信息,如主机名、邮件协议、授权码等;
    • 可以开启Debug模式,查看调试信息。
  2. 获得Transport对象:
    • 通过Session获取;
    • 连接到邮件服务器,需要传入端口号、发件人、授权码;
    • 用于发送邮件。
  3. 创建邮件:
    • MimeMessage,需要传入Session(拿到Session里的配置信息);
    • 设置发件人、收件人、主题;
    • 设置内容 (普通邮件和复杂邮件的区别)
  4. 发送Message,关闭连接:
    • 发送时需要传入Message对象、收件人。

三、Java实现

以QQ邮箱为例

1、开启邮箱服务

得到授权码

2、代码实现

SendMail方法

要使用不同的邮箱服务器、邮箱账号等,只需修改参数即可

private void sendMail() throws MessagingException, GeneralSecurityException {

    // 参数设置:根据不同的邮箱服务器和邮箱地址修改
    final String host = "smtp.qq.com"; // 服务器主机号
    final String authCode = "授权码"; // 授权码
    final String addresser = "bbbb@qq.com"; // 发件人邮箱
    final String addressee = "aaaa@126.com"; // 收件人邮箱

    // 配置参数:可以写在配置文件中
    Properties prop = new Properties();
    prop.setProperty("mail.host", host);
    prop.setProperty("mail.transport.protocol", "smtp");// 邮件协议
    prop.setProperty("mail.smtp.auth", "true"); // 权限:验证用户名、授权码
    

    // SSL加密:QQ邮箱需要设置,通过以下4行代码
    MailSSLSocketFactory mssf = new MailSSLSocketFactory();
    mssf.setTrustAllHosts(true);
    prop.put("mail.smtp.ssl.enable", "true");
    prop.put("mail.smtp.ssl.socketFactory", mssf);


    // 1、创建Session对象
    Session session = Session.getDefaultInstance(prop, new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(addresser, authCode);
        }

    });
    // 经测试,使用该构造方法也可以
    // Session session = Session.getDefaultInstance(prop);


    session.setDebug(true); // 开启Debug模式

    // 2、获得Transport对象
    Transport transport = session.getTransport();
    transport.connect(host, addresser, authCode);

    // 3、创建邮件:根据邮件类型调用相应方法(纯文本/内嵌资源/带附件)
    MimeMessage message = makeSimpleMessage(session, addresser, addressee);

    // 4、发送邮件
    transport.sendMessage(message, message.getAllRecipients());

    // 关闭邮件服务器
    transport.close();
}

创建简单邮件

/**
 * 创建简单邮件
 *
 * @param session   Session对象
 * @param addresser 发件人
 * @param addressee 收件人
 * @return 简单邮件
 */
private MimeMessage makeSimpleMessage(Session session, String addresser, String addressee) throws MessagingException {
    MimeMessage message = new MimeMessage(session);

    message.setFrom(new InternetAddress(addresser)); // 发件人
    message.setRecipient(Message.RecipientType.TO, new InternetAddress(addressee)); // 收件人
    message.setSubject("Java实现邮件发送:简单邮件"); // 邮件主题
    // 邮件内容
    message.setContent("<h1>MailSend的sendMail方法,发送一封普通邮件</h1>", "text/html;charset=utf-8");

    return message;
}

创建复杂邮件

/**
 * 创建复杂邮件
 * 相比普通邮件增加:图片和附件(需要数据处理,设置CID和附件名)、描述数据关系、设置并保存修改
 *
 * @param session   Session对象
 * @param addresser 发件人
 * @param addressee 收件人
 * @return 复杂邮件
 */
private MimeMessage makeMixedMessage(Session session, String addresser, String addressee) throws MessagingException {
    MimeMessage message = new MimeMessage(session);

    message.setFrom(new InternetAddress(addresser)); // 发件人
    message.setRecipient(Message.RecipientType.TO, new InternetAddress(addressee)); // 收件人
    message.setSubject("Java实现邮件发送:复杂邮件"); // 邮件主题

    // 邮件内容
    // 1、正文
    MimeBodyPart textPart = new MimeBodyPart();
    textPart.setContent("复杂邮件的正文,<img src='cid:Jay004'>附带一张图片", "text/html;charset=utf-8");

    // 2、图片
    MimeBodyPart imgPart = new MimeBodyPart();
    DataHandler dh = new DataHandler(new FileDataSource("src\\main\\resources\\img\\Jay004.jpg"));
    imgPart.setDataHandler(dh); // 需要数据处理
    imgPart.setContentID("Jay004"); // 设置CID

    // 3、附件
    MimeBodyPart attachPart = new MimeBodyPart();
    DataHandler dh1 = new DataHandler(new FileDataSource("src\\main\\resources\\md\\文件上传.md"));
    attachPart.setDataHandler(dh1); // 需要数据处理
    attachPart.setFileName("fileUpload.md"); // 设置附件名,最好包含后缀名

    // 描述数据关系:alternative/related/mixed
    MimeMultipart mm = new MimeMultipart();
    mm.addBodyPart(textPart);
    mm.addBodyPart(imgPart);
    mm.addBodyPart(attachPart);
    mm.setSubType("mixed");

    // 设置到消息中,保存修改
    message.setContent(mm);
    message.saveChanges();

    return message;
}

四、Java Web邮件发送

网站的注册功能中,经常会使用到邮件发送。

邮件的内容可能包含注册的用户名和密码、激活账户的超链接、激活码等信息。

五、Java Web实现

1、前端页面

register.jsp

<form action="register.do" method="post">
    <label>
        账号:<input type="text" name="username"><br>
    </label>
    <label>
        密码:<input type="password" name="password"><br>
    </label>
    <label>
        邮箱:<input type="text" name="mail"><br>
    </label>
    <input type="submit">|<input type="reset">

success.jsp

<h1>${msg}</h1>

2.1、Servlet

public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String mail = req.getParameter("mail");

        User user = new User(username, password, mail);

        // 启动线程来发送邮件,优化注册等待时间
        MailThread mailThread = new MailThread(user);
        mailThread.start(); // start()才是开启多线程,run()只是调用方法

        req.setAttribute("msg","注册成功,稍后会收到一封邮件");
        req.getRequestDispatcher("success.jsp").forward(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

2.2、相关类

实体类:User

需要添加构造方法、Getter和Setter,如需在控制台调试可添加toString()方法。

public class User {
    private String username; // 账户
    private String password; // 密码
    private String mailAddress; // 邮箱地址
}

工具类:MailThread

多线程实现邮件发送的意义:

  • 如果不用多线程,在邮件成功发出后,程序才会继续运行;
  • 用户体验差,等待时间长;
  • 使用多线程优化,在完成注册后程序继续运行(如跳转页面等),邮件发送在单独的线程中完成。
public class MailThread extends Thread {
    final private String host = "smtp.qq.com"; // 服务器主机号
    final private String authCode = "yybjtadqjclodijh"; // 授权码
    final private String addresser = "1666305862@qq.com"; // 发件人邮箱
    final private User user;

    public MailThread(User user) {
        this.user = user;
    }

    @Override
    public void run() {

        try {
            // 配置参数
            Properties prop = new Properties();
            prop.setProperty("mail.host", host);
            prop.setProperty("mail.transport.protocol", "smtp");// 邮件协议
            prop.setProperty("mail.smtp.auth", "true"); // 权限:验证用户名、授权码

            // SSL加密:QQ邮箱需要设置,通过以下4行代码
            MailSSLSocketFactory mssf = null;
            mssf = new MailSSLSocketFactory();
            mssf.setTrustAllHosts(true);
            prop.put("mail.smtp.ssl.enable", "true");
            prop.put("mail.smtp.ssl.socketFactory", mssf);

            // 1、创建Session对象
            Session session = Session.getDefaultInstance(prop, new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(addresser, authCode);
                }
            });

            session.setDebug(true); // 开启Debug模式

            // 2、获得Transport对象
            Transport transport = session.getTransport();
            transport.connect(host, addresser, authCode);


            // 3、创建邮件:调用相应方法(纯文本/内嵌资源/带附件)
            MimeMessage message = new MimeMessage(session);

            message.setFrom(new InternetAddress(addresser)); // 发件人
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getMailAddress())); // 收件人
            message.setSubject("用户注册邮件"); // 邮件主题

            String info = "注册成功,您的用户名:" + user.getUsername() + ",密码:" + user.getPassword();


            // 邮件内容
            message.setContent(info, "text/html;charset=utf-8");

            // 4、发送邮件
            transport.sendMessage(message, new InternetAddress[]{new InternetAddress(user.getMailAddress())});

            // 关闭邮件服务器
            transport.close();
        } catch (GeneralSecurityException | MessagingException e) {
            e.printStackTrace();
        }
    }
}

3、注册Servlet

<servlet>
    <servlet-name>RegisterServlet</servlet-name>
    <servlet-class>indi.jaywee.mail.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>RegisterServlet</servlet-name>
    <url-pattern>/register.do</url-pattern>
</servlet-mapping>
posted @ 2021-07-20 17:28  Jaywee  阅读(275)  评论(0编辑  收藏  举报

👇