关于javamail的一些笔记
1.为什么要学习javamail?
企业中系统 经常需要信息通知 和 信息校验2.利用telnet实现邮件收发
注册sina和sohu账户yuyang94895@sina.com
yuyang94895@sohu.com
密码:1qaz2wsx
将用户名和密码加密,加密使用Base64Util 类
package cn.itcast.utils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import sun.misc.BASE64Encoder; public class Base64Util { public static void main(String args[]) throws IOException { System.out.print("请输入用户名:"); BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); String userName = in.readLine(); System.out.print("请输入密码:"); String password = in.readLine(); BASE64Encoder encoder = new BASE64Encoder(); System.out.println("编码后的用户名为:" + encoder.encode(userName.getBytes())); System.out.println("编码后的密码为:" + encoder.encode(password.getBytes())); } }
编码后的用户名为:eXV5YW5nOTQ4OTU=
编码后的密码为:MXFhejJ3c3g=
使用telnet的流程
打开cmd,使用telnet命令 smtp.sina.comehlo xxx
auth login
eXV5YW5nOTQ4OTU=
MXFhejJ3c3g=
mail from:<yuyang94895@sina.com>
rcpt to:<yuyang94895@sina.com>
data
一封测试邮件。
.
quit
发送邮件服务 smtp.sina.com
邮件正文需要一定格式的?RFC822要求 标准格式邮件
ehlo xxx
auth login
eXV5YW5nOTQ4OTU=
MXFhejJ3c3g=
mail from:<yuyang94895@sina.com>
rcpt to:<yuyang94895@sohu.com>
data
from:<yuyang94895@sina.com>
to:<yuyang94895@sohu.com>
subject:测试邮件
一封测试邮件。
.
quit
----------------------------------------------------------
收邮件 pop3协议格式 telnet pop3.sohu.com
user 用户名
pass 密码
stat (查看所有邮件总和信息)
list 邮件编号 (查看某封邮件大小)
retr 邮件编号 (查看邮件内容)
企业实际中,通常需要使用收邮件客户端 收取邮件 outlook 和 foxmail
区分:smtp、pop3 、RFC822 、MIME
smtp、pop3 ----- 邮件传输协议
RFC822、MIME ----- 邮件正文格式
-----------------------------------------------------------
RFC822 漏洞
mail from:<yuyang94895@sina.com> 真正发件人
from:<yuyang94895@sina.com> 仅是正文中,显示发件人信息
篡改from字段,冒充别人发邮件
ehlo xxx
auth login
YWFh
MTIz
mail from:<aaa@estore.com>
rcpt to:<bbb@estore.com>
data
from:<hr@google.com>
to:<bbb@estore.com>
subject:入职通知
您已被google录取,年薪30万,下周一(2012.3.12) 正式入职。
.
quit
------------------------------------------------------------
通过nslookup
set type=mxsina.com
查询所有新浪mx记录 ----- 这些邮件服务器不需要用户名和密码校验
freemx3.sinamail.sina.com.cn
ehlo xxx
mail from:<hr@google.com>
rcpt to:<yuyang94895@sina.com>
data
from:<hr@google.com>
to:<yuyang94895@sina.com>
subject:测试邮件2
一封测试邮件2。
.
quit
----------------------------- 结果失败!!!
MX记录和A记录的作用:
1. A记录:WEB服务器的IP指向(IP是固定的,怎么样都不变,域名是变化的)
A (Address) 记录是用来指定主机名(或域名)对应的IP地址记录。
2.MX记录(Mail Exchange):邮件路由记录
这个大家都明白了吗?就是将你的域名中邮件服务器分开,将它设置到其它的IP去!
比如同样是 myweb.com ,如果你设置A记录是指向123.12.123.123,而MX记录你设置是指向222.22.222.222,那么你的DNS服务器接收到别人的邮件路 由请求时就将会将它的请求解释到222.22.222.222上去!而别人访问你的网页的时候仍然是访问123.12.123.123。
JavaMail
MIME协议描述一封邮件
ContentType:text/html(html网页) ------ content-type:text/plain(纯文本)
邮件正文
这是一张图片<img src="cid:mypic" /> , 该图片是xxx
--------------------------------------------------------------
Content-Type:image/jpeg
Content-Disposition: inline
Content-id:mypic
邮件图片
--------------------------------------------------------------
Content-Disposition: attachment
附件
--------------------------------------------------------------
content-id为每个部分起一个唯一标识 ----------------------- 用于引用
Content-Type: 指定数据类型
Content-Disposition: 指定数据阅读处理方式:inline attachment
JavaMail API
邮件是用 Message 来表示的
MIME是不是一个复杂邮件,有很多部分组成 每一个部分就是一个part
BodyPart
Message
Multipart
java.lang.NoClassDefFoundError: com/sun/mail/util/LineInputStream 异常 解决?
主要原因是:
javax.mail和javax.activation这两个包已经在javaEE5当中属于基础包了,就是JDK中自带了已经,但是里面的方法与现在外面的mail.jar和activation.jar有一些出入,所以初学者在直接copy别人代码的时候往往会出现上面的错误。
废话不多说下面是解决方法
进到
X:/Program Files/MyEclipse 6.5/myeclipse/eclipse/plugins/com.genuitec.eclipse.j2eedt.core_6.5.0.zmyeclipse650200806/data/libraryset/EE_5
这个路径里,可以看到javaee.jar,用rar把这个文件打开,然后进到javax文件夹里,删除mail.jar和activation.jar(我的javaee.jar里,这两个东西是文件夹,总之删掉就OK,不过要注意备份一下)
删掉之后运行下面的代码,经行简单的修改以后就可以实现接收邮件的功能了!
依赖jar包中 javamail jaf(java activation framework) ---- javamail 依赖jaf
JDK6.0开始 引入jaf
** 如果使用JDK5.0 包括之前版本,使用javamail时 需要手动引入jaf
复杂邮件内容直接关系
邮件中一块数据 ------ BodyPart
当两块数据合并时 ----- MultiPart(并不表示是一块数据) ------ BodyPart
Message代表整封邮件,Message相当于一个大的BodyPart
---------------------------------------------------------------------------------
JavaMail设计邮件案例
1、编写最简单邮件(邮件内容是文本内容) from to subject ---- setText2、编写复杂邮件 原理 :每个页面中的部分 对应 MimeBodyPart
当两个BodyPary 合并到一起时 ,需要MultiPart ----- setSubType(设置邮件部分之间组合方式)
3、编写带有嵌入图片邮件
* 为图片BodyPart 设置Content-id, 在邮件正文中 通过cid引用
4、编写带有附件邮件
* 在建立附件时,设置附件名称(注意中文转码 MimeUtility )
乱码问题:
1、正文中乱码 text/html;charset=utf-8;2、附件名乱码 MimeUtility.encodeText
如 attachment.setFileName(MimeUtility.encodeText("因为爱情.mp3"));
如何书写复杂邮件:
@Test public void demo() throws AddressException, MessagingException, FileNotFoundException, IOException { // 编写 嵌入图片 携带附件 邮件 // 包含附件的邮件 Properties props = new Properties();// 发送邮件、接收邮件 Session session = Session.getDefaultInstance(props); // 编写嵌入图片的邮件 Message message = new MimeMessage(session); // 所有邮件必须信息 message.setFrom(new InternetAddress("aaa@estore.com")); message.setRecipient(RecipientType.TO, new InternetAddress( "bbb@estore.com")); message.setSubject("这是一个包含附件和嵌入图片的邮件"); // 编写邮件正文 MimeBodyPart content = new MimeBodyPart(); content.setContent("该邮件既有图片 又有附件!图片<img src='cid:mypic'/>", "text/html;charset=utf-8"); MimeBodyPart pic = new MimeBodyPart(); DataHandler dh1 = new DataHandler(new FileDataSource("mm.jpg")); pic.setDataHandler(dh1); pic.setContentID("mypic"); MimeBodyPart attachment = new MimeBodyPart(); DataHandler dh2 = new DataHandler(new FileDataSource("love.mp3")); attachment.setDataHandler(dh2); attachment.setFileName(MimeUtility.encodeText("因为爱情.mp3")); // 整合 MimeMultipart m1 = new MimeMultipart(); m1.addBodyPart(content); m1.addBodyPart(pic); m1.setSubType("related"); MimeBodyPart temp = new MimeBodyPart(); temp.setContent(m1); // 继续整合 MimeMultipart m2 = new MimeMultipart(); m2.addBodyPart(temp); m2.addBodyPart(attachment); m2.setSubType("mixed"); message.setContent(m2); message.writeTo(new FileOutputStream("c:\\测试用复杂邮件.eml")); }
------------------------------------------------------------------------------------
真实案例:注册后激活邮件案例
注册功能、登陆功能激活邮件原理是什么?
原理:在用户注册的时候,保存用户信息到数据库 同时,在数据库里面添加两个字段 一个字段是否激活、另一个字段存放激活码,激活过期时间
设计数据表
id username password email active activeCode expiresActionDate
编写登陆页面(login.jsp),注册页面(regist.jsp),注册成功页面(regist_success.jsp)(略)
vo类中将上述所有属性建立set get方法
registServlet的编写方法
package cn.servlet; import java.io.IOException; import java.sql.SQLException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.dbutils.QueryRunner; import cn.itcast.mail.SendMail; import cn.itcast.utils.JDBCUtils; import cn.itcast.utils.UUIDUtils; import cn.itcast.vo.User; public class RegistServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); // 获得客户端数据 String username = request.getParameter("username"); String password = request.getParameter("password"); String email = request.getParameter("email"); String activeCode = UUIDUtils.generatedActiveCode(); long now = System.currentTimeMillis(); long expires = now + 1000 * 60 * 60 * 24; // datetime yyyy-MM-dd hh:mm:ss DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String expiresActiveDate = dateFormat.format(new Date(expires)); // 编写DBUtils存储程序 String sql = "insert into user values(null,?,?,?,?,?,?)"; // 0 表示未激活 1表示激活 Object[] param = { username, password, email, "0", activeCode, expiresActiveDate }; QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource()); try { queryRunner.update(sql, param); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } // 发送激活邮件,为了不让用户等待,开启一个独立线程发送 User user = new User(); user.setUsername(username); user.setPassword(password); user.setExpiresActiveDate(expiresActiveDate); user.setActiveCode(activeCode); user.setEmail(email); // 开启独立线程(防止用户等待时间过长) new Thread(new SendMail(user)).start(); // 发邮件 request.getRequestDispatcher("/regist_success.jsp").forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
然后编写自动发送激活邮件的程序代码:
package cn..mail; import java.util.Properties; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.Message.RecipientType; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import cn.itcast.vo.User; public class SendMail implements Runnable { private User user; public SendMail(User user) { this.user = user; } @Override public void run() { // 建立Session Properties properties = new Properties(); properties.setProperty("mail.smtp.host", "localhost"); properties.setProperty("mail.transport.protocol", "smtp"); Session session = Session.getDefaultInstance(properties); // 编写Message Message message = new MimeMessage(session); try { // 设置发件人 message.setFrom(new InternetAddress("aaa@estore.com")); // 设置收件人 message.setRecipient(RecipientType.TO, new InternetAddress(user .getEmail())); // 设置邮件主题 message.setSubject("estore商城激活邮件"); // 设置内容 message .setContent( "<h4>欢迎使用estore商城系统,您的用户名是" + user.getUsername() + ",您的密码是" + user.getPassword() + ",请妥善保管。</h4><h4>请于" + user.getExpiresActiveDate() + "之前点击下面链接激活账号,否则账号将被删除!</h4><h3><a href='http://localhost/day20_mail/active?activeCode=" + user.getActiveCode() + "'>http://localhost/day20_mail/active?activeCode=" + user.getActiveCode() + "</a></h3>", "text/html;charset=utf-8"); // 通过Transport发送 Transport transport = session.getTransport(); transport.connect("aaa", "123"); transport.sendMessage(message, message.getAllRecipients()); } catch (AddressException e) { e.printStackTrace(); } catch (MessagingException e) { e.printStackTrace(); } } }
编写登陆的LoginServlet
package cn.servlet; import java.io.IOException; import java.sql.SQLException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import cn.itcast.utils.JDBCUtils; import cn.itcast.vo.User; public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); String sql = "select * from user where username=? and password = ?"; QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource()); try { User user = queryRunner.query(sql, new BeanHandler<User>(User.class), new Object[] { username, password }); if (user == null) { // 登陆失败 request.getRequestDispatcher("/login.jsp").forward(request, response); } else { // 判断账号是否激活 if (user.getActive().equals("0")) { response.getWriter().println("对不起,账户未激活,请赶快查收邮件激活!"); } else { response.getWriter().println("用户登陆成功!"); } } } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
编写激活邮件激活账号(操作数据库)代码
package cn.servlet; import java.io.IOException; import java.sql.SQLException; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import cn.itcast.utils.JDBCUtils; import cn.itcast.vo.User; public class ActiveServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获得激活码 String activeCode = request.getParameter("activeCode"); response.setContentType("text/html;charset=utf-8"); // 到数据库查找激活码对应数据 String sql = "select * from user where activeCode = ?"; QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource()); try { User user = queryRunner.query(sql, new BeanHandler<User>(User.class), activeCode); if (user == null) { // 激活码无效 response.getWriter().println("您输入的激活码无效,可能因为账号过期 未激活,系统已经删除!"); } else { // 激活码有效 // 判断是否过期 String date1 = user.getExpiresActiveDate(); DateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss"); long expires = dateFormat.parse(date1).getTime(); long now = System.currentTimeMillis(); if (expires > now) { // 没过期 // 操作数据库 String updateSql = "update user set active='1' where id=?"; queryRunner.update(updateSql, user.getId()); response.getWriter().println("账号激活成功!"); } else { // 激活过期 response.getWriter().println("激活时间已过期,请重新注册!"); } } } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (ParseException e) { e.printStackTrace(); throw new RuntimeException(e); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
编写用Base64Util加密的UUID类(激活邮件时使用)
package cn.utils; import java.util.UUID; import sun.misc.BASE64Encoder; public class UUIDUtils { public static String generatedActiveCode() { UUID uuid = UUID.randomUUID(); BASE64Encoder base64Encoder = new BASE64Encoder(); return base64Encoder.encode(uuid.toString().getBytes());//加密UUID的代码 } public static void main(String[] args) { System.out.println(UUIDUtils.generatedActiveCode()); } }
上述代码即为编写激活邮件的后端实现
JavaMail技术很多服务器都是支持的 ---- Tomcat(内置javamail使用方法)
配置<Context>元素,配置JavaMail服务(3种方法)
1、配置server.xml <Host>元素中配置Context元素 ---- 对当前工程有效
2、配置context.xml 配置Tomcat连接池 ---- 对于tomcat服务器中所有web工程都有效
3、在web工程下META-INFO/context.xml ---- 对当前工程有效
java类加载器机制
同一个类 被不同类加载器分别加载,会认为不是一个类
*同过tomcat配置JavaMail的Session 通过JNDI方式访问 -------- 类加载器问题
package cn.servlet; import java.io.IOException; import javax.mail.Message; import javax.mail.Session; import javax.mail.Transport; import javax.mail.Message.RecipientType; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import javax.naming.Context; import javax.naming.InitialContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class JNDIMailServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // 获得session Context initCtx = new InitialContext(); Context envCtx = (Context) initCtx.lookup("java:comp/env"); Session session = (Session) envCtx.lookup("mail/Session"); // 编写最简单邮件 Message message = new MimeMessage(session); message.setFrom(new InternetAddress("aaa@estore.com")); message.setRecipient(RecipientType.TO, new InternetAddress( "bbb@estore.com")); message.setSubject("JNDI测试邮件"); message.setText("JDNI测试内容"); Transport transport = session.getTransport(); transport.connect("aaa", "123"); transport.sendMessage(message, message.getAllRecipients()); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }