项目课程链接:https://www.bilibili.com/video/BV1CE411E7h4
完整课程连接:https://www.bilibili.com/video/BV1uJ411k7wy
接上篇黑马旅游网(2):用户注册,本篇博客将分析和实现用户登录与退出。登录是后台获取当前访客身份的方式,也是提供个性化服务的基础。
执行登录时:用户在访问网站的任意页面时均可点击
区域中的登录按钮跳转到登录页面进行登录操作。相应的前端页面如下方H5页面所示:
登录页面的主要操作在右侧的一系列输入栏完成,用户根据前端提供的引导信息,在对应的输入栏内填写自己在注册时预留的 账号
和 密码
和 验证码
,然后后点击 登录
按钮后完成身份信息提交。后端经一些列身份核验逻辑后向前端反馈登陆结果。
执行退出时:用户只需点击
区域中的退出
按钮即可实现退出登陆的功能。
另外,登录页面中还预留了自动登录
功能,但是课程中并没有讲如何实现。笔者在完成全部的课上内容后也将这个功能实现了。这个功能不算难,需要额外添加的代码也非常少,但是思考并实现这个功能的过程还是蛮值得回味的。我打算在后面单独写一篇博客分析这个功能,届时也分享一下自己对 Session 和 Cookie 的理解吧。
登录需要用户在前端页面中填充个人身份信息和验证码并提交。这些内容在前端被 标签包裹。为
登录
绑定一个单机事件,当用户点击 登录
时,向后端发送一个 AJAX 形式的 POST 请求将表单数据提交至后端处理。
后端在接收到请求数据后,首先做 验证码
校验,在本案例中,验证码由后端生成并完成校验。然后再核对其它身份信息,核对方式为:
tab_user
表中查询记录Y
状态(激活):是则,将 User 对象加入到 Session 对象中,登录成功;否则登录失败,在登陆页面反馈失败信息。在登录成功的情况下,会在网站中任意页面的
标签中显示登录用户的个人信息。前端的代码中已经将
部分单独定义在一个 header.html 文件中,每个页面都会加载这个文件来渲染页面信息。
可以在
标签中定义一个 AJAX 形式的 GET 请求,后端专门定义一个查询方法 findOne() 来负责此请求,该方法可以直接从 Session 对象中获取登录用户对象,并将用户的信息回写给前端。前端在收到用户信息后,将页面渲染切换至已登录状态的渲染形式。
对于此功能,将前端的
标签中的 退出
文本使用 标签包围。点击
退出
时会向后端发送一个 GET 请求,在后端定义一个退出方法 exit() 负责此请求,该方法会从 Session 对象中删除 User 对象,并重定向至登录页面。
登录状态判定逻辑链
$(function () {
// 1.给登录按钮绑定事件
$("#btn_sub").click(function () {
// 2.发送ajax请求,提交表单数据
$.post("user/login", $("#loginForm").serialize(), function (resultInfo) {
// resultInfo: {flag:true/false, errorMsg, "..."}
// 3.处理响应结果
if (resultInfo["flag"]) { // 登陆成功
location.href = "index.html";
} else { // 登陆失败
$("#errorMsg").html(resultInfo["errorMsg"]);
}
});
});
});
/**
* 用户登陆方法
*/
public void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
ResultInfo resultInfo = new ResultInfo();
HttpSession session = request.getSession();
// 0.验证码核对
String checkCode_browser = request.getParameter("check"); // 获取客户端浏览器提交的验证码
String checkCode_server = (String) session.getAttribute("CheckCode_Server"); // 获取服务器生成的验证码
if (!checkCode_server.equalsIgnoreCase(checkCode_browser)) { // 验证失败
resultInfo.setFlag(false);
resultInfo.setErrorMsg("验证码错误!");
// 回写客户端浏览器
this.writeValue(resultInfo, response);
return;
} // else 验证通过
// 1.获取用户信息
Map<String, String[]> parameterMap = request.getParameterMap();
// 2.封装 User Bean 对象
User user = new User();
try {
BeanUtils.populate(user, parameterMap);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
// 3.调用service查询user
User loginUser = service.login(user);
// 4.判断用户是否存在
if (loginUser == null) { // 用户不存在
resultInfo.setFlag(false);
resultInfo.setErrorMsg("用户名或密码错误!");
} // else 用户存在
// 5.判断用户是否激活
if (loginUser != null && !"Y".equals(loginUser.getStatus())) { // 用户未激活
resultInfo.setFlag(false);
resultInfo.setErrorMsg("您尚未未激活,请激活。");
} // else 用户已激活
// 6.用户名和密码正确且已激活,跳转网站首页
if (loginUser != null && "Y".equals(loginUser.getStatus())) {
resultInfo.setFlag(true);
session.setAttribute("user", loginUser);
}
this.writeValue(resultInfo, response);
}
/**
* 登录用户
* @param user 用户名
* @return
* true:激活成功
* false:激活失败
*/
@Override
public User login(User user) {
return userDao.findByUsernameAndPassword(user.getUsername(), user.getPassword());
}
/**
* 根据用户名和密码查询用户信息
* 登陆验证
* @param username String 用户名
* @param password String 密码
* @return
* 查询成功:User Bean 对象
* 查询失败:null
*/
@Override
public User findByUsernameAndPassword(String username, String password) {
User user = null;
try {
String sql = "SELECT * FROM tab_user WHERE username = ? AND password = ?";
user = template.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), username, password);
} catch (DataAccessException e) {
e.printStackTrace();
}
return user;
}
/**
* 查询用户信息
*/
$.get("user/findOne", {}, function (user) {
// user: {uid:...,username:...}
let msg = "欢迎回来," + user["username"];
$("#span_username").html(msg);
});
/**
* 查询登录用户信息方法,查询结果在index.html中的header上显示
*/
public void findOne(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 从session中获取登录用户
User user = (User) request.getSession().getAttribute("user");
// 将user写回客户端
this.writeValue(user, response);
}
header.html
<!-- 登录状态 -->
<div class="login">
<span id="span_username"></span>
<a href="myfavorite.html" class="collection">我的收藏</a>
<a href="user/exit">退出</a> <!--向后端发送退出登录请求-->
</div>
/**
* 退出登录方法
*/
public void exit(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 1.销毁session
request.getSession().invalidate();
// 2.跳转至登陆页面
response.sendRedirect(request.getContextPath() + "/login.html");
}
部分代码并未完全展示,完整代码可以参考我的 GitHub 仓库