1、对用户信息的描述
首先用户有一些基本信息:
最简单的:
用户名称 + 用户密码
然后是用户状态,例如封号,注销,停用,等等
用户名称 + 用户密码 + 账号状态
接着为了防止脚本攻击,又产生了图形码验证,为了区分人和机器
用户名称 + 用户密码 + 用户状态 + 图形验证码
2、过程设计:
所以按照最后的这种形式,我们设计一个登陆业务的过程
首先用户在界面上看到三个东西:
- 名称框
- 密码框
- 验证码框
由于账号状态由后台控制,所以这里不会出现,也不会给用户查看
用户名是否会产生重复?
这个问题可以在注册的时候进行业务控制,
也就是说,我们是在一个用户名不会重复的前提下设计的
第一点:
三个框框都是必须填入信息的,这是第一限制
第二点:
基于上面用户名不重复的前提上,如果用户输入不存在的用户名就应该限制,并作出提示,用户不存在
第三点:
根据用户提供的名称获取到对应的密码,就开始对密码进行匹配,如果错误,则限制,并作出提示,密码错误
第四点:
检查验证码是否匹配,如果错误则限制,并作出提示
而限制是在页面上实现,服务器则负责信息校验的问题:
所以我们的页面和服务器职责是明确的。
对用户的输入信息进行控制,符合第一道工序之后传输信息给服务器,
服务器根据提供的信息进行校验,然后反馈给页面,
页面对反馈信息进行对应的处理,实现页面的限制
所以数据库的用户表设计根据这个业务的需要
至少提供:
用户名|用户密码|账号状态|
验证码是随机生成的,不应该存储在数据库,且浪费存储资源
前置的总结:
但是我没有想到的是可以使用JS来处理页面之间的跳转
因为之前的案例都是通过表单完成,这个步骤完全没有JS来参与逻辑控制
然后后端的事情显得非常的明确,我要做的业务其实也只是查询需要的结果打包给前端
信息校验在业务中处理,然后返回
具体实现描述
JS在发送Ajax之前进行控制,给服务后由程序对数据库访问,查询的结果和用户输入比较
再来是验证码校验,然后返回信息给前端,只有对的JS页面跳转,其他弹窗警告
3、具体实现的代码:
验证码图片生成和保存验证码:
应该简称ImageIDServlet
package cn.dzz.servlet; import javax.imageio.ImageIO; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.PrintWriter; import java.util.Random; /** * @author ArkD42 * @file OA * @create 2020 - 06 - 18 - 10:01 */ @WebServlet("/verify") public class VerificationCodeServlet extends BaseServlet{ /** * /verify?act=genImage * @param request * @param response * @throws IOException */ public void genImage(HttpServletRequest request, HttpServletResponse response) throws IOException { // 设置图片的宽高 int width = 60, height = 20; // 创建具有可访问图像数据缓冲区的Image BufferedImage bufferedImage = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB); Graphics2D graphics2D = bufferedImage.createGraphics(); // 创建一个随机数生成对象 Random random = new Random(); graphics2D.setColor(Color.WHITE); graphics2D.fillRect(0, 0, width, height); // 创建字体,字体的大小应该根据图片的高度来定 Font font = new Font("微软雅黑", Font.PLAIN, 18); // 设置字体 graphics2D.setFont(font); // 画边框 graphics2D.setColor(Color.BLACK); graphics2D.drawRect(0, 0, width - 1, height - 1); // 随机产生160条干扰线 graphics2D.setColor(Color.LIGHT_GRAY); for (int i = 0; i < 160; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int x1 = random.nextInt(12); int y1 = random.nextInt(12); graphics2D.drawLine(x, y, x + x1, y + y1); } // randomCode 用于保存随机产生的验证码 StringBuffer randomCode = new StringBuffer(); int red = 0, green = 0, blue = 0; // 随机产生4位数字的验证码 for (int i = 0; i < 4; i++) { // 得到随机产生的验证码数字 String strRand = String.valueOf(random.nextInt(10)); // 产生随机的颜色分量来构造颜色值 red = random.nextInt(110); green = random.nextInt(50); blue = random.nextInt(50); // 用随机产生的颜色将验证码绘制到图像中 graphics2D.setColor(new Color(red, green, blue)); graphics2D.drawString(strRand, 13 * i + 6, 16); randomCode.append(strRand); } // 将四位数字的验证码保存到session中 request.getSession().setAttribute("randomCode", randomCode.toString()); System.out.println(this.getClass().getName() + " 生成的验证码:" + randomCode.toString()); // 禁止图像缓存 response.setHeader("Pragma", "no-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); response.setContentType("image/jpeg"); // 将图像输出到servlet输出流中 ServletOutputStream sos = response.getOutputStream(); ImageIO.write(bufferedImage, "jpeg", sos); sos.close(); } }
然后在登录页的事件绑定:
<img src="verify?act=genImage" id="verify_img" title="看不清?换一张" name="verify_img" align="middle" onclick="loadImage();return false;" /> <script type="text/javascript"> function loadImage(){ document.getElementById("verify_img").src="verify?act=genImage&time=" + new Date().getTime(); } </script>
在Dao层中需要的实现:
@Override public User queryUserByName(String username) { final String SQL = "SELECT * FROM t_user WHERE user_name = ?"; return JdbcForTxUtil.queryOne( TransactionManager.getCurrentThreadConnection(), User.class, SQL, new Object[]{username} ); }
然后是登陆业务实现:
@Override public JsonResult login(HttpServletRequest request) { // 验证码的验证 HttpSession session = request.getSession(); String randomCode = session.getAttribute("randomCode").toString(); String imgID = request.getParameter("imgID"); System.out.println(request.getRemoteAddr() + "用户输入的是:" + imgID + " 从Session得到的是:" + randomCode); if (!(randomCode.equals(imgID))) return new JsonResult(500,"验证码错误",null); // 用户信息验证 String username = request.getParameter("username"); String password = request.getParameter("password"); User user = userDao.queryUserByName(username); System.out.println(user); if (user == null) return new JsonResult(200,"没有此用户",null); else { if (!(password.equals(user.getUser_password()))) return new JsonResult(300,"密码错误",null); else if (user.getUser_status() != 1) return new JsonResult(400,"账号状态异常",null); } session.setAttribute("userInfo",user); return new JsonResult(100,"验证通过",null); }
登陆Servlet反馈给登录页
/** * /login?act=loginCheck * 处理从/WEB-INF/jsp/login.jsp页的表单发送的请求,进行信息校验 * @param request * @param response * @return */ public JsonResult loginCheck(HttpServletRequest request, HttpServletResponse response) { JsonResult result = loginService.login(request); return result; }
最后是由登录页的JS处理
/* 登陆验证 */ $(function () { $("#loginBtn").click(function () { let username= $("#username").val(); let password= $("#password").val(); let imgID= $("#imgID").val(); if(username === "" || password === "" || imgID === "") { alert("请输入用户或密码或验证码!!!"); } else { //ajax 登录 let url = "/login?act=loginCheck"; let obj = { username:username, password:password, imgID:imgID } $.ajax({ url: url, type: "post", data: obj, success: function (data) { /* 不要使用全等,JS会把对象和字符串比较类型,对不上就不走了 */ if (data.feedbackStatus == "100") location.href="/home"; // 状态100 验证通过 else if (data.feedbackStatus == "200") alert(data.messageses); // 状态200 用户不存在 else if (data.feedbackStatus == "300") alert(data.messageses); // 状态200 密码错误 else if (data.feedbackStatus == "400") alert(data.messageses); // 状态400 账号状态异常 else if (data.feedbackStatus == "500") alert(data.messageses); // 状态500 验证码错误 }, dataType:"json" }); } }); });