JavaWeb_仿小米商场,(2):用户注册
1 业务描述
注册业务旨在收集和管理用户的个人信息,是未来提供个性化服务的基础。相应的前端页面如下方静态H5页面图所示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pU0b5THs-1652276034718)(D:\鲲鹏培训\项目\仿小米商城\项目编写过程截图\用户注册界面.png)]
网页中提供多个输入框,并给出对应的提示信息,逐步引导用户根据提示向输入框中填写信息。最终点击下方的 注册
按钮完成用户注册。
前端ajax验证,用户名在输入后鼠标丢失焦点的时候发送求情到servlet检查用户是否在数据库中存在,不存在则可以注册,存在则给出提示
前端ajax验证,邮箱在输入后鼠标丢失焦点的时候发送求情到servlet检查邮箱是否在数据库中存在,不存在则可以注册,存在则给出提示
注册成功后给出提示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AsIfcGBF-1652276034719)(D:\鲲鹏培训\项目\仿小米商城\项目编写过程截图\注册后激活界面.png)]
进一步地,为了验证联系方式的有效性,在完成注册功能后,前端还会提示用户执行 激活
操作:通过向用户注册时填写的邮箱发送验证邮件。在邮件正文中引导用户点击相应的超链接完成账户激活,并在激活页面提供登录链接,引导用户登录网站。整个注册业务流程完毕
2 业务分析
2.1 业务流程抽象
在本案例中,注册业务分为两阶段:
注册阶段:用户根据前端页面的提示信息填写相应内容,进而提交至后台处理,最终将信息保存至数据库。
激活阶段:系统后台自动向用户填写的邮件地址发送一封 激活邮件,邮件中包含一个激活链接。用户通过点击激活链接向服务器发送一个请求,服务器在接收到该请求后,再向用户浏览器回写激活成功信息。
2.2 可能的技术难点与解决策略
1避免机器人批量注册
添加验证码,优先核对,不满足直接跳出方法。(后续单独写一篇博客分析此功能)
2如何保证激活用户的唯一性?
在 tab_user 表中添加字段:active_code 。它是一种保证唯一性的序列码,本案例中调用 Java 中的工具类 UUID 生成序列并作为用户的激活码。在用户注册时生成并与用户信息一并写入数据库,同时向用户提交的邮件地址发送一封带有激活码链接的邮件。只有用户在邮件中点击了这个链接,使用带有激活码的URL向服务器发出请求,服务器才能收到用户“发来的”激活码,并在后端完成校验并向用户端浏览器回写响应信息。
3如何定义和处理激活状态?
在 tab_user 表中添加两个字段:status 。status 用于标注用户是否激活,它只有两种可能的取值:Y(已激活)和 N(未激活)。在用户刚刚提交注册表单时,后端在数据库中添加用户信息时对该字段设为 N,当用户在激活邮件中点击激活链接向服务器发送激活请求时,服务器在核对通过的前提下将该字段修改为 Y。
前端校验用户填写的表单信息:正则表达式校验,这里参考了暮光乐鱼的博客,他的正则表达式写的要比我的好。
非空 :.+
字符集 :^\w{6,20}$
手机号 :1(3|4|5|7|8)\d{9}
邮箱 :^\w+(.\w+)*@\w+(.\w+)+$
3 代码实现
3.1 信息验证与提交
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SPs1wpZj-1652276034720)(D:\鲲鹏培训\项目\仿小米商城\项目编写过程截图\信息验证与提交流程.png)]
3.1.1前端
通过ajax异步校验用户名是否存在
$(function () {
$("#username").change(function () {
//使用ajax 做username 的异步验证 checkUsername?username=xxxx
$.get("user.do?action=checkUserName", "username=" + this.value, function (data) {
if (data == 1) {
$("#usernameMsg").html("用户名已经存在").css("color", "red");
$("#registerBtn").attr("disabled", true);
} else {
$("#usernameMsg").html("用户名可用").css("color", "green");
$("#registerBtn").removeAttr("disabled");
}
})
});
})
通过ajax校验邮箱是否存在
$(function () {
$("#email").change(function () {
//使用ajax 做username 的异步验证 checkUsername?username=xxxx
$.get("user.do?action=checkEmial", "username=" + this.value, function (data) {
if (data == 1) {
$("#usernameMsg").html("邮箱已经存在").css("color", "red");
$("#registerBtn").attr("disabled", true);
} else {
$("#usernameMsg").html("邮箱可用").css("color", "green");
$("#registerBtn").removeAttr("disabled");
}
})
});
})
3.1.2 用户名邮箱校验Servlet代码
@WebServlet("/user.do")
public class UserServlet extends BaseServlet implements Serializable {
//...............
public String checkUserName(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
IUserService userService=new UserServiceImpl();
User user=new User();
String username = req.getParameter("username");
user.setUsername(username);
boolean falg=userService.queryDataByUser(user);
return falg==true?"0":"1";
}
public String checkEmail(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
IUserService userService=new UserServiceImpl();
User user=new User();
String email = req.getParameter("email");
user.setEmail(email);
boolean falg=userService.queryDataByUser(user);
return falg==false?"0":"1";
}
//.............................
表单提交函数
//单词字符 8-12
function checkUsername(){
const s = $("#username").val();
const check = /^\w{2,20}$/;
const flag = check.test(s);
if(flag){
$("#username").css("border","");
}else{
$("#username").css("border","1px solid red");
}
return flag;
}
// 单词字符,8-12
function checkPassword(){
const s = $("#password").val();
const check = /^\w{4,20}$/;
const flag = check.test(s);
if(flag){
$("#password").css("border","");
}else{
$("#password").css("border","1px solid red");
}
return flag;
}
// email校验
function checkEmail(){
const s = $("#email").val();
//定义正则 itcast@163.com
const check = /^\w+@\w+\.\w+$/;
const flag = check.test(s);
if(flag){
$("#email").css("border","");
}else {
$("#email").css("border","1px solid red");
}
return flag;
}
//验证验证码
function checkCode(){
const s = $("#vaildateCode").val();
let flag = false;
if(s!=null){
$("#vaildateCode").css("border","");
flag = true;
}else {
$("#vaildateCode").css("border","1px solid red");
}
//todo 在数据库是否存在 鼠标焦点丢失 使用ajax验证在数据库是否存在相同的邮箱
return flag;
}
$(function () {
// 当表单提交时调用所有的校验方法
$("#registerForm").submit(function () {
// 1.发送数据到服务器
if (
checkUsername() && // 用户名校验
checkPassword() && // 密码校验
checkEmail() && // 邮箱校验
checkCode() // 验证码校验
) { // 校验成功
// 发送AJAX请求,提交表单数据 username=Alex&password=123 ...
$.post("user/register", $(this).serialize(), function (data) {
// 处理响应数据 data {flag:true/false, errorMsg:"..."}
if (data["flag"]) { // 注册成功
// 跳转成功页面
location.href = "register_ok.html";
} else { // 注册失败
// 在注册页面添加提示信息
$("#errorMsg").html(data["errorMsg"]);
}
});
} else { // 校验失败
}
// 2.跳转页面
return false;
});
// 当某一个组件失去焦点时,调用对应的校验方法
$("#username").blur(checkUsername);
$("#password").blur(checkPassword);
$("#email").blur(checkEmail);
$("#name").blur(checkName);
$("#telephone").blur(checkTelephone);
$("#sex").blur(checkSex);
$("#birthday").blur(checkBirthday);
$("#check").blur(checkCode);
})
使用ajax异步验证邮箱和用户名是否唯一
$(function () {
$("#username").change(function () {
//使用ajax 做username 的异步验证 checkUsername?username=xxxx
$.get("user.do?action=checkUserName", "username=" + this.value, function (data) {
if (data == 1) {
$("#usernameMsg").html("用户名已经存在").css("color", "red");
$("#registerBtn").attr("disabled", true);
} else {
$("#usernameMsg").html("用户名可用").css("color", "green");
$("#registerBtn").removeAttr("disabled");
}
})
});
})
//异步校验邮箱
$(function () {
$("#email").change(function () {
//使用ajax 做username 的异步验证 checkUsername?username=xxxx
$.get("user.do?action=checkUserEmail", "email=" + this.value, function (data) {
if (data == 1) {
$("#emailMsg").html("邮箱已经存在").css("color", "red");
$("#registerBtn").attr("disabled", true);
} else {
$("#emailMsg").html("邮箱可用").css("color", "green");
$("#registerBtn").removeAttr("disabled");
}
})
});
})
//验证码点击改变函数
function changeValidateCode(img) {
img.src = "captcha.do?d" + new Date().getTime();
}
注册完整页面代码
数据控制层IUserDao接口定义如下
public interface IUserDao extends IBaseDao<User> {
int findDataByUser(User user);
int updateUserState(User user);
}
IUserDao接口实现类UserDaoImpl类的代码如下,用于完成对注册用户的信息的检查以及注册信息是否合法
public class UserDaoImpl implements IUserDao {
private QueryRunner queryRunner = null;
public UserDaoImpl() {
queryRunner = new QueryRunner();
}
/**
* 验证用户名和邮箱是否存在
* @param user
* @return
*/
@Override
public int findDataByUser(User user) {
int n = -1;
String sql = "select * from tb_user where 1=1";
String parms = "";
if (user.getEmail() != null && !user.getEmail().equals("")) {
sql += " and email=? ";
parms = user.getEmail();
} else if (user.getUsername() != null && !user.getUsername().equals("")) {
sql += " and username=? ";
parms = user.getUsername();
} else {
return 100;
}
try {
User u = queryRunner.query(DBUtils.getConn(), sql, parms, new BeanHandler<>(User.class));
if (u != null) {
n = 5;
}
} catch (SQLException e) {
e.printStackTrace();
}
return n;
}
/**
* 修改用户状态
* @param user
* @return
*/
@Override
public int updateUserState(User user) {
int n=-1;
String sql="update tb_user set flag=? where code=? and email=?";
try {
n = queryRunner.update(DBUtils.getConn(), sql,user.getFlag(),user.getCode(), user.getEmail());
} catch (SQLException e) {
e.printStackTrace();
}
return n;
}
/**
* 用户数据添加
* @param user
* @return
*/
@Override
public int addData(User user) {
int n = -1;
String sql = "insert into tb_user(username,password,email,gender,flag,role,code) values(?,?,?,?,?,?,?)";
try {
n = queryRunner.update(DBUtils.getConn(), sql, user.getUsername(), user.getPassword(),
user.getEmail(), user.getGender(), user.getFlag(), user.getRole(), user.getCode());
} catch (SQLException e) {
e.printStackTrace();
}
return n;
}
@Override
public int updateData(User user) {
return 0;
}
@Override
public int deleteById(int id) {
return 0;
}
@Override
public User findDataById(int id) {
return null;
}
@Override
public List<User> getAllDatas() {
return null;
}
}
业务处理层IUserService接口定义如下
public interface IUserService extends IBaseService<User>{
boolean queryDataByUser(User user);
boolean activate(User user);
}
该接口的实现类UserServiceImpl代码如下 用于链接视图层和数据处理层,根据数据控制层的处理结果对视图出的请求回应不同的处理结果
public class UserServiceImpl implements IUserService {
private IUserDao userDao = new UserDaoImpl();
@Override
public boolean queryDataByUser(User user) {
int n = userDao.findDataByUser(user);
return n >= 1 ? true : false;
}
@Override
public boolean activate(User user) {
int n = userDao.updateUserState(user);
return n >= 1 ? true : false;
}
/**
* 用户注册逻辑
*
* @param user
* @return
*/
@Override
public boolean saveData(User user) {
boolean flag = false;
//这里需要二次验证用户名邮箱
User u1 = new User();
u1.setUsername(user.getUsername());
User u2 = new User();
u2.setEmail(user.getEmail());
if (!this.queryDataByUser(u1) && !this.queryDataByUser(u2)) {
//密码md5加密
String md5 = MD5Utils.md5(user.getPassword());
user.setPassword(md5);
//生成激活码
String activeCode = RandomUtils.createActive();
user.setCode(activeCode);
if (userDao.addData(user) >= 1) {
//发激活提示邮件
EmailUtils.sendEmail(user);
flag = true;
}
}
return flag;
}
@Override
public boolean editData(User user) {
return false;
}
@Override
public boolean removeData(int id) {
return false;
}
@Override
public User queryDataById(int id) {
return null;
}
@Override
public List<User> queryAllData() {
return null;
}
}
UserServlet类增加了对注册页面校验注册是否合法的不同验证方法
@WebServlet("/user.do")
public class UserServlet extends BaseServlet {
private ResultData resultData = new ResultData();
/**
* 用户登录测试
*
* @param req
* @param resp
* @return
* @throws ServletException
* @throws IOException
*/
public String login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//http://localhost:8080/user.do?action=login
return "TODO 这是用户登录功能";
}
/**
* 验证用户名
*
* @param req
* @param resp
* @return
* @throws ServletException
* @throws IOException
*/
public String checkUserName(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
IUserService userService = new UserServiceImpl();
User user = new User();
String username = req.getParameter("username");
user.setUsername(username);
boolean flag = userService.queryDataByUser(user);
return flag == false ? "0" : "1";
}
/**
* 注册功能
*
* @param req
* @param resp
* @return
* @throws ServletException
* @throws IOException
*/
public String register(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
IUserService userService = new UserServiceImpl();
User user = new User();
// String username = req.getParameter("username");
// user.setUsername(username);
// String password = req.getParameter("password");
// user.setPassword(password);
// String email = req.getParameter("email");
// user.setEmail(email);
// String gender = req.getParameter("gender");
// user.setGender(gender);
this.analysisFromToObject(req,user);
String code = req.getParameter("code");
String msg = "";
resultData.setFlag(false);
if (code != null && !"".equals(code)) {
//对比验证码
String codeVd = (String) req.getSession().getAttribute("code");
if (codeVd.equalsIgnoreCase(code)) {
//调用业务层方法
if (userService.saveData(user)) {
resultData.setFlag(true);
} else {
msg = "注册失败";
}
} else {
msg = "验证码错误";
}
} else {
msg = "验证码不能为空";
}
resultData.setErrorMsg(msg);
String json = JSON.toJSONString(resultData);
return json;
}
/**
* 验证邮箱
*
* @param req
* @param resp
* @return
*/
public String checkUserEmail(HttpServletRequest req, HttpServletResponse resp) {
IUserService userService = new UserServiceImpl();
User user = new User();
String email = req.getParameter("email");
user.setEmail(email);
boolean flag = userService.queryDataByUser(user);
return flag == false ? "0" : "1";
}
/**
* 邮箱激活
* @param req
* @param resp
* @return
*/
public String activate(HttpServletRequest req, HttpServletResponse resp) {
String mesg="用户激活失败,请重试";
String emailB64=req.getParameter("e");
String email= Base64Utils.decode(emailB64);
String codeB64=req.getParameter("c");
String code=Base64Utils.decode(codeB64);
IUserService userService=new UserServiceImpl();
User user=new User();
user.setEmail(email);
user.setCode(code);
//如果有html链接内容输出要设置响应
resp.setContentType("text/html;charset=utf-8");
user.setFlag(1);
if (userService.activate(user)){
mesg="用户激活成功,请<a href='login.html'>登录</a>";
mesg= Constants.FORWARD+"message.html";
}
return mesg;
}
}
其中还用到了一些工具类
如有需要可到gitee仓库中寻找
;
String codeB64=req.getParameter(“c”);
String code=Base64Utils.decode(codeB64);
IUserService userService=new UserServiceImpl();
User user=new User();
user.setEmail(email);
user.setCode(code);
//如果有html链接内容输出要设置响应
resp.setContentType("text/html;charset=utf-8");
user.setFlag(1);
if (userService.activate(user)){
mesg="用户激活成功,请<a href='login.html'>登录</a>";
mesg= Constants.FORWARD+"message.html";
}
return mesg;
}
}
其中还用到了一些工具类
如有需要可到我的gitee仓库中寻找
链接地址:[仿小米商场gitee仓库链接](https://gitee.com/playliwen/mi_-shop.git)