该项目使用的是SSM框架,由于是单表查询,业务逻辑比较简单,因此只配上Controller层
1.POM.xml中导入javaxmail需要的依赖
<dependency >
<groupId >com.sun.mail </groupId >
<artifactId >javax.mail </artifactId >
<version >1.5.4 </version >
</dependency >
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
2.编辑regist.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>注册</title>
</head>
<body>
<form action="/email/regist" method="post">
<table>
<tr><td>用户名:</td><td><input type="text" name="username"/></td></tr>
<tr><td>密码:</td><td><input type="password" name="password"/></td></tr>
<tr><td>邮箱:</td><td><input type="text" name="email"/></td></tr>
<tr><td colspan="2"><input type="submit" value="注册"/></td></tr>
</table>
</form>
</body>
</html>
package pojo;
public class User {
private int id;
private String username;
private String password;
private String email;
private boolean activated; //账号状态
private String codeUrl; //激活链接中的随机码
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public boolean isActivated() {
return activated;
}
public void setActivated(boolean activated) {
this.activated = activated;
}
public String getCodeUrl() {
return codeUrl;
}
public void setCodeUrl(String codeUrl) {
this.codeUrl = codeUrl;
}
}
package controller;
import java.io.IOException;
import java.util.UUID;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.User;
import service.UserService;
import util.EmailUtils;
import util.GenerateLinkUtils;
@Controller
@RequestMapping("/email/")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/toregist")
public String toemail() {
return "regist";
}
@RequestMapping("/regist")
public void regist(User user,HttpSession httpSession,HttpServletResponse response) {
user.setActivated(false); //刚注册默认是没有激活状态
user.setCodeUrl(UUID.randomUUID().toString());
//注册用户
if(userService.findUserByEmail(user.getEmail()) == null) {
userService.saveUser(user);
} else {
throw new RuntimeException("该邮箱已注册");
}
//查看是否注册成功,为实体类User的id赋值
User findUser = userService.findUserByEmail(user.getEmail());
if(findUser != null) {
user.setId(findUser.getId());
} else {
throw new RuntimeException("注册用户失败");
}
//注册成功后,发送账户激活链接
httpSession.setAttribute("user", user);
EmailUtils.sendAccountActivateEmail(user);
try {
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("激活邮件已经发送,请注意提醒查收");
} catch (IOException e) {
e.printStackTrace();
}
}
@RequestMapping("/activate")
public void activate(String id,String checkCode,HttpServletResponse response) {
int idInt = Integer.parseInt(id);
//根据用户id查找用户
User user = userService.findUserById(idInt);
//验证无误,状态更改为1,即激活
if(GenerateLinkUtils.verifyCheckcode(user,checkCode)) {
//修改状态
int activated = 1;
userService.updateActivated(activated,idInt);
user.setActivated(true);
try {
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("恭喜,激活成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package util;
import java.util.Date;
import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage.RecipientType;
import pojo.User;
public class EmailUtils {
private static final String FROM = "xxx@163.com";
public static void sendAccountActivateEmail(User user) {
Session session = getSession();
MimeMessage message = new MimeMessage(session);
try {
message.setSubject("这是一封激活账号的邮件");
message.setSentDate(new Date());
//setFrom 表示用哪个邮箱发送邮件
message.setFrom(new InternetAddress(FROM));
/**
* RecipientType.TO||BCC||CC
* TO表示主要接收人
* BCC表示秘密抄送人
* CC表示抄送人
* InternetAddress 接收者的邮箱地址
*/
message.setRecipient(RecipientType.TO, new InternetAddress(user.getEmail()));
/*message.setContent("<a target='_BLANK' href='"+GenerateLinkUtils.generateActivateLink(user)+"'>"+user.getUsername()+"先生/女士您好,请点击此链接激活账号"+GenerateLinkUtils.generateActivateLink(user)
+"</a>","text/html;charset=utf-8");*/
message.setContent("<a target='_BLANK' href='http://www.baidu.com'>"+user.getUsername()+"先生/女士您好,请点击此链接激活账号"+GenerateLinkUtils.generateActivateLink(user)
+"</a>","text/html;charset=utf-8");
Transport.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
}
public static Session getSession() {
Properties props = new Properties();
props.setProperty("mail.transport.protocol", "smtp");//指定发送的邮箱的邮箱协议
props.setProperty("mail.smtp.host","smtp.163.com");//指定SMTP服务器
props.setProperty("mail.smtp.port", "25"); //smtp是发信邮件服务器,端口是25
props.setProperty("mail.smtp.auth","true");//指定是否需要SMTP验证
Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(FROM, "此处为客户端授权密码");
}
});
return session;
}
}
6.编辑GenerateLinkUtils工具类
package util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import pojo.User;
public class GenerateLinkUtils {
private static final String CHECK_CODE = "checkCode";
public static String generateActivateLink(User user) {
return "http://localhost:8091/email/activate?id="+user.getId()+"&"+CHECK_CODE+"="+generateCheckcode(user);
}
/**
* 生成校验码,用户名+UUID唯一标识符,为安全把他们加密发送
* @param user
* @return
*/
private static String generateCheckcode(User user) {
String userName = user.getUsername();
String randomCode = user.getCodeUrl();
return md5(userName+":"+randomCode);
}
private static String md5(String string) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("md5");//返回实现指定摘要算法的 MessageDigest 对象。
md.update(string.getBytes());//使用指定的 byte 数组更新摘要。
byte[] md5Bytes = md.digest();//通过执行诸如填充之类的最终操作完成哈希计算。在调用此方法之后,摘要被重置。
return bytes2Hex(md5Bytes);//本信息摘要
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
System.out.println("md5这里出错了");
}
return null;
}
//二行制转字符串
private static String bytes2Hex(byte[] byteArray) {
StringBuffer strBuf = new StringBuffer();
for (int i = 0; i < byteArray.length; i++) {
if(byteArray[i] >= 0 && byteArray[i] < 16) {
strBuf.append("0");
}
strBuf.append(Integer.toHexString(byteArray[i] & 0xFF));
}
return strBuf.toString();
}
public static boolean verifyCheckcode(User user, String checkCode) {
boolean flag = generateCheckcode(user).equals(checkCode);
return flag;
}
/**
* 补充:
* 为了显示一个byte型的单字节十六进制(两位十六进制表示)的编码,请使用:
* Integer.toHexString((byteVar & 0x000000FF) | 0xFFFFFF00).substring(6);
* byteVar & 0x000000FF的作用是,如果byteVar 是负数,则会清除前面24个零,正的byte整型不受影响。
* (...) | 0xFFFFFF00的作用是,如果byteVar 是正数,则置前24位为一,
* 这样toHexString输出一个小于等于15的byte整型的十六进制时,倒数第二位为零且不会被丢弃,
* 这样可以通过substring方法进行截取最后两位即可。
*/
}
7.出现异常(详细原因之前有说过)
(1)java.lang.NoClassDefFoundError: com/sun/mail/util/MailLogger
此异常为导的依赖不对的原因
(2)javax.mail.AuthenticationFailedException: 550 User has no permission
此异常是因为邮箱没有开启客户端授权密码
(3)javax.mail.AuthenticationFailedException: 535 Error: authentication failed
此异常表示认证为通过,通常原因为密码设置的邮箱登录密码,应当改成客户端授权密码