JavaWeb项目(登录注册页面)全过程总结

文章目录

  • JavaWeb项目(登录注册页面)全过程总结
  • 一、环境准备与开发工具
  • 二、创建 JavaWeb 项目
    • 2.1 新建Dynamic Web Project项目
    • 2.2 创建前端页面
      • 2.2.1 登录页面
        • 1.login.jsp
        • 2. login.js
        • 3. time.js
        • 4. focus.js + animate.js
      • 2.2.2 注册页面(register.jsp)
      • 2.2.3 修改密码的页面(change_pwd.jsp)
      • 2.2.4 登录成功的页面(success.jsp)
    • 2.3 创建相关配置类
      • 2.3.1 DAO类(DAO.java)
      • 2.3.2 JDBCUtil类(JDBCUtil.java)
      • 2.3.3 JsonResult类(JsonResult.java)
      • 2.3.4 MailUtil类(MailUtil.java)
      • 2.3.5 User类(User.java)
    • 2.4 创建 Servlet
      • 2.4.1 登录界面的Servlet(LoginServlet)
      • 2.3.2 注册界面的Servlet(RegisterServlet)
      • 2.3.3 修改密码的Servlet(HandlePwdServlet)
      • 2.3.4 发送邮件的Servlet(EmailServlet)

项目需求:

  1. 实现登录页面:
    • 顶部有logo栏目
    • 左侧提供轮播图
    • 提供两种登录方式:①账号密码登录 ②邮箱+验证码登录
    • 登录成功后跳到显示“登录成功”四字的页面(简单设计)
    • 有修改密码的功能
  2. 注册页面
  3. 数据统一存储在数据库

一、环境准备与开发工具

此次项目用到的工具是:

① 前端:HTML + CSS + JS

② 后端:Tomcat 9 + Servlet

③ 项目开发工具 :Eclipse(Java EE IDE) java运行环境是:jdk 15

④ 数据库:Mysql + Navicat 15 for MySQL

此次实验需要提前安装配置好Eclipse、jdk、tomcat,tomcat的安装和servlet的基本使用请见另两篇文章:Servlet的使用 和 Tomcat的使用

二、创建 JavaWeb 项目

2.1 新建Dynamic Web Project项目

里面的Dynamic Web module version 我使用的2.5

项目的目录结构如下:

2.2 创建前端页面

这里只展示 jsp 和 js 文件的代码,页面中的 icon 是使用的 icomoon

2.2.1 登录页面

1.login.jsp

<!DOCTYPE html><html><head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>LARP-LOGIN</title><link rel="stylesheet" href="../css/base.css"><link rel="stylesheet" href="../css/login.css"> <script src="../js/focus.js"></script>  <script src="../js/animate.js"></script> <script src="../js/time.js"></script><script src="../js/login.js"></script></head><body><div class="container"><header class="header"><img src="../images/LARP.png" alt=""><div class="logo"><h6>Load Assessment And Risk Prediction</h6><h6>运动员负荷评估和风险预测系统</h6></div><div class="vline"></div><h2 class="brand">LARP数据可视化管理平台</h2><div class="time"><h6 class="date"></h6><h6 class="second"></h6></div></header><main><div class="left"><div class="left_container"><div class="focus"><a href="javascript:;" class="arrow-l"></a><a href="javascript:;" class="arrow-r"></a><ul><li><img src="../images/1.jpg" alt=""></li><li><img src="../images/2.png" alt=""></li><li><img src="../images/3.png" alt=""></li></ul><ol class="circle"></ol></div></div></div><div class="right"><form action="/my_login/LoginServlet" method="post" class="login_container"><div class="login_method"><span><a href="javascript:;" class="account_a">账号登录</a></span><span><a href="javascript:;" class="phone_a">邮箱登录</a> </span></div><div class="hline"></div><div class="input_box"></div><a href="change_pwd.jsp" class="forget_pwd">修改密码</a><input type="submit" class="click_login" value="登录"/><a href="register.jsp" class="to_register">未注册,先去注册</a></form><div class="other_methods"><div class="ways">其他登录方式</div><a href="javascript:;" class="icon qq"></a><a href="javascript:;" class="icon wechat"></a></div><h3 class="author">-Directed By Elvira-</h3></div></main></div></body></html>

2. login.js

功能讲解:

① 登录方式 tab 栏切换

最开始我是在 login.jsp 把两种登录方式的 html 代码都写上,然后再父盒子上使用的display:none 和 block 来切换实现,在显示上是可以做到切换显示和隐藏,但是再代码层两种方式的代码都存在,在表单提交时,就会出现问题,因为其提交的时两种方法中4个输入框中的内容,且无法通过 required 约束表单不能为空,造成表单不能提交(因为其要求了4个输入框都需要填内容,而有两个输入框隐藏)。

转换思路:

  1. 在 login.jsp 中使用一个容器来占位
  2. 通过 js 控制具体显示的是哪种登录方法的 html 代码
  3. 在 login.js 中定义 html 模板以及变量 isAccount,给 tab 栏的登录方式添加点击事件,点击到哪种方法就展示哪个方法对应的 html 模板和样式

② 邮箱登录方式下获取验证码按钮点击后禁用,10s后解禁

思想:

  1. 定义定时器 setInterval 和变量 second(定义要禁用的时间)
  2. 禁用点击按钮
  3. 用 innerText 替换按钮中的文字,定时器每隔1s刷新一次按钮中的文字,seond自减1
  4. 若 second <= 0,清除定时器,解禁按钮,并将按钮中的文字换回:获取验证码
// 登录方式切换window.addEventListener('load', function () {// 是否是账户登录var isAccount = true// html模板var user_template = ``var phone_template = ``// 获取输入框的元素var input_box = document.querySelector('.input_box')// 挂载用户密码登录方式的htmlinput_box.innerHTML = user_templatevar account_a = document.querySelector('.account_a')var phone_a = document.querySelector('.phone_a')var forget_pwd = document.querySelector('.forget_pwd')// 给账号登录的链接添加事件account_a.addEventListener('click', function () {// 将用户密码方法的html代码渲染,必须放在前面,不然获取不到元素input_box.innerHTML = user_template//获取输入框中的值var input_user = document.querySelector('#username')isAccount = trueaccount_a.style.color = '#03a9f4'phone_a.style.color = '#666'forget_pwd.style.display = 'block'input_user.focus() // 解决切换页面后输入框的聚焦问题})// 给手机登录的链接添加事件phone_a.addEventListener('click', function () {// 将手机号验证码方法的html代码渲染,必须放在前面,不然获取不到元素input_box.innerHTML = phone_template//获取元素var phone_input = document.querySelector('.phone_input')//输入框中的值var input_phone = document.querySelector('#phone')isAccount = falseaccount_a.style.color = '#666'phone_a.style.color = '#03a9f4'forget_pwd.style.display = 'none'phone_input.style.marginBottom = '8.1vh'input_phone.focus()// 创建XMLHttpRequestfunction CreateXmlHttp() {// 定义XMLHttpRequest对象var xhr = null// 创建XMLHttpRequest对象if (window.XMLHttpRequest) {// 其他浏览器xhr = new XMLHttpRequest()} else if (window.ActiveXObject) {// IE浏览器 IE5 IE6xhr = new ActiveXObject('Microsoft.XMLHTTP')}return xhr}// 获取点击获取验证码的按钮var getCodeBtn = document.querySelector(".getCode");// 获取验证码点击按钮点击后禁用getCodeBtn.addEventListener("click", function() {// 点击按钮后,将按钮禁用10秒钟getCodeBtn.disabled = true;var second = 10;var timer = setInterval(function () {getCodeBtn.innerText = second + "s 后可重新获取"if (second <= 0) {clearInterval(timer);getCodeBtn.innerText = "获取验证码"getCodeBtn.disabled = false;}second--;}, 1000);// 发送post请求// 创建XMLHttpRequestvar xhr = CreateXmlHttp()var email = input_phone.value// 指定响应函数(回调函数)xhr.onreadystatechange = function () {if (xhr.readyState == 4) {// 请求已经完成,信息已经成功返回,开始处理信息if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {// 将从服务器端返回是JSON格式数据转换成JavaScript对象var res = xhr.responseTextvar jsonObj = eval("("+res+")")console.log("res:"+res)if(jsonObj.type == 0) {alert(jsonObj.error);} else {alert("邮箱发送成功,请查阅邮箱,尽快认证") }} else {alert("邮箱发送失败") }}}xhr.open('POST','/my_login/EmailServlet',true)// 设置HTTP的输出内容类型为json格式数据:application/x-www-form-urlencodedxhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')// 设置浏览器不使用缓存,服务器不从缓存中找,重新执行代码,而且服务器返回给浏览器的时候,告诉浏览器也不要保存缓存。xhr.setRequestHeader('If-Modified-Since', '0')// 发送请求xhr.send("email="+email);})})})

3. time.js

顶部导航栏动态显示时间

// 获取时间window.addEventListener('load', function () {// 获取元素var date = document.querySelector('.date')var sec = document.querySelector('.second')setInterval(function () {// 获取当前时间var d = new Date()var year = d.getFullYear() // 获取年var month = d.getMonth() + 1 // 获取月var day = d.getDate() // 获取日期var hour = d.getHours() // 获取小时var minute = d.getMinutes() // 获取分钟var second = d.getSeconds() // 获取秒if (month < 10) month = '0' + monthif (day < 10) day = '0' + dayif (hour < 10) day = '0' + hourif (minute < 10) minute = '0' + minuteif (second < 10) second = '0' + second// 拼接字符串var date_str = year + ' 年 ' + month + ' 月 ' + day + ' 日 'var sec_str = hour + ' : ' + minute + ' : ' + seconddate.innerHTML = date_strsec.innerHTML = sec_str}, 1000)})

4. focus.js + animate.js

轮播图的实现,animate.js是抽象出来的元素移动的函数

轮播图功能:

  • 鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮;
  • 点击右侧按钮一次,图片向左移动播放后一张,左侧按钮同理;
  • 图片播放的同时,下面小圆圈模块跟随一起变化;
  • 点击小圆圈,可以播放相应的图片;
  • 鼠标不经过轮播图,轮播图自动播放图片;
  • 鼠标经过轮播图模块,自动播放停止

轮播图功能实现思想:

① 动态生成小圆圈

  • 核心思路:小圆圈个数与图片数目一致
  • 具体实现步骤:
    1. 首先的得到 ul 里面图片的张数(即 li 的个数)
    2. 利用循环动态生成小圆圈(小圆圈放在 ol 里面)
    3. 创建 li 节点 createElement(“li”)
    4. 插入 ol 节点 ol.appendChild(“li”)
    5. 第一个小圆圈添加 current 类(当前显示的元素的样式)

② 点击小圆圈滚动图片

  • 核心算法:点击某个小圆圈,就让图片滚动:小圆圈索引号乘以图片的宽度作为 ul 的移动距离
  • 注意:
    1. 此时用到 animate.js 函数,将 js 文件引入(因为 index.js 依赖 animate.js, 所以animate.js 要写到 index.js 上面)
    2. 使用animate.js 动画函数的前提,该元素必须要有定位
    3. 移动 ul 不是 li
    4. 需要知道小圆圈的索引号,可以在生成小圆圈的时候,给他设置一个自定义属性,点击的时候获取该自定义属性

③ 点击右侧按钮一次,就让图片滚动一张(左侧按钮类似)

  • 核心思想:声明一个变量 num,点击一次,自增1,让这个变量乘以图片宽度,就是 ul 的滚动距离
  • 图片无缝滚动原理:
    1. 把 ul 第一个 li 复制一份,放到 ul 最后面
    2. 当图片滚动到克隆的最后一张照片时, 让 ul 快速的、不做动画的跳到最左侧:left:0
    3. 同时 num 赋值为 0, 就可以重新开始滚动图片了

④ 点击右侧按钮,小圆圈跟随变化

思想:

  1. 声明变量 circle,每次点击自增1,注意:左侧按钮也需要这个变量,所以声明全局变量
  2. 图片有 4 张,小圆圈只有 3 个,所以添加判断条件:如果 circle == 3,就重新复原为 0

⑤ 自动播放

思想:

  1. 添加一个定时器,自动播放轮播图,就类似于点击了右侧按钮
  2. 使用手动调用右侧按钮点击事件 arrow_r.click()
  3. 鼠标经过轮播图就停止定时器
  4. 鼠标离开轮播图就开启定时器

⑥ 节流阀

  • 作用:防止轮播图按钮连续点击造成播放过快

  • 目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发

  • 核心思路:利用回调函数,添加一个变量控制,锁住函数和解锁函数

    设置变量 var flag = true;

    if(flag){ flag = false; do something} 关闭水龙头

    利用回调函数,动画执行完毕,flag = true 打开水龙头

focus.js

window.addEventListener('load', function () {//1、获取元素var arrow_l = document.querySelector('.arrow-l')var arrow_r = document.querySelector('.arrow-r')var focus = document.querySelector('.focus')var focusWidth = focus.offsetWidth//2、鼠标经过focus 就显示隐藏的左右按钮focus.addEventListener('mouseenter', function () {arrow_l.style.display = 'block'arrow_r.style.display = 'block'clearInterval(timer)timer = null //清除定时器变量,让图片静止})//鼠标离开focus 就隐藏左右按钮focus.addEventListener('mouseleave', function () {arrow_l.style.display = 'none'arrow_r.style.display = 'none'timer = setInterval(function () {//手动调用点击事件arrow_r.click()}, 3500)})// 3、动态生成小圆圈,有几张图片就有几个小圆圈var ul = focus.querySelector('ul')var ol = focus.querySelector('.circle')for (var i = 0; i < ul.children.length; i++) {// 创建小livar li = document.createElement('li')// 用自定义属性记录当前小圆圈的索引号li.setAttribute('index', i)// 把小li插入到ol 里面ol.appendChild(li)// 4、小圆圈的排他思想,我们可以直接在生成小圆圈的同时直接绑定事件li.addEventListener('click', function () {// 干掉所有人 ,把所有的小li 清除current 类名for (var i = 0; i < ol.children.length; i++) {ol.children[i].className = ''}//留下我自己 当前的小li设置current属性this.className = 'current'// 5、点击小圆圈,移动图片 移动的是ul 不是li// 当我们点击了某个小li,就拿到当前li 的索引号var index = this.getAttribute('index')num = index //没有这句话图片不会跟着小圆点变化circle = index //没有这句话小圆点不会跟着变化animate(ul, -index * focusWidth)})}//把 ol 里面的第一个小li设置类名为 currentol.children[0].className = 'current'//6、克隆第一张图片放到ul最后,写在生成li的后面var first = ul.children[0].cloneNode(true)ul.appendChild(first)// 7、点击右侧按钮,图片滚动一张var num = 0// circle 控制小圆圈的播放var circle = 0// flag节流阀var flag = truearrow_r.addEventListener('click', function () {if (flag) {flag = false// 如果走到了最后一张复制的图片,此时ul要快速复原left为0if (num == ul.children.length - 1) {ul.style.left = 0num = 0}num++animate(ul, -num * focusWidth, function () {flag = true // 只有一张图片播放完了才展示下一张,点击多快都没有用})circle++//如果circle=4,说明走到克隆的那张图片了if (circle == ol.children.length) {circle = 0}circleChange()}})// 8.左侧按钮arrow_l.addEventListener('click', function () {if (flag) {flag = falseif (num == 0) {num = ul.children.length - 1 //num=3ul.style.left = -num * focusWidth + 'px'}num--animate(ul, -num * focusWidth, function () {flag = true})circle-- //circle为序号// if (circle < 0) {// circle = ol.children.length - 1;//circle=2// }circle = circle < 0 " />.children.length - 1 : circlecircleChange()}})function circleChange() {// 先清除其他小圆圈的current类名,for (var i = 0; i < ol.children.length; i++) {ol.children[i].className = ''}// 当前的留下currentol.children[circle].className = 'current'}//自动播放模块var timer = setInterval(function () {//手动调用点击事件arrow_r.click()}, 2000)})

animate.js

function animate(obj, target, callback) {//前面必须要加function关键字clearInterval(obj.timer);obj.timer = setInterval(function () {var step = (target - obj.offsetLeft) / 10;step = step > 0 ? Math.ceil(step) : Math.floor(step);if (obj.offsetLeft == target) {//停止动画 本质停止定时器clearInterval(obj.timer);//回调函数写到定时器结束里面// if (callback) {// //调用函数// callback();// }callback && callback();//短路的思想}obj.style.left = obj.offsetLeft + step + 'px';},15);}

登录页面界面如下:


2.2.2 注册页面(register.jsp)

<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>LARP-REGISTER</title><link rel="stylesheet" href="../css/base.css"> <link rel="stylesheet" href="../css/register.css"> <script src="../js/register.js"></script></head><body><div class="container"><form action="/my_login/RegisterServlet" method="post" id="myform"><h2>新用户注册</h2><div class="item">用户名称:<input type="text" name="username" id="username" required></div><div class="item">设置密码:<input type="password" name="password" id="password" required></div><div class="item">确认密码:<input type="password" name="again_password" id="again_password" required></div><div class="button"><input type="submit" value="确认" id="submit"><input type="reset" value="重置" id="reset"></div></form></div></body></html>

注册页面界面如下:

2.2.3 修改密码的页面(change_pwd.jsp)

<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>修改密码</title><link rel="stylesheet" href="../css/base.css"><link rel="stylesheet" href="../css/change_pwd.css"><script src="../js/change_pwd.js"></script></head><body><div class="container"><form action="/my_login/HandlePwdServlet" method="post" id="myform"><h2>修改密码</h2><div class="item">登录名称:<input type="text" name="username" id="username" required></div><div class="item">修改密码:<input type="password" name="password" id="password" required></div><div class="item">确认密码:<input type="password" name="again_password" id="again_password" required></div><div class="button"><input type="submit" value="确认" id="submit"><input type="reset" value="重置" id="reset"></div></form></div></body></html>

修改密码界面如下:

2.2.4 登录成功的页面(success.jsp)

<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>success</title><style>body {display: flex;min-height: 100vh;margin: 0;}.success {height: 18vh;width: 25vw;line-height: 18vh;margin: auto;font-size: 2.4em;text-align: center;letter-spacing: 0.3em;background-color: rgba(255, 255, 255, 0.815);}#background_video {overflow: hidden;width: 100%;position: fixed;z-index: -1;}</style></head><body><div class="success">登录成功</div><videoid="background_video" muted loop autoplay><source src="../images/starbroken.mp4" type="video/mp4" /></video></body></html>

登录成功界面:

2.3 创建相关配置类

2.3.1 DAO类(DAO.java)

DAO 类增加数据库查询用户的功能

JDBC开发的步骤:

导入驱动jar包 mysql-connector-java-5.1.47-bin.jar

  • 复制粘贴到lib

  • 右键点击 build path -> add to bulid path (在java 工程当中引入了 jar 包)

注册驱动

获取数据库的连接对象 Connection

基本操作:执行sql

  • 定义sql语句
  • 获取执行sql语句的对象 Statement
  • 执行sql,用 ResultSet 接收返回的结果集
  • 遍历处理结果集

释放资源

以上的步骤2、3、5 都是在 JDBCUtil.java 中完成的

package modle;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;public class DAO {// 登录验证public User login(Connection conn,User user) throws Exception{User resultUser = null;// sql 查询语句String sql="select * from users where username=" />;// 获得执行sql语句的对象PreparedStatement pstatement =conn.prepareStatement(sql);pstatement.setString(1, user.getUsername());pstatement.setString(2, user.getPassword());// 执行sql语句获得结果集ResultSet rs = pstatement.executeQuery();if(rs.next()){ resultUser = new User();resultUser.setUsersname(rs.getString("username"));resultUser.setPassword(rs.getString("password"));}return resultUser;}// 修改密码查找用户public User searchUser(Connection conn,User user) throws Exception {User resultUser = null;// sql 查询语句String sql="select * from users where username=?";// 获得执行sql语句的对象PreparedStatement pstatement =conn.prepareStatement(sql);pstatement.setString(1, user.getUsername());// 执行sql语句获得结果集ResultSet rs = pstatement.executeQuery();if(rs.next()){ resultUser = new User();resultUser.setUsersname(rs.getString("username"));}return resultUser;}// 注册验证public boolean register(Connection conn,User user) throws Exception {boolean flag = false;// sql 查询语句String sql="insert into users(username,password)values(?,?)";// 获得执行sql语句的对象PreparedStatement pstatement =conn.prepareStatement(sql);pstatement.setString(1, user.getUsername());pstatement.setString(2, user.getPassword());// 执行sql语句获得结果集int res = pstatement.executeUpdate();if(res > 0) {flag = true;}return flag;}}

2.3.2 JDBCUtil类(JDBCUtil.java)

JDBCUtil类提供与数据库连接时 jdbc 的相关配置

package modle;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.Statement;public class JDBCUtil {// 数据库的参数private String dbUrl="jdbc:mysql://localhost:3306/my_login?useSSL=false";private String dbUsername="root";private String dbPassword="XXXX"; // 自己的密码// 与数据库连接public Connection getConn() {try {// 加载驱动Class.forName("com.mysql.jdbc.Driver");} catch (Exception e) {e.printStackTrace();}Connection conn = null;try {// 获得连接,返回connection 对象conn = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);} catch (Exception e) {e.printStackTrace();}return conn;}// 释放资源// 关闭结果集 ResultSetpublic void close(ResultSet resultSet) throws Exception {if(resultSet != null) {resultSet.close();}}// 关闭 sql 语句对象 Statementpublic void close(Statement statement) throws Exception {if(statement != null) {statement.close();}}// 关闭连接对象 Connectionpublic void close(Connection conn) throws Exception {if(conn != null) {conn.close();}}}

2.3.3 JsonResult类(JsonResult.java)

JsonResult类处理 Json 返回的数据。

package modle;public class JsonResult {private int type; //0为失败, 1为成功private String error;//错误信息public int getType() {return type;}public void setType(int type) {this.type = type;}public String getError() {return error;}public void setError(String error) {this.error = error;}}

2.3.4 MailUtil类(MailUtil.java)

MailUtil 类提供调用邮件发邮件时的相关配置。这里我用的126邮箱,记得去打开邮箱中的 POP3和SMTP服务,记住授权密码,需要导入 javax.mail.jar 包:

开启服务后的界面:

package modle;import java.util.Properties;import javax.mail.Authenticator;import javax.mail.Message;import javax.mail.PasswordAuthentication;import javax.mail.Session;import javax.mail.Transport;import javax.mail.internet.InternetAddress;import javax.mail.internet.MimeMessage;public class MailUtil {/** * @param email 登录用户的邮件 * @param emailMsg发送的邮件信息 * @throws Exception */public void sendMail(String userEmail, String emailMsg) throws Exception {// 1. 创建一封邮件,创建一个程序与邮件服务器会话对象sessionProperties props = new Properties();props.setProperty("mail.transport.protocol", "SMTP");props.setProperty("mail.host", "smtp.126.com"); //smtp.126.com为SMTP服务器地址,为指定这个服务器发送邮件props.setProperty("mail.smtp.auth", "true"); // 指定验证为true// 创建验证器Authenticator auth = new Authenticator() {public PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication("xxxx", "xxxx"); //参数分别为:用户名和授权密码}};// 用于连接邮件服务器的参数配置(发送邮件时需要用到)Session session= Session.getInstance(props,auth);// 根据参数配置,创建会话对象(为了发送邮件准备的)// 2.创建邮件对象message,相当于邮件内容Message message = new MimeMessage(session);// From: 发件人// 其中 InternetAddress 的三个参数分别为: 邮箱, 显示的昵称(只用于显示, 没有特别的要求), 昵称的字符集编码// 真正要发送时, 邮箱必须是真实有效的邮箱。message.setFrom(new InternetAddress("xxxxxxx"));// To: 收件人 设置收件人和发送方式message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(userEmail));// Subject: 邮件主题message.setSubject("邮箱验证");// Content: 邮件正文(可以使用html标签)message.setContent(emailMsg, "text/html;charset=UTF-8");// 3. 创建 transport 用于将邮件发出Transport.send(message);}}

2.3.5 User类(User.java)

User类提供用户基本信息的配置

package modle;public class User {private String username;private String password;// 构造函数public User() {} public User(String username, String password) {this.username = username;this.password = password;}// 获取用户名public String getUsername() {return username;}// 设置用户名public void setUsersname(String username) {this.username = username;}// 获取密码public String getPassword() {return password;}// 设置密码public void setPassword(String password) { this.password = password;}}

2.4 创建 Servlet

2.4.1 登录界面的Servlet(LoginServlet)

主要思想:

  1. 接收前台传来的值:账号和密码、邮箱和验证码,通过判断账号和密码或是邮箱和验证码谁不为空判断出前台使用的哪种登录方式
  2. 邮箱和验证码登录方式中:检验验证码是否正确是从 session 中取出 emailCode 的内容(在EmailServlet.java中在随机创建出6位的验证码后就把其存入 session 中了)与用户输入的进行核对。但其不够完美,因为有时间限制,超出时间后 session 就失效了。
package controller;import java.io.IOException;import java.io.PrintWriter;import java.sql.Connection;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.alibaba.fastjson.JSON;import modle.DAO;import modle.JDBCUtil;import modle.JsonResult;import modle.User;/** * 登录的 Servlet */public class LoginServlet extends HttpServlet {private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 接收前台传来的值 账号和密码 或是 邮箱和验证码String username = request.getParameter("username");String password = request.getParameter("password");String email = request.getParameter("email");String code = request.getParameter("code");System.out.println(password);System.out.println(email);System.out.println(code);response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();// 账号密码登录的方式if(username != null && password != null) {//解决中文字符乱码byte[] bytes = username.getBytes("ISO-8859-1");username = new String(bytes,"utf-8");System.out.println(username);JDBCUtil db = new JDBCUtil();// 创建一个用户保存下将密码和用户名保存User user = new User(username,password);DAO dao = new DAO();try {//数据库连接Connection conn = db.getConn();if(dao.login(conn, user) != null) {request.getSession().setAttribute("user", user);response.sendRedirect("jsp/success.jsp");} else {out.println("

用户名或者密码错误,验证失败

"
);out.println("

3秒以后跳转回登录页面

"
);response.setHeader("Refresh", "3;url=jsp/login.jsp");}} catch (Exception e) {e.printStackTrace();} finally {out.close();}} else if(email != null && code != null) { //邮箱验证码方式// 判断emailCode是否正确String s_emailCode = (String)request.getSession().getAttribute("emailCode");JsonResult jr = new JsonResult();if(!code.equalsIgnoreCase(s_emailCode)) {out.println("

邮件验证码错误,验证失败

"
);out.println("

3秒以后跳转回登录页面

"
);response.setHeader("Refresh", "3;url=jsp/login.jsp");} else { // 验证成功response.sendRedirect("jsp/success.jsp");}out.close();}}}

2.3.2 注册界面的Servlet(RegisterServlet)

package controller;import java.io.IOException;import java.io.PrintWriter;import java.sql.Connection;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import modle.DAO;import modle.JDBCUtil;import modle.User;/** * 注册的servlet */public class RegisterServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取注册名和密码String username = request.getParameter("username").trim();String password = request.getParameter("password").trim();String again_password = request.getParameter("again_password").trim();//解决中文字符乱码byte[] bytes = username.getBytes("ISO-8859-1");username = new String(bytes,"utf-8");response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();JDBCUtil db = new JDBCUtil();// 创建一个用户保存下将密码和用户名保存User user = new User(username,password);DAO dao = new DAO();try {//数据库连接Connection conn = db.getConn();if(!password.equals(again_password)) {out.println("

两次输入的密码不一致

"
);out.println("

3秒以后返回注册页面

"
);response.setHeader("Refresh", "3;url=jsp/register.jsp");} else {if(dao.register(conn, user)) {out.println("

注册新用户成功

"
);out.println("

3秒以后跳转回注册页面

"
);response.setHeader("Refresh", "3;url=jsp/login.jsp");} else {out.println("

注册新用户失败,用户名已经存在

"
);out.println("

3秒以后跳转回注册页面

"
);response.setHeader("Refresh", "3;url=jsp/register.jsp");}}} catch (Exception e) {e.printStackTrace();} finally {out.close();}}}

2.3.3 修改密码的Servlet(HandlePwdServlet)

package controller;import java.io.IOException;import java.io.PrintWriter;import java.sql.Connection;import java.sql.PreparedStatement;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import modle.DAO;import modle.JDBCUtil;import modle.User;/** * 修改密码的 servlet */public class HandlePwdServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取数据String username = request.getParameter("username").trim();String password = request.getParameter("password").trim();String again_password = request.getParameter("again_password").trim();//解决中文字符乱码byte[] bytes = username.getBytes("ISO-8859-1");username = new String(bytes,"utf-8");response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();JDBCUtil db = new JDBCUtil();// 创建一个用户保存下将密码和用户名保存User user = new User(username,password);DAO dao = new DAO();try {//数据库连接Connection conn = db.getConn();// 数据库中没有该用户if(dao.searchUser(conn, user) == null) {out.println("

该用户不存在,请先去注册

"
);out.println("

3秒以后返回修改密码页面

"
);response.setHeader("Refresh", "3;url=jsp/change_pwd.jsp");} else {if(!password.equals(again_password)) {out.println("

两次输入的密码不一致

"
);out.println("

3秒以后返回修改密码页面

"
);response.setHeader("Refresh", "3;url=jsp/change_pwd.jsp");} else {String sql="update users set password=" />;// 获得执行sql语句的对象PreparedStatement pstatement =conn.prepareStatement(sql);pstatement.setString(1, user.getPassword());pstatement.setString(2, user.getUsername());// 返回受影响修改的行数int res = pstatement.executeUpdate();if(res != 0) {out.println("

修改密码成功

"
);out.println("

3秒以后跳转回登录页面

"
);response.setHeader("Refresh", "3;url=jsp/login.jsp");} else {out.println("

修改密码失败

"
);out.println("

3秒以后返回修改密码页面

"
);response.setHeader("Refresh", "3;url=jsp/change_pwd.jsp");}}}} catch (Exception e) {e.printStackTrace();} finally {out.close();}}}

2.3.4 发送邮件的Servlet(EmailServlet)

主要思想:

  1. 获取前台用户的邮箱
  2. 随机生成6位数的验证码(需要导入commons-lang3-3.12.0.jar包)
  3. 在服务器端通过 session 保存验证码
  4. 通过 MailUtil 中对邮箱的配置发送邮件
package controller;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.apache.commons.lang3.RandomStringUtils;import com.alibaba.fastjson.JSON;import modle.JsonResult;import modle.MailUtil;import net.sf.json.JSONObject;/** * 处理邮件的Servlet */public class EmailServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取邮箱String email = request.getParameter("email");System.out.println(email);// 获取随机的6位数String num = RandomStringUtils.randomNumeric(6);// 邮件内容String emailMsg = "邮箱验证码为:"+ num +"请勿泄漏给他人!";// 在服务器端保存邮件验证码request.getSession().setAttribute("emailCode", num);JsonResult jr = new JsonResult();try {MailUtil mail = new MailUtil();// 发送邮件mail.sendMail(email, emailMsg);jr.setType(1); // 发送成功response.getWriter().write(JSON.toJSONString(jr));return;} catch (Exception e) {e.printStackTrace();jr.setType(0); // 发送失败jr.setError("邮件发送失败");response.getWriter().write(JSON.toJSONString(jr));return;}}}

注意:

  1. 在运行过程中,mail.sendMail(email, emailMsg); 这部总是报 java.lang.ClassNotFoundException: javax.activation.DataHandler的错误,查找资料下载导入了 javax.activation-1.2.0.jar就解决问题了

  2. 使用 JSON.toJSONString(jr)是导入了第三方的包来对 Json 进行快速处理,使用该方法可以导入两种类型的包:

    ① 导入fastjson-1.2.75.jar 一个包就行, 下载地址:Maven Repository: com.alibaba » fastjson (mvnrepository.com)

    ② 导入 6 个包:

    • commons-beanutils-1.9.4.jar

    • commons-collections4-4.4.jar

    • commons-lang3-3.12.0.jar

    • commons-logging-1.2.jar

    • ezmorph-1.0.6.jar

    • json-lib-2.4-jdk15.jar

    可以在下面的地址中下载:

    • http://commons.apache.org/index.html

    • http://json-lib.sourceforge.net/

    • http://ezmorph.sourceforge.net/

    • http://www.docjar.com/