JavaWeb-仿小米商场(3):登录与退出
1 业务描述
接上篇仿小米商场(2):用户注册,本篇博客将分析和实现用户登录与退出。登录是后台获取当前访客身份的方式,也是提供个性化服务的基础。
执行登录时:用户在访问网站的任意页面时均可点击<header></header> 区域中的登录按钮跳转到登录页面进行登录操作。相应的前端页面如下图
登录页面的主要操作在右侧的一系列输入栏完成,用户根据前端提供的引导信息,在对应的输入栏内填写自己在注册时预留的 账号
和 密码
和 验证码
,然后后点击 登录
按钮后完成身份信息提交。后端经一系列身份核验逻辑后向前端反馈登陆结果。
执行退出时:用户只需点击<header></header> 区域中的退出按钮即可实现退出登陆的功能。
2 业务分析
2.1 业务流程抽象
2.1.1 登录信息核验
登录需要用户在前端页面中填充个人身份信息和验证码并提交。这些内容在前端被 <form></form> 标签包裹。为 登录 绑定一个单机事件,当用户点击 登录 时,向后端发送一个 AJAX 形式的 POST 请求将表单数据提交至后端处理。
后端在接收到请求数据后,首先做 验证码 校验,在本案例中,验证码由后端生成并完成校验。然后再核对其它身份信息,核对方式为:
将表单信息作为数据库查询条件向 tab_user 表中查询记录
若能从数据库中查询到用户信息,则后端会获取一个 User 对象,否则会获取 null,登录失败,在登陆页面反馈失败信息。
获取 User 对象后,还要进一步核验其 status 属性是否为 1状态(激活):是则,将 User 对象加入到 Session 对象中,登录成功;否则登录失败,在登陆页面反馈失败信息。
2.1.2 用户信息展示
在登录成功的情况下,会在网站中任意页面的 <header> 标签中显示登录用户的个人信息。前端的代码中已经将 <header> 部分单独定义在一个 header.html 文件中,每个页面都会加载这个文件来渲染页面信息。
可以在 <header> 标签中定义一个 AJAX 形式的 GET 请求,后端专门定义一个查询方法 findOne() 来负责此请求,该方法可以直接从 Session 对象中获取登录用户对象,并将用户的信息回写给前端。前端在收到用户信息后,将页面渲染切换至已登录状态的渲染形式。
2.1.2 退出登录
对于此功能,将前端的 <header> 标签中的 退出 文本使用 <a> 标签包围。点击 退出 时会向后端发送一个 GET 请求,在后端定义一个退出方法 exit() 负责此请求,该方法会从 Session 对象中删除 User 对象,并重定向至登录页面。
2.2 可能的技术难点与解决策略
登录状态判定逻辑链
3 代码实现
3.1 登录信息校验
3.1.1 登录页面(login.html)校验函数
$(function () {
//1.验证用户名是否存在
$("#username").change(function () {
$.get("user.do?action=checkUserName", "username=" + this.value, function (data) {
if (data == 0) {
$("#nameMsg").html("用户名不存在").css("color", "red");
} else {
$("#nameMsg").html("");
}
})
});
//2.点击验证码 跟新验证码
$("#pagecode").click(function () {
$("#pagecode").attr("src", "captcha.do?d=" + Math.random());
});
//3.验证输入的验证码 是否正确
$("#vcode").change(function () {
$.get("user.do?action=checkCode", "code=" + this.value, function (data) {
if (data == 0) {
$("#checkMsg").html("<font color='green'>OK</font>");
$("#btn").removeAttr("disabled");
} else {
$("#checkMsg").html("<font color='red'>ERROR</font>");
$("#pagecode").attr("src", "captcha.do?d=" + Math.random());
$("#btn").Attr("disabled", true);
}
})
});
//4.两周以内自动登录 友好提示
$("#autoLogin").click(function () {
if (this.checked) {
$("#autoLoginMsg").html("公司电脑请勿勾选此项").css("color", "red");
} else {
$("#autoLoginMsg").html("");
}
})
})
3.1.2 登录页面(login.html)ajax代码
$(function () {
// 当表单提交时调用所有的校验方法
$("#userLogin").submit(function () {
// 1.发送数据到服务器
// 校验成功
// 发送AJAX请求,提交表单数据 username=Alex&password=123 ...
$.post("user.do?action=register", $(this).serialize(), function (data) {
//需要将字符串转json
// var jsObj = JSON.parse(data)
// 处理响应数据 data {flag:true/false, errorMsg:"..."}
if (data.flag === false) { // 注册成功
// 跳转成功页面
location.href = "index.html";
} else { // 注册失败
// 在注册页面添加提示信息
$("#logMsg").html(data.errorMsg);
}
});
// 2.跳转页面
return false;
});
});
3.1.3用户登录验证和验证用户是否登录方法方法
@WebServlet("/user.do")
public class UserServlet extends BaseServlet {
//..../
private ResultData resultData = new ResultData();
/**
* 用户登录验证
*
* @param req
* @param resp
* @return
* @throws ServletException
* @throws IOException
*/
public String userLogin(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resultData.setFlag(false);
IUserService userService = new UserServiceImpl();
String username = req.getParameter("username");
String password = req.getParameter("password");
String auto = req.getParameter("auto");
System.out.println("auto===" + auto);
User user = userService.userLogin(username, password);
Cookie[] cok = req.getCookies();
if (cok != null) {
for (Cookie ck : cok) {
if (ck.getName().equals(user)) {
if (ck.getValue().equals(password)) {
}
}
}
}
if (user != null) {
req.getSession().setAttribute(Constants.LOGINUSER, user);
resultData.setFlag(true);
//判断是否需要存储登录凭证
Cookie[] cookies = req.getCookies();
if (auto != null) {
String nameStr = Base64Utils.encode(username + Constants.SALT);//不安全加密,建议使用加盐机制
String pwdStr = Base64Utils.encode(password + Constants.SALT);
Cookie ck = new Cookie("username", nameStr);
Cookie pwd = new Cookie("pwd", pwdStr);
pwd.setMaxAge(24 * 60 * 60 * 14);
ck.setMaxAge(24 * 60 * 60 * 14);
resp.addCookie(ck);
resp.addCookie(pwd);
}
// if (cookies.)
} else {
resultData.setErrorMsg("登录失败,密码错误");
}
String json = JSON.toJSONString(resultData);
System.out.println(json);
resp.setContentType("application/json;charset=utf-8");
return json;
}
//退出方法
public String exit(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getSession().invalidate();
return Constants.REDIRECT + "/login.html";
}
/**
* 验证用户是否登录
*
* @param req
* @param resp
* @return
* @throws ServletException
* @throws IOException
*/
public String checkUserLogin(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//todo 如果要在自动登录,需要取得cookid
//如果要保证session是否有凭证
//Cookie[] cookies = req.getCookies();
/* if (cookies != null) {
for (Cookie ck : cookies) {
if (ck.getName().equals("username")) {
}
}
}*/
User user = (User) req.getSession().getAttribute(Constants.LOGINUSER);
if (user != null) {
resultData.setFlag(true);
resultData.setData(user);
}
String json = JSON.toJSONString(resultData);
System.out.println(json);
resp.setContentType("application/json;charset=utf-8");
return json;
}
//..../
}
3.1.4 用户登录操作
用户登录成功后跳到首页,首页头部状态改为以登录用户名
首页index.html代码
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="css/login2.css">
<link href="css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="js/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/login2.css">
<link href="css/bootstrap.min.css" rel="stylesheet">
<title>小米商城首页</title>
<!---引入公共的头部-->
<script type="text/javascript" src="./js/lwHeader.js">
</script>
</head>
<body>
<div id="headtop">
</div>
<div id="second">
</div>
<!--网站中间内容开始-->
<div id="thred">
<img src="image/banner2.jpg" width="1230" height="460" />
</div>
<div id="forth">
<span>
<a href=""><img src="image/hjh_01.gif" /></a>
<a href=""><img src="image/hjh_02.gif" /></a>
<a href=""><img src="image/hjh_03.gif" /></a>
<a href=""><img src="image/hjh_04.gif" /></a>
<a href=""><img src="image/hjh_05.gif" /></a>
<a href=""><img src="image/hjh_06.gif" /></a>
</span>
<a href="" id="a_left"><img src="image/hongmi4x.png" width="316" height="170" /></a>
<a href="" id="a_left"><img src="image/xiaomi5.jpg" width="316" height="170" /></a>
<a href="" id="a_left"><img src="image/pinghengche.jpg" width="316" height="170" /></a>
</div>
<div id="fifth">
<span id="fif_text">小米明星单品</span>
</div>
<div id="sixth">
<span style="margin-left:0px; border-top:#ffa500 1px solid">
<a href="" id="siximg"><img src="image/pinpai1.png" width="234" height="234" /></a>
<a href="" id="na">小米MIX</a>
<p id="chip">5月9日-21日享花呗12期分期免息</p>
<p id="pri">3499元起</p>
</span>
<span style=" border-top:#008000 1px solid">
<a href="" id="siximg"><img src="image/pinpai2.png" width="234" height="234" /></a>
<a href="" id="na">小米MIX</a>
<p id="chip">5月9日-21日享花呗12期分期免息</p>
<p id="pri">3499元起</p>
</span>
<span style="border-top:#0000ff 1px solid">
<a href="" id="siximg"><img src="image/pinpai3.png" width="234" height="234" /></a>
<a href="" id="na">小米MIX</a>
<p id="chip">5月9日-21日享花呗12期分期免息</p>
<p id="pri">3499元起</p>
</span>
<span style="border-top:#ff0000 1px solid">
<a href="" id="siximg"><img src="image/pinpai4.png" width="234" height="234" /></a>
<a href="" id="na">小米MIX</a>
<p id="chip">5月9日-21日享花呗12期分期免息</p>
<p id="pri">3499元起</p>
</span>
<span style="border-top:#008080 1px solid">
<a href="" id="siximg"><img src="image/pinpai5.png" width="234" height="234" /></a>
<a href="" id="na">小米MIX</a>
<p id="chip">5月9日-21日享花呗12期分期免息</p>
<p id="pri">3499元起</p>
</span>
</div>
<!-- 底部 -->
<!--网站版权部分开始-->
<div id="seventh">
<p id="sevep">小米商城|MIUI|米聊|多看书城|小米路由器|视频电话|小米天猫店|小米淘宝直营店|小米网盟|小米移动|隐私政策|Select Region</p>
<p id="sevep">©mi.com 京ICP证110507号 京ICP备10046444号 京公网安备11010802020134号 京网文[2014]0059-0009号</p>
<p id="sevep">违法和不良信息举报电话:185-0130-1238,本网站所列数据,除特殊说明,所有数据均出自我司实验室测试</p>
</div>
</body>
</html>
因为多个页面需要根据登录状态改变头部导航栏
所以吧头导航栏封装成一个页面(header.html页面)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="css/login2.css">
<link href="css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="js/jquery-1.12.4.js"></script>
<title>头部</title>
<script type="text/javascript">
$(document).ready(function () {
//获取用户登录状态的请求
$.get("user.do?action=checkUserLogin", "", function (data) {
if (data.flag == true) {//登录
var temp = '欢迎回来,<a href="userAddress?flag=show" id="a_top">'+data.data.username+'</a>' +
'<li>|</li>' +
'<a href="userservlet?method=logOut" id="a_top">注销</a> ' +
'<li>|</li>' +
'<a href="getOrderList" id="a_top">我的订单</a> ' +
' <li>|</li> ' +
'<a href="userservlet?method=getAddress" id="a_top">地址管理</a>' +
'<a href="" id="a_top">消息通知</a> ' +
'<a href="cartservlet?method=getCart" id="shorpcar">购物车</a> ';
$("#showuser").html(temp);
} else {
var temp = '<a href="login.html" id="a_top">登录</a> ' +
'<li>|</li>' +
'<a href="register.html" id="a_top">注册</a> ' +
'<a href="" id="a_top">消息通知</a> ' +
'<a href="cartservlet?method=getCart" id="shorpcar">购物车</a> ';
$("#showuser").html(temp);
}
})
//获取商品类别的
/* $.ajax({
url: "${pageContext.request.contextPath}/goodsTypeAjax",
type: "GET",
dataType: "json",
success: function (data) {
for (var i in data) {
var a = $("<a href='${pageContext.request.contextPath}/getGoodsListByTypeId?typeid=" + data[i].id + "'>" + data[i].name + "</a>");
$("#goodsType").append(a);
}
},
error: function () {
//alert("失败");
}
})*/
})
</script>
</head>
<body>
<div id="top">
<div id="topdiv">
<span>
<a href="index.jsp" id="a_top" target="_blank">小米商城</a>
<li>|</li>
<a href="" id="a_top">小米商城移动版</a>
<li>|</li>
<a href="" id="a_top">问题反馈</a>
</span>
<!--根据登录状态显示用户菜单-->
<span id="showuser" style="float:right">
</span>
</div>
</div>
<div id="second">
<a href="" id="seimg" style=" margin-top:23px;"><img id="logo" src="image/logo_top.png" width="55" height="54"/></a>
<a href="" id="seimg" style=" margin-top:17px;"><img id="gif" src="image/yyymix.gif" width="180" height="66"/></a>
<p id="goodsType">
<!-- 根据ajax 回调函数 填写数据 到此id中 -->
</p>
<form class="form-inline pull-right" style="margin-top: 40px;margin-right: 10px;">
<div class="form-group">
<input type="text" class="form-control" style="width: 400px" placeholder="搜索一下好东西...">
</div>
<button type="submit" class="btn btn-warning"><span class="glyphicon glyphicon-search"></span> 搜索
</button>
</form>
</div>
</body>
</html>
然后编写一个javaSript脚本,脚本的作用为吧header页面加载到引用该脚本的页面
javaScript代码如下
$(function () {
//前端的包含
$.get("header.html",function (data){
$("#headtop").html(data)
});
})
如果有头部导航栏的页面 可以通过应用该脚本代码,加载头部区域,记得加上jquery框架 和id名为headtop的dib标签
示例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XYzQKW9V-1652363170213)(D:\鲲鹏培训\项目\仿小米商城\项目编写过程截图\引用脚本示例.png)]
搜索
然后编写一个javaSript脚本,脚本的作用为吧header页面加载到引用该脚本的页面
javaScript代码如下
$(function () {
//前端的包含
$.get("header.html",function (data){
$("#headtop").html(data)
});
})
如果有头部导航栏的页面 可以通过应用该脚本代码,加载头部区域,记得加上jquery框架 和id名为headtop的dib标签
示例
把所有需要加载头部导航栏的页面都跟换成这种方式.