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);
}
}