案例:登录中输入验证码(会话技术)
1.案例需求:1.访问带有验证码的登录页面login.jsp2.用户输入用户名,密码以及验证码如果用户和密码输入有误,跳转登录页面。提示:用户或密码错误如果验证码输入有误,跳转登录页面,提示:验证码有误如果全部输入正确,跳转到success.jsp页面,显示:用户名,欢迎您
2.案例分析:1.设置request的编码2.获取请求参数3.获取session中的验证码此处会出现验证码反复重用的可能因为图片资源会被浏览器缓存,所以不会再次向服务器请求生成新验证码所以当用户再次输入相同的验证码,直接会拿session中的对比解决方案:获取到session中的程序生成的验证码后,立即删除4.封装表单中的请求数据到Bean5.判断程序生成的验证码和用户输入的是否一致一致:判断用户名密码是否一致一致:存储User数据到session重定向到sucess.jsp不一致:1.给提示信息2.转发到登录页面不一致:1.给用户提示信息2.转发到登录页面
案例分析图解:
3.涉及的知识点:checkCode验证码生成案例相关知识StringBuilder对象及其append(char c):添加字符数据 -->该方法有多种重载HttpSession对象及其getSession():获取对象方法setAttribute(String s, Object obj):设置共享数据getAttribute(String s):获取共享的数据removeAttribute(String s):删除共享的数据Request对象及其setCharacterEncoding():设置请求数据的字符集请求参数通用方法:如 getParameter() getParameterMap()getContextPath():获取项目的虚拟目录setAttribute():设置request域的共享数据getRequestDispatcher():转发Response对象及其sendRedirect():重定向方法注意:session数据共享可以用于一次会话中的多次请求之间,所以可用在重定向中request数据共享只可以用于一次请求中,所以可以用于转发中
login.jsp(登录时的动态资源):
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>登录页</title>
</head><script>window.onload = function() {document.getElementById("img").onclick = function() {this.src= "/day16/checkCodeServlet?time=" + new Date().getTime();};}
</script><style>div {color: red;}
</style>
<body>
<form action="/day16/loginServlet" method="post"><table><tr><td>用户名</td><td><input type="text" name="username"></td></tr><tr><td>密码</td><td><input type="password" name="password"></td></tr><tr><td>验证码</td><td><input type="text" name="checkcode"></td></tr><tr><td colspan="2"><img src="/day16/checkCodeServlet" id="img" alt=""></td></tr><tr><td colspan="2"><input type="submit" value="登录"></td></tr></table><div><%= request.getAttribute("user_error") == null ? "" : request.getAttribute("user_error")%></div><div><%= request.getAttribute("checkcode_error") == null ? "" : request.getAttribute("checkcode_error")%></div>
</form>
</body>
</html>
success.jsp(登录成功的动态资源):
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>成功登录</title>
</head>
<body><h1>欢迎您回来,<%= request.getSession().getAttribute("user")%></h1>
</body>
</html>
LoginServlet(登录业务逻辑的动态资源):
package servlet;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 javax.servlet.http.HttpSession;
import java.io.IOException;@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 设置request编码request.setCharacterEncoding("utf-8");// 获取请求参数String username = request.getParameter("username");String password = request.getParameter("password");String checkcode = request.getParameter("checkcode");// 获取程序中生成的验证码字符串HttpSession session = request.getSession();String generatedCheckCode = (String) session.getAttribute("generatedCheckCode");// 保证验证码只可以使用一次session.removeAttribute("generatedCheckCode");// 判断验证码if (generatedCheckCode != null && generatedCheckCode.equalsIgnoreCase(checkcode)) {// 判断用户名和密码,假设已经让userDAO从数据库中获取到了相关数据if ("zhangsan".equals(username) && "123".equals(password)) {// 共享session中的数据(假设此处的值是从数据库获取的)session.setAttribute("user", "zhangsan");// 重定向到success页面String contextPath = request.getContextPath();response.sendRedirect(contextPath + "/success.jsp");} else {// 给提示信息并且转发回login.jsp// 转发为一次请求,所以可直接用request域对象共享数据request.setAttribute("user_error", "用户名或密码不正确");// 转发不需要用getContextPath()加虚拟目录request.getRequestDispatcher("login.jsp").forward(request, response);}} else {// 给提示信息并且转发回login.jsp// 转发为一次请求,所以可直接用request域对象共享数据request.setAttribute("checkcode_error", "验证码不正确");// 转发不需要用getContextPath()加虚拟目录request.getRequestDispatcher("login.jsp").forward(request, response);}}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);
}
}
CheckCodeServlet(验证码生成的动态资源):
package servlet;import javax.imageio.ImageIO;
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 javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {int width = 100;int height = 50;// 1.创建内存中的图片对象BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 生成随机数对象Random random = new Random();// 2.美化图片// 获取画笔对象Graphics graphics = bufferedImage.getGraphics();// 设置色彩graphics.setColor(Color.PINK);// 填充正方形graphics.fillRect(0, 0, width, height);// 设置色彩graphics.setColor(Color.BLUE);// 画边框graphics.drawRect(0, 0, width-1, height-1);StringBuilder sb = new StringBuilder();// 写字String code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";for (int i = 1; i <= 4; i++) {int index = random.nextInt(code.length());char c = code.charAt(index);sb.append(c);graphics.drawString(String.valueOf(c), width/5*i, height/2);}// 新增代码,作用是共享程序生产的验证码字符串String generatedCheckCode = sb.toString();HttpSession session = req.getSession();session.setAttribute("generatedCheckCode", generatedCheckCode);// 写干扰线for (int i = 0; i < 10; i++) {int x1 = random.nextInt(width);int x2 = random.nextInt(width);int y1 = random.nextInt(height);int y2 = random.nextInt(height);graphics.drawLine(x1, y1, x2, y2);}// 3.将图片输出到页面ImageIO.write(bufferedImage, "jpg", resp.getOutputStream());}@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req, resp);
}
}