938
技術社區[雲棲]
JavaMail學習筆記(七)、帳號激活與忘記密碼 實例
一、帳戶激活
在很多時候,在某些網站注冊一個用戶之後,網站會給這個用戶注冊時填寫的email地址發送一封帳戶激活郵件,這封郵件的內容就是一個激活帳戶的鏈接和一段簡短的文字描述,如果用戶沒有去郵箱將帳戶激活,可能在使用網站的某些功能時就會受到限製,比如不能發貼、下載資料、評論等限製。這麼做的原因應該是為了保證帳戶的安全性和郵箱的有效性,以後網站如果有什麼活動、資訊或係統安全通知等,可以在第一時間通知到用戶。比如我在奇藝視頻網站注冊一個帳號之後,它就會往我注冊時填寫的郵箱中發一封帳戶激活的郵件,郵件中有一個激活的鏈接,點擊它就可以將你的帳戶激活。如下圖所示:
這個功能是怎麼做到的呢,其實實現也是很簡單的,說白了就是一個Update操作。
實現思路:當一個用戶注冊成功之後,係統向用戶注冊時提供的email地址發送一封郵件,郵件的內容是一個激活帳戶的鏈接和一段簡短的文字描述,這個鏈接的功能就是將這個帳戶的狀態修改成激活狀態。這個功能的重點在於如何生成這個激活帳戶的鏈接。直接可以聯想到的方法就是建一個Servlet,Servlet接收一個用戶名參數,Servlet拿到用戶名參數之後,到數據庫把這條記錄load出來,做一次update操作即可。但是這樣做會有風險,比如有人想在你的網站上搞破壞,注冊時他填的email地址就是亂填的,這個Email地址可能根本就收不到郵件(但符合email格式),注冊完成之後,然後在瀏覽器地址欄輸入網站之前發送的帳戶激活鏈接,並加上新注冊的用戶名參數,這樣一來就跳過了去郵箱激活的步聚,時間久了,庫中可能會留下許多無效的email地址和帳戶。為了避免這個問題,解決方案是在用戶注冊成功之後,在發送激活帳戶的鏈接中加一個隨機碼,這個隨機碼是將用戶名和一個隨機數用md5加密後的一串16進製字符串,每次用戶在激活帳戶的時候,都驗證這個隨機碼是否和注冊時發送的一致,這樣就可以避免人為的修改激活鏈接達到帳戶激活的功能。
二、忘記密碼
當你在某網站注冊一個帳戶之後,發現這個帳戶很久沒用了,密碼也忘記了,如果還記得之前注冊時填寫的Email,那就可以向你的email中發一個重設密碼的鏈接,就可以重新設置你的密碼。大部份網站一般都提供了根據Email找回密碼的功能。實現此功能的思路:在登錄界麵添加一個“忘記密碼“的鏈接,用戶點擊進去之後,輸入用戶名或Email,係統根據用戶名或Email找出用戶信息,並生成一個重新設置密碼的鏈接發送到用戶的郵箱中(生成鏈接的方式和生成帳戶激活鏈接的方式一樣),用戶在郵箱中點擊重設密碼的鏈接,根據提示進入密碼重設頁麵,重新輸入新的密碼即可。
功能實現步聚(帳號激活):
1、用戶注冊頁麵
<form action="${pageContext.request.contextPath}/register" method="post"> 用戶名:<input type="text" name="userName" value="${param.userName}"><span >${errors.userName}</span><br/> 密碼:<input type="password" name="password" ><span >${errors.password}</span><br/> 確認密碼:<input type="password" name="password2"><span >${errors.password2}</span><br/> email:<input type="text" name="email" value="${param.email}"><span >${errors.email}</span><br/> <input type="submit" value="注冊"> </form>
2、處理注冊請求的Servlet(RegisterServlet)
package org.study.accountactivate.web.servlet; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.study.accountactivate.dao.UserDao; import org.study.accountactivate.dao.impl.UserDaoImpl; import org.study.accountactivate.domail.User; import org.study.accountactivate.util.EmailUtils; /** * 用戶注冊 */ public class RegisterServlet extends HttpServlet { private static final long serialVersionUID = 1L; private UserDao userDao = UserDaoImpl.getInstance(); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String userName = request.getParameter("userName"); String password = request.getParameter("password"); String password2 = request.getParameter("password2"); String email = request.getParameter("email"); Map<String, String> errors = new HashMap<String,String>(); if (userName == null || "".equals(userName)) { errors.put("userName", "用戶名不能為空!"); } else if (userName != null && userDao.findUserByName(userName) != null) { errors.put("userName", "該用戶已注冊!"); } if (password == null || "".equals(password)) { errors.put("password","密碼不能為空!"); } else if (password != null && password.length() < 3) { errors.put("password","密碼長度不能低於3位!"); } if (password2 == null || "".equals(password2)) { errors.put("password2", "確認密碼不能為空!"); } else if (password2 != null && !password2.equals(password)) { errors.put("password2", "兩次輸入的密碼不一致!"); } if (email == null || "".equals(email)) { errors.put("email", "email不能為空!"); } else if (email != null && !email.matches("[0-9a-zA-Z_-]+@[0-9a-zA-Z_-]+\\.[0-9a-zA-Z_-]+(\\.[0-9a-zA-Z_-])*")) { errors.put("email", "email格式不正確!"); } if (!errors.isEmpty()) { request.setAttribute("errors", errors); request.getRequestDispatcher("/registerUI").forward(request, response); return; } User user = new User(); user.setUserName(userName); user.setPassword(password); user.setEmail(email); user.setActivated(false); userDao.addUser(user); // 注冊成功後,發送帳戶激活鏈接 EmailUtils.sendAccountActivateEmail(user); // 注冊成功直接將當前用戶保存到session中 request.getSession().setAttribute("user", user); request.getRequestDispatcher("/WEB-INF/pages/registerSuccess.jsp").forward(request,response); } }
3、激活帳戶的Servlet(ActivateAccountServle)
package org.study.accountactivate.web.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.study.accountactivate.dao.UserDao; import org.study.accountactivate.dao.impl.UserDaoImpl; import org.study.accountactivate.domail.User; import org.study.accountactivate.util.GenerateLinkUtils; /** * 帳戶激活 */ public class ActivateAccountServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String idValue = request.getParameter("id"); int id = -1; try { id = Integer.parseInt(idValue); } catch (NumberFormatException e) { throw new RuntimeException("無效的用戶!"); } UserDao userDao = UserDaoImpl.getInstance(); User user = userDao.findUserById(id);// 得到要激活的帳戶 user.setActivated(GenerateLinkUtils.verifyCheckcode(user, request));// 校驗驗證碼是否和注冊時發送的一致,以此設置是否激活該帳戶 userDao.updateUser(user); request.getSession().setAttribute("user", user); request.getRequestDispatcher("/accountActivateUI").forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }4、操作用戶的DAO接口與實現類(UserDao和UserDaoImpl類)
package org.study.accountactivate.dao; import org.study.accountactivate.domail.User; public interface UserDao { void addUser(User user); void updateUser(User user); User findUserById(int id); User findUserByName(String userName); User findUserByNameOrEmail(String nameOrEmail); } package org.study.accountactivate.dao.impl; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.UUID; import org.study.accountactivate.dao.UserDao; import org.study.accountactivate.domail.User; public class UserDaoImpl implements UserDao { private static UserDaoImpl instance = new UserDaoImpl(); private UserDaoImpl() {} public static UserDaoImpl getInstance() { return instance; } Map<Integer,User> users = new HashMap<Integer, User>(); int nextId = 1; @Override public void addUser(User user) { user.setId(nextId++); user.setRandomCode(UUID.randomUUID().toString()); users.put(user.getId(), user); } @Override public void updateUser(User user) { users.put(user.getId(), user); } @Override public User findUserById(int id) { return users.get(id); } @Override public User findUserByName(String userName) { Collection<User> userValues = users.values(); for (Iterator<User> iterator = userValues.iterator();iterator.hasNext();) { User user = iterator.next(); if (user.getUserName().equals(userName)) { return user; } } return null; } @Override public User findUserByNameOrEmail(String nameOrEmail) { Collection<User> userValues = users.values(); for(Iterator<User> iterator = userValues.iterator();iterator.hasNext();) { User user = iterator.next(); if (user.getEmail().equals(nameOrEmail) || user.getUserName().equals(nameOrEmail)) { return user; } } return null; } }5、發送Email的工具類(EmailUtils,用於發送帳戶激活鏈接和密碼重置鏈接)
package org.study.accountactivate.util; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.Properties; import javax.mail.Authenticator; import javax.mail.Message.RecipientType; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import org.study.accountactivate.domail.User; public class EmailUtils { private static final String FROM = "xyang81@163.com"; /** * 注冊成功後,向用戶發送帳戶激活鏈接的郵件 * @param user 未激活的用戶 */ public static void sendAccountActivateEmail(User user) { Session session = getSession(); MimeMessage message = new MimeMessage(session); try { message.setSubject("帳戶激活郵件"); message.setSentDate(new Date()); message.setFrom(new InternetAddress(FROM)); message.setRecipient(RecipientType.TO, new InternetAddress(user.getEmail())); message.setContent("<a href='" + GenerateLinkUtils.generateActivateLink(user)+"'>點擊激活帳戶</a>","text/html;charset=utf-8"); // 發送郵件 Transport.send(message); } catch (Exception e) { e.printStackTrace(); } } /** * 發送重設密碼鏈接的郵件 */ public static void sendResetPasswordEmail(User user) { Session session = getSession(); MimeMessage message = new MimeMessage(session); try { message.setSubject("找回您的帳戶與密碼"); message.setSentDate(new Date()); message.setFrom(new InternetAddress(FROM)); message.setRecipient(RecipientType.TO, new InternetAddress(user.getEmail())); message.setContent("要使用新的密碼, 請使用以下鏈接啟用密碼:<br/><a href='" + GenerateLinkUtils.generateResetPwdLink(user) +"'>點擊重新設置密碼</a>","text/html;charset=utf-8"); // 發送郵件 Transport.send(message); } catch (Exception 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"); props.setProperty("mail.smtp.port", "25"); props.setProperty("mail.smtp.auth", "true"); Session session = Session.getInstance(props, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { String password = null; InputStream is = EmailUtils.class.getResourceAsStream("password.dat"); byte[] b = new byte[1024]; try { int len = is.read(b); password = new String(b,0,len); } catch (IOException e) { e.printStackTrace(); } return new PasswordAuthentication(FROM, password); } }); return session; } }
6、生成帳戶激活、密碼重設鏈接的工具類(GenerateLinkUtils)
package org.study.accountactivate.util; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import javax.servlet.ServletRequest; import org.study.accountactivate.domail.User; /** * 生成帳戶激活、重新設置密碼的鏈接 */ public class GenerateLinkUtils { private static final String CHECK_CODE = "checkCode"; /** * 生成帳戶激活鏈接 */ public static String generateActivateLink(User user) { return "https://localhost:8080/AccountActivate/activateAccount?&" + CHECK_CODE + "=" + generateCheckcode(user); } /** * 生成重設密碼的鏈接 */ public static String generateResetPwdLink(User user) { return "https://localhost:8080/AccountActivate/resetPasswordUI?userName=" + user.getUserName() + "&" + CHECK_CODE + "=" + generateCheckcode(user); } /** * 生成驗證帳戶的MD5校驗碼 * @param user 要激活的帳戶 * @return 將用戶名和密碼組合後,通過md5加密後的16進製格式的字符串 */ public static String generateCheckcode(User user) { String userName = user.getUserName(); String randomCode = user.getRandomCode(); return md5(userName + ":" + randomCode); } /** * 驗證校驗碼是否和注冊時發送的驗證碼一致 * @param user 要激活的帳戶 * @param checkcode 注冊時發送的校驗碼 * @return 如果一致返回true,否則返回false */ public static boolean verifyCheckcode(User user,ServletRequest request) { String checkCode = request.getParameter(CHECK_CODE); return generateCheckcode(user).equals(checkCode); } private static String md5(String string) { MessageDigest md = null; try { md = MessageDigest.getInstance("md5"); md.update(string.getBytes()); byte[] md5Bytes = md.digest(); return bytes2Hex(md5Bytes); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } 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(); } }
7、實體類(User)
package org.study.accountactivate.domail; public class User { // 編號 private int id; // 用戶名 private String userName; // 密碼 private String password; // email private String email; // 是否激活 private boolean activated; // 隨機碼(激活帳戶與生成重設密碼鏈接時使用) private String randomCode; 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 boolean isActivated() { return activated; } public void setActivated(boolean activated) { this.activated = activated; } public String getRandomCode() { return randomCode; } public void setRandomCode(String randomCode) { this.randomCode = randomCode; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
功能實現步驟(忘記密碼):
1、登錄頁麵
<form action="${pageContext.request.contextPath}/login" method="post"> <span >${errors.loginError}</span> 用戶名:<input type="text" name="userName" value="${param.userName}"><span >${errors.userName}</span><br/> 密碼:<input type="password" name="password"><span >${errors.password}</span><br/> <input type="submit" value="登錄"> <a href="${pageContext.request.contextPath}/forgotPwdUI">忘記密碼?</a> <a href="${pageContext.request.contextPath}/registerUI">注冊</a> </form>
2、發送重設密碼申請的頁麵
<form action="${pageContext.request.contextPath}/forgotPwd" method="post"> <span >${requestScope.sendMailMsg}</span> 用戶名/郵箱:<input type="text" name="userNameOrEmail" /><span >${requestScope.errorMsg}</span><br/> <input type="submit" value="提交" /><a href=""></a> </form>
3、處理“發送重設密碼鏈接“請求的Servlet(ForgotPwdServlet)
package org.study.accountactivate.web.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.study.accountactivate.dao.UserDao; import org.study.accountactivate.dao.impl.UserDaoImpl; import org.study.accountactivate.domail.User; import org.study.accountactivate.util.EmailUtils; /** * 發送重設密碼申請的鏈接 */ public class ForgotPwdServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String userNameOrEmail = request.getParameter("userNameOrEmail"); UserDao userDao = UserDaoImpl.getInstance(); User user = userDao.findUserByNameOrEmail(userNameOrEmail); if (user == null) { request.setAttribute("errorMsg", userNameOrEmail + ",不存在!"); request.getRequestDispatcher("/forgotPwdUI").forward(request, response); return; } // 發送重新設置密碼的鏈接 EmailUtils.sendResetPasswordEmail(user); request.setAttribute("sendMailMsg", "您的申請已提交成功,請查看您的"+user.getEmail()+"郵箱。"); request.getRequestDispatcher("/WEB-INF/pages/forgotPwdSuccess.jsp").forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }4、重新設置密碼的頁麵
<form action="${pageContext.request.contextPath}/resetPassword" method="post"> <span >${errors.passwordError}</span> 用戶名:<input type="text" name="userName" value="${userName}" readonly="readonly"/><br/> 新密碼:<input type="password" name="newPassword" /><span >${errors.newPassword }</span><br/> 確認新密碼:<input type="password" name="newPassword2"/><span >${errors.newPassword2 }</span><br/> <input type="submit" value="修改" /> </form>5、處理重新設置密碼請求的Servlet(ResetPasswordServlet)
package org.study.accountactivate.web.servlet; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.study.accountactivate.dao.UserDao; import org.study.accountactivate.dao.impl.UserDaoImpl; import org.study.accountactivate.domail.User; /** * 重新設置密碼 */ public class ResetPasswordServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String userName = request.getParameter("userName"); String newPassword = request.getParameter("newPassword"); String newPassword2 = request.getParameter("newPassword2"); Map<String,String> errors = new HashMap<String, String>(); if (newPassword == null || "".equals(newPassword)) { errors.put("newPassword", "新密碼不能為空!"); } if (newPassword2 == null || "".equals(newPassword2)) { errors.put("newPassword2", "確認新密碼不能為空!"); } if (!newPassword.equals(newPassword2)) { errors.put("passwordError", "兩次輸入的密碼不一致!"); } if (!errors.isEmpty()) { request.setAttribute("errors", errors); request.getRequestDispatcher("/resetPasswordUI?userName=" + userName).forward(request, response); return; } UserDao userDao = UserDaoImpl.getInstance(); User user = userDao.findUserByName(userName); user.setPassword(newPassword); request.getRequestDispatcher("/WEB-INF/pages/resetPasswordSuccess.jsp").forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
帳戶激活操作流程:
1)、注冊頁麵
2)、注冊成功頁麵
3)、帳戶未激活,登錄後的界麵
4)、前往郵箱中激活帳戶
5)、激活成功
6)、帳戶已激活,登錄後的界麵
忘記密碼操作流程:
1)、登錄頁麵(點擊“忘記密碼”鏈接,進入重設密碼頁麵)
2、重設密碼申請頁麵(輸入用戶名或郵箱,點擊“提交”按鈕)
3、重設密碼申請成功頁麵
4、郵箱中重設密碼的鏈接
5、重新設置密碼頁麵
6、密碼重新設置成功頁麵
源碼下載地址:https://download.csdn.net/detail/xyang81/4510396
最後更新:2017-04-02 16:47:46