• 用户登录(记住用户)&用户注册(验证码) [运用Cookie Session技术]


    需求如下: (相比以前的登陆注册添加的新功能如下框)

    一、用户登录:(要求如下图)

    用户登录总代码结构如下:

     

    User实体类:

    1. package com.itheima.pojo;
    2. public class User {
    3. private Integer id;
    4. private String username;
    5. private String password;
    6. public Integer getId() {
    7. return id;
    8. }
    9. public void setId(Integer id) {
    10. this.id = id;
    11. }
    12. public String getUsername() {
    13. return username;
    14. }
    15. public void setUsername(String username) {
    16. this.username = username;
    17. }
    18. public String getPassword() {
    19. return password;
    20. }
    21. public void setPassword(String password) {
    22. this.password = password;
    23. }
    24. @Override
    25. public String toString() {
    26. return "User{" +
    27. "id=" + id +
    28. ", username='" + username + '\'' +
    29. ", password='" + password + '\'' +
    30. '}';
    31. }
    32. }

    UserServlet:

    1. package com.itheima.service;
    2. import com.itheima.mapper.UserMapper;
    3. import com.itheima.pojo.User;
    4. import com.itheima.util.SqlSessionFactoryUtils;
    5. import org.apache.ibatis.session.SqlSession;
    6. import org.apache.ibatis.session.SqlSessionFactory;
    7. /**
    8. * 业务逻辑层
    9. */
    10. public class UserService {
    11. SqlSessionFactory Factory =SqlSessionFactoryUtils.getSqlSessionFactory();
    12. /**
    13. * 登录方法
    14. */
    15. public User login(String username,String password){
    16. // 1. 获取SqlSession
    17. SqlSession sqlSession =Factory.openSession();
    18. UserMapper userMapper =sqlSession.getMapper(UserMapper.class);
    19. // 2. 调用UserMapper接口的查询用户和密码方法
    20. User user =userMapper.select(username,password);
    21. // 3. 释放资源
    22. sqlSession.close();
    23. return user;
    24. }
    25. }

    UserMapper:

    1. package com.itheima.mapper;
    2. import com.itheima.pojo.User;
    3. import org.apache.ibatis.annotations.Insert;
    4. import org.apache.ibatis.annotations.Param;
    5. import org.apache.ibatis.annotations.Select;
    6. public interface UserMapper {
    7. /**
    8. * 根据用户名和密码查询用户对象
    9. * @param username @param 参数占位符mybatis完成增删改查里面有解释
    10. * @param password
    11. * @return
    12. */
    13. @Select("select * from tb_user where username = #{username} and password = #{password}")
    14. User select(@Param("username") String username, @Param("password") String password);
    15. /**
    16. * 根据用户名查询用户对象
    17. * @param username
    18. * @return
    19. */
    20. @Select("select * from tb_user where username = #{username}")
    21. User selectByUsername(String username);
    22. /**
    23. * 添加用户
    24. * @param user
    25. */
    26. @Insert("insert into tb_user values(null,#{username},#{password})")
    27. void add(User user);
    28. }

     登录失败/成功结果演示:

    登录成功:

     

    登录失败: 

    记住一句话:重定向会改变URL路径 也就是说我们重定向中写的一个路径,URL会变成该写的路径,转发不改变URL路径

    核心代码如下所示:

    login.jsp:

    先访问login.jsp然后提交到loginServlet路径资源下 再继续其他的操作(转发)

    1. <%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
    2. <!DOCTYPE html>
    3. <html lang="en">
    4. <head>
    5. <meta charset="UTF-8">
    6. <title>login</title>
    7. <link href="css/login.css" rel="stylesheet">
    8. </head>
    9. <body>
    10. <div id="loginDiv" style="height: 350px">
    11. <form action="/brand-demo/loginServlet" id="form">
    12. <h1 id="loginMsg">LOGIN IN</h1>
    13. <div id="errorMsg">${login_msg}</div>
    14. <%--${login_msg} 就是我们在LoginServlet资源下登录失败后转发到login页面把
    15. 登录页面展示给用户,并且把转发时储存到request域当中的数据(用户名或密码错误)拿
    16. 到展示在登录页面上 ${login_msg}:EL表达式 拿储存在域中数据的
    17. --%>
    18. <p>Username:<input id="username" name="username" type="text"></p>
    19. <p>Password:<input id="password" name="password" type="password"></p>
    20. <p>Remember:<input id="remember" name="remember" type="checkbox"></p>
    21. <div id="subDiv">
    22. <input type="submit" class="button" value="login up">
    23. <input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;
    24. <a href="register.html">没有账号?</a>
    25. </div>
    26. </form>
    27. </div>
    28. </body>
    29. </html>

    LoginServlet:

    登录失败的话就跳跳转到login.jsp并携带着储存数据

    1. package com.itheima.web;
    2. import com.itheima.pojo.User;
    3. import com.itheima.service.UserService;
    4. import javax.servlet.ServletException;
    5. import javax.servlet.annotation.WebServlet;
    6. import javax.servlet.http.HttpServlet;
    7. import javax.servlet.http.HttpServletRequest;
    8. import javax.servlet.http.HttpServletResponse;
    9. import javax.servlet.http.HttpSession;
    10. import java.io.IOException;
    11. @WebServlet("/loginServlet")
    12. public class LoginServlet extends HttpServlet {
    13. @Override
    14. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    15. // 1、接收客户端用户名和密码
    16. String username =request.getParameter("username");
    17. String password =request.getParameter("password");
    18. // 2、调用service层进行查询
    19. UserService userService =new UserService();
    20. User user =userService.login(username,password);
    21. // 3、判断查询是否有结果
    22. if (user != null){
    23. // 2. 把user查询出来的数据先封装到Session域当中 (数据保存在了服务器之间共享)
    24. HttpSession httpSession =request.getSession();
    25. // 存储到Session域中
    26. httpSession.setAttribute("user",user);
    27. // 1.登录成功 (要求:动态重定向到MVC三层架构讲的商品增删改查操作:SelectAllServlet资源下查询所有)
    28. String path =request.getContextPath();
    29. response.sendRedirect(path+"/selectAllServlet");
    30. } else {
    31. // 登录失败
    32. // 储存错误提示信息到request域当中 转发给login.jsp
    33. request.setAttribute("login_msg","用户名或密码错误");
    34. // 跳转到登录的login.jsp页面
    35. request.getRequestDispatcher("/login.jsp").forward(request,response);
    36. }
    37. }
    38. @Override
    39. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    40. this.doGet(request, response);
    41. }
    42. }

    登录成功:把查询到的用户名和密码先储存在Session域当中(因为前几天刚学的Session所以这里用Session 其实同样储存在request域中转发到selectAllServlet路径下也可以),因为后面我们在用户登录成功展示出所有商品的时候要展示出 xxxx欢迎您 

    Session:服务器中数据共享 (随便转发或者重定向,资源路径下都共享着该Session数据)

    这里用重定向的原因是:重定向可以改变URL路径(也就是说我们访问的时候是login.jsp路径 然后URL重定向成selectAllServlet路径)

     

     selectAllServlet:(这里对商品的增删改查回显等 三层架构里面已经写过了)

    1. package com.itheima.web1;
    2. import com.itheima.pojo.Brand;
    3. import com.itheima.service.BrandService;
    4. import javax.servlet.ServletException;
    5. import javax.servlet.annotation.WebServlet;
    6. import javax.servlet.http.HttpServlet;
    7. import javax.servlet.http.HttpServletRequest;
    8. import javax.servlet.http.HttpServletResponse;
    9. import java.io.IOException;
    10. import java.util.List;
    11. @WebServlet("/selectAllServlet")
    12. public class SelectAllServlet extends HttpServlet {
    13. @Override
    14. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    15. // 1 调用BrandService完成查询
    16. BrandService brandService =new BrandService();
    17. List<Brand> brands =brandService.selectAll();
    18. // 2 把查询到的数据存入request域当中
    19. request.setAttribute("brands",brands);
    20. // 3 把存入的数据转发到brand.jsp页面中供用户查看数据
    21. request.getRequestDispatcher("/brand.jsp").forward(request,response);
    22. }
    23. @Override
    24. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    25. this.doGet(request, response);
    26. }
    27. }

    brand.jsp:

    1. <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
    2. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%-- 引入标签库 --%>
    3. <!DOCTYPE html>
    4. <html lang="en">
    5. <head>
    6. <meta charset="UTF-8">
    7. <title>Title</title>
    8. </head>
    9. <body>
    10. <%-- 拿到Session封装的数据 EL表达式通过$符可以拿到Session域中的数据和request域使用方法一样 --%>
    11. <h1>${user.username},欢迎您</h1>
    12. <input type="button" value="新增" id="add"><br>
    13. <hr>
    14. <table border="1" cellspacing="0" width="80%">
    15. <tr>
    16. <th>序号</th>
    17. <th>品牌名称</th>
    18. <th>企业名称</th>
    19. <th>排序</th>
    20. <th>品牌介绍</th>
    21. <th>状态</th>
    22. <th>操作</th>
    23. </tr>
    24. <c:forEach items="${brands}" var="brand" varStatus="status">
    25. <tr align="center">
    26. <td>${status.count}</td>
    27. <td>${brand.brandName}</td>
    28. <td>${brand.companyName}</td>
    29. <td>${brand.ordered}</td>
    30. <td>${brand.description}</td>
    31. <c:if test="${brand.status ==1}">
    32. <td> 启用 </td>
    33. </c:if>
    34. <c:if test="${brand.status ==0}">
    35. <td> 禁止 </td>
    36. </c:if>
    37. <%-- ?id 是把id传送给selectByIdServlet路径下的资源当中 --%>
    38. <td><a href="/brand-demo/selectByIdServlet?id=${brand.id}">修改</a>
    39. <a href="/brand-demo/deleteById?id=${brand.id}">删除</a></td>
    40. </tr>
    41. </c:forEach>
    42. </table>
    43. <script>
    44. <%-- 这里的 href是添加商品点击按钮后所进入的addBrand.jsp资源路径 --%>
    45. document.getElementById("add").onclick = function (){
    46. location.href = "/brand-demo/addBrand.jsp";
    47. }
    48. </script>
    49. </body>
    50. </html>

    二、记住用户

    写Cookie数据: 

     login.jsp:

    1. <%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
    2. <!DOCTYPE html>
    3. <html lang="en">
    4. <head>
    5. <meta charset="UTF-8">
    6. <title>login</title>
    7. <link href="css/login.css" rel="stylesheet">
    8. </head>
    9. <body>
    10. <div id="loginDiv" style="height: 350px">
    11. <form action="/brand-demo/loginServlet" id="form">
    12. <h1 id="loginMsg">LOGIN IN</h1>
    13. <div id="errorMsg">${login_msg}</div>
    14. <%--${login_msg} 就是我们在LoginServlet资源下登录失败后转发到login页面把
    15. 登录页面展示给用户,并且把转发时储存到request域当中的数据(用户名或密码错误)拿
    16. 到展示在登录页面上 ${login_msg}:EL表达式 拿储存在域中数据的
    17. --%>
    18. <p>Username:<input id="username" name="username" type="text"></p>
    19. <p>Password:<input id="password" name="password" type="password"></p>
    20. <%-- value 的作用就是在复选框中,假设选中了该复选框那么该复选框的值也就是该value的值
    21. 这里remember是复选框 当我们勾选后 该默认值为“1”
    22. --%>
    23. <p>Remember:<input id="remember" name="remember" value="1" type="checkbox"></p>
    24. <div id="subDiv">
    25. <input type="submit" class="button" value="login up">
    26. <input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;
    27. <a href="register.html">没有账号?</a>
    28. </div>
    29. </form>
    30. </div>
    31. </body>
    32. </html>

     LoginServlet:

    用户登录成功后,判断用户是否点了记住我,点了话就发送Cookie数据到客户端浏览器中,没点的话就正常执行登录功能呗,只不过没有发送Cookie数据而已

    1. package com.itheima.web;
    2. import com.itheima.pojo.User;
    3. import com.itheima.service.UserService;
    4. import javax.servlet.ServletException;
    5. import javax.servlet.annotation.WebServlet;
    6. import javax.servlet.http.*;
    7. import java.io.IOException;
    8. @WebServlet("/loginServlet")
    9. public class LoginServlet extends HttpServlet {
    10. @Override
    11. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    12. // 1、接收客户端用户名和密码
    13. String username =request.getParameter("username");
    14. String password =request.getParameter("password");
    15. // 获取复选框数据
    16. String remember =request.getParameter("remember");
    17. // 2、调用service层进行查询
    18. UserService userService =new UserService();
    19. User user =userService.login(username,password);
    20. // 3、判断查询是否有结果
    21. if (user != null){
    22. // 判断user不为null说明登录成功了
    23. // 判断用户是否勾选了记住我 remember
    24. // 这里用:"1".equals(remember) 而不用remember.equals("1")
    25. // 是为了防止空指针异常 因为remember有可能用户没勾选 为null 然后比较的话会空指针
    26. if ("1".equals(remember)){
    27. // 勾选了,发送Cookie
    28. // 1 创建Cookie对象
    29. Cookie c_username =new Cookie("username",username);
    30. Cookie c_password =new Cookie("password",password);
    31. // 设置Cookie数据在客户端存活的时间
    32. c_username.setMaxAge(60*60*24*60);
    33. c_password.setMaxAge(60*60*24*60);
    34. // 2 发送Cookie
    35. response.addCookie(c_username);
    36. response.addCookie(c_password);
    37. }
    38. // 2. 把user查询出来的数据先封装到Session域当中 (数据保存在了服务器之间共享)
    39. HttpSession httpSession =request.getSession();
    40. // 存储到Session域中
    41. httpSession.setAttribute("user",user);
    42. // 1.登录成功 (要求:动态重定向到MVC三层架构讲的商品增删改查操作:SelectAllServlet资源下查询所有)
    43. String path =request.getContextPath();
    44. response.sendRedirect(path+"/selectAllServlet");
    45. } else {
    46. // 登录失败
    47. // 储存错误提示信息到request域当中 转发给login.jsp
    48. request.setAttribute("login_msg","用户名或密码错误");
    49. // 跳转到登录的login.jsp页面
    50. request.getRequestDispatcher("/login.jsp").forward(request,response);
    51. }
    52. }
    53. @Override
    54. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    55. this.doGet(request, response);
    56. }
    57. }

    客户端/浏览器中携带了封装好的用户名和密码的Cookie数据:

     获取Cookie数据:(EL表达式)

     例如此时浏览器已经登录过一次了,也就是说携带了Cookie封装的数据了:

    此时我们login.jsp通过EL表达式获取该username和password数据:

    1. <%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
    2. <!DOCTYPE html>
    3. <html lang="en">
    4. <head>
    5. <meta charset="UTF-8">
    6. <title>login</title>
    7. <link href="css/login.css" rel="stylesheet">
    8. </head>
    9. <body>
    10. <div id="loginDiv" style="height: 350px">
    11. <form action="/brand-demo/loginServlet" id="form">
    12. <h1 id="loginMsg">LOGIN IN</h1>
    13. <div id="errorMsg">${login_msg}</div>
    14. <%--${login_msg} 就是我们在LoginServlet资源下登录失败后转发到login页面把
    15. 登录页面展示给用户,并且把转发时储存到request域当中的数据(用户名或密码错误)拿
    16. 到展示在登录页面上 ${login_msg}:EL表达式 拿储存在域中数据的
    17. --%>
    18. <p>Username:<input id="username" name="username" value="${cookie.username.value}" type="text"></p>
    19. <p>Password:<input id="password" name="password" value="${cookie.password.value}" type="password"></p>
    20. <%-- value 的作用就是在复选框中,假设选中了该复选框那么该复选框的值也就是该value的值
    21. 这里remember是复选框 当我们勾选后 该默认值为“1”
    22. --%>
    23. <p>Remember:<input id="remember" name="remember" value="1" type="checkbox"></p>
    24. <div id="subDiv">
    25. <input type="submit" class="button" value="login up">
    26. <input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;
    27. <a href="register.html">没有账号?</a>
    28. </div>
    29. </form>
    30. </div>
    31. </body>
    32. </html>

     我们再此访问login.jsp页面的时候会帮我们自动填写用户名和密码了:

     三、用户注册

    register.jsp:

    1. <%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
    2. <!DOCTYPE html>
    3. <html lang="en">
    4. <head>
    5. <meta charset="UTF-8">
    6. <title>欢迎注册</title>
    7. <link href="css/register.css" rel="stylesheet">
    8. </head>
    9. <body>
    10. <div class="form-div">
    11. <div class="reg-content">
    12. <h1>欢迎注册</h1>
    13. <span>已有帐号?</span> <a href="login.jsp">登录</a>
    14. </div>
    15. <form id="reg-form" action="/brand-demo/registerServlet" method="get">
    16. <table>
    17. <tr>
    18. <td>用户名</td>
    19. <td class="inputs">
    20. <input name="username" type="text" id="username">
    21. <br>
    22. <span id="username_err" class="err_msg">${register_msg}</span>
    23. <%--
    24. ${register_msg} 通过EL表达式拿到RegisterServlet路径下封装转发过来的
    25. 数据 (用户名已存在,注册失败)
    26. --%>
    27. </td>
    28. </tr>
    29. <tr>
    30. <td>密码</td>
    31. <td class="inputs">
    32. <input name="password" type="password" id="password">
    33. <br>
    34. <span id="password_err" class="err_msg" style="display: none">密码格式有误</span>
    35. </td>
    36. </tr>
    37. </table>
    38. <div class="buttons">
    39. <input value="注 册" type="submit" id="reg_btn">
    40. </div>
    41. <br class="clear">
    42. </form>
    43. </div>
    44. </body>
    45. </html>

    RegisterServlet:

    1. package com.itheima.web;
    2. import com.itheima.pojo.User;
    3. import com.itheima.service.UserService;
    4. import javax.servlet.ServletException;
    5. import javax.servlet.annotation.WebServlet;
    6. import javax.servlet.http.HttpServlet;
    7. import javax.servlet.http.HttpServletRequest;
    8. import javax.servlet.http.HttpServletResponse;
    9. import java.io.IOException;
    10. @WebServlet("/registerServlet")
    11. public class RegisterServlet extends HttpServlet {
    12. @Override
    13. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    14. // 1.获取用户注册的用户名和密码
    15. String username =request.getParameter("username");
    16. String password =request.getParameter("password");
    17. // 2. 传递用户名
    18. UserService userService =new UserService();
    19. User user =userService.register_u(username);
    20. // 3. 判断查询的user对象是否为null 不为null说明查询到该用户了 说明已经注册过了
    21. if (user !=null){
    22. // 用户已经存在了,说明注册失败了
    23. // 注册失败后,还跳转到注册的页面
    24. request.setAttribute("register_msg","用户名已存在,注册失败");
    25. request.getRequestDispatcher("/register.jsp").forward(request,response);
    26. }else {
    27. // 用户不存在,把封装到User对象中的数据insert注册到数据库中
    28. User user1 =new User();
    29. user1.setUsername(username);
    30. user1.setPassword(password);
    31. userService.register_i(user1);
    32. // 注册成功后,跳转到登录页面
    33. request.setAttribute("register_msg","注册成功,请登录");//数据储存到request域中
    34. request.getRequestDispatcher("/login.jsp").forward(request,response);
    35. }
    36. }
    37. @Override
    38. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    39. this.doGet(request, response);
    40. }
    41. }

    UserServlet:

    1. package com.itheima.service;
    2. import com.itheima.mapper.UserMapper;
    3. import com.itheima.pojo.User;
    4. import com.itheima.util.SqlSessionFactoryUtils;
    5. import org.apache.ibatis.session.SqlSession;
    6. import org.apache.ibatis.session.SqlSessionFactory;
    7. /**
    8. * 业务逻辑层
    9. */
    10. public class UserService {
    11. SqlSessionFactory Factory =SqlSessionFactoryUtils.getSqlSessionFactory();
    12. /**
    13. * 登录方法
    14. */
    15. public User login(String username,String password){
    16. // 1. 获取SqlSession
    17. SqlSession sqlSession =Factory.openSession();
    18. UserMapper userMapper =sqlSession.getMapper(UserMapper.class);
    19. // 2. 调用UserMapper接口的查询用户和密码方法
    20. User user =userMapper.select(username,password);
    21. // 3. 释放资源
    22. sqlSession.close();
    23. return user;
    24. }
    25. /**
    26. * 注册方法
    27. * 1 先通过用户传递的用户名查询数据库判断是否有该用户
    28. */
    29. // 1. 获取SqlSession
    30. public User register_u(String username){
    31. SqlSession sqlSession =Factory.openSession();
    32. UserMapper userMapper =sqlSession.getMapper(UserMapper.class);
    33. // 2. 调用UserMapper接口的查询用户方法
    34. User user =userMapper.selectByUsername(username);
    35. // 3. 释放资源
    36. sqlSession.close();
    37. return user;
    38. }
    39. /**
    40. * 注册方法
    41. * 2 用户不存在的时候 把用户注册的用户名和密码insert添加到数据库当中
    42. * 注意:别忘记提交事务
    43. */
    44. public void register_i(User user){
    45. SqlSession sqlSession =Factory.openSession();
    46. UserMapper userMapper =sqlSession.getMapper(UserMapper.class);
    47. // 2. 调用UserMapper接口的查询用户方法
    48. userMapper.add(user);
    49. sqlSession.commit(); // 提交事务
    50. // 3. 释放资源
    51. sqlSession.close();
    52. }
    53. }

    login.jsp:

    1. <%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
    2. <!DOCTYPE html>
    3. <html lang="en">
    4. <head>
    5. <meta charset="UTF-8">
    6. <title>login</title>
    7. <link href="css/login.css" rel="stylesheet">
    8. </head>
    9. <body>
    10. <div id="loginDiv" style="height: 350px">
    11. <form action="/brand-demo/loginServlet" id="form">
    12. <h1 id="loginMsg">LOGIN IN</h1>
    13. <div id="errorMsg">${login_msg} ${register_msg}</div>
    14. <%--
    15. ${login_msg} 就是我们在LoginServlet资源下登录失败后转发到login页面把
    16. 登录页面展示给用户,并且把转发时储存到request域当中的数据(用户名或密码错误)拿
    17. 到展示在登录页面上 ${login_msg}:EL表达式 拿储存在域中数据的
    18. ${register_msg} 拿到的是RegisterServlet资源下封装到request域当中的数据通过转发过来
    19. (注册成功,请登录)展示在登录的页面上
    20. --%>
    21. <p>Username:<input id="username" name="username" value="${cookie.username.value}" type="text"></p>
    22. <p>Password:<input id="password" name="password" value="${cookie.password.value}" type="password"></p>
    23. <%-- value 的作用就是在复选框中,假设选中了该复选框那么该复选框的值也就是该value的值
    24. 这里remember是复选框 当我们勾选后 该默认值为“1”
    25. --%>
    26. <p>Remember:<input id="remember" name="remember" value="1" type="checkbox"></p>
    27. <div id="subDiv">
    28. <input type="submit" class="button" value="login up">
    29. <input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;
    30. <a href="register.html">没有账号?</a>
    31. </div>
    32. </form>
    33. </div>
    34. </body>
    35. </html>

     结果演示:

    用户已存在时:

     

     用户不存在时:

    四、验证码     展示&校验

    验证码的展示:

    代码如下:

    验证码工具类:

    1. package com.itheima.util;
    2. import javax.imageio.ImageIO;
    3. import java.awt.*;
    4. import java.awt.geom.AffineTransform;
    5. import java.awt.image.BufferedImage;
    6. import java.io.*;
    7. import java.util.Arrays;
    8. import java.util.Random;
    9. /**
    10. * 生成验证码工具类
    11. */
    12. public class CheckCodeUtil {
    13. public static final String VERIFY_CODES = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    14. private static Random random = new Random();
    15. public static void main(String[] args) throws IOException {
    16. OutputStream fos = new FileOutputStream("d://a.jpg");
    17. String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, fos, 4);
    18. System.out.println(checkCode);
    19. }
    20. /**
    21. * 输出随机验证码图片流,并返回验证码值(一般传入输出流,响应response页面端,Web项目用的较多)
    22. *
    23. * @param width 图片宽度
    24. * @param height 图片高度
    25. * @param os 输出流
    26. * @param verifySize 数据长度
    27. * @return 验证码数据
    28. * @throws IOException
    29. */
    30. public static String outputVerifyImage(int width, int height, OutputStream os, int verifySize) throws IOException {
    31. String verifyCode = generateVerifyCode(verifySize);
    32. outputImage(width, height, os, verifyCode);
    33. return verifyCode;
    34. }
    35. /**
    36. * 使用系统默认字符源生成验证码
    37. *
    38. * @param verifySize 验证码长度
    39. * @return
    40. */
    41. public static String generateVerifyCode(int verifySize) {
    42. return generateVerifyCode(verifySize, VERIFY_CODES);
    43. }
    44. /**
    45. * 使用指定源生成验证码
    46. *
    47. * @param verifySize 验证码长度
    48. * @param sources 验证码字符源
    49. * @return
    50. */
    51. public static String generateVerifyCode(int verifySize, String sources) {
    52. // 未设定展示源的字码,赋默认值大写字母+数字
    53. if (sources == null || sources.length() == 0) {
    54. sources = VERIFY_CODES;
    55. }
    56. int codesLen = sources.length();
    57. Random rand = new Random(System.currentTimeMillis());
    58. StringBuilder verifyCode = new StringBuilder(verifySize);
    59. for (int i = 0; i < verifySize; i++) {
    60. verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1)));
    61. }
    62. return verifyCode.toString();
    63. }
    64. /**
    65. * 生成随机验证码文件,并返回验证码值 (生成图片形式,用的较少)
    66. *
    67. * @param w
    68. * @param h
    69. * @param outputFile
    70. * @param verifySize
    71. * @return
    72. * @throws IOException
    73. */
    74. public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException {
    75. String verifyCode = generateVerifyCode(verifySize);
    76. outputImage(w, h, outputFile, verifyCode);
    77. return verifyCode;
    78. }
    79. /**
    80. * 生成指定验证码图像文件
    81. *
    82. * @param w
    83. * @param h
    84. * @param outputFile
    85. * @param code
    86. * @throws IOException
    87. */
    88. public static void outputImage(int w, int h, File outputFile, String code) throws IOException {
    89. if (outputFile == null) {
    90. return;
    91. }
    92. File dir = outputFile.getParentFile();
    93. //文件不存在
    94. if (!dir.exists()) {
    95. //创建
    96. dir.mkdirs();
    97. }
    98. try {
    99. outputFile.createNewFile();
    100. FileOutputStream fos = new FileOutputStream(outputFile);
    101. outputImage(w, h, fos, code);
    102. fos.close();
    103. } catch (IOException e) {
    104. throw e;
    105. }
    106. }
    107. /**
    108. * 输出指定验证码图片流
    109. *
    110. * @param w
    111. * @param h
    112. * @param os
    113. * @param code
    114. * @throws IOException
    115. */
    116. public static void outputImage(int w, int h, OutputStream os, String code) throws IOException {
    117. int verifySize = code.length();
    118. BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
    119. Random rand = new Random();
    120. Graphics2D g2 = image.createGraphics();
    121. g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    122. // 创建颜色集合,使用java.awt包下的类
    123. Color[] colors = new Color[5];
    124. Color[] colorSpaces = new Color[]{Color.WHITE, Color.CYAN,
    125. Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
    126. Color.PINK, Color.YELLOW};
    127. float[] fractions = new float[colors.length];
    128. for (int i = 0; i < colors.length; i++) {
    129. colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
    130. fractions[i] = rand.nextFloat();
    131. }
    132. Arrays.sort(fractions);
    133. // 设置边框色
    134. g2.setColor(Color.GRAY);
    135. g2.fillRect(0, 0, w, h);
    136. Color c = getRandColor(200, 250);
    137. // 设置背景色
    138. g2.setColor(c);
    139. g2.fillRect(0, 2, w, h - 4);
    140. // 绘制干扰线
    141. Random random = new Random();
    142. // 设置线条的颜色
    143. g2.setColor(getRandColor(160, 200));
    144. for (int i = 0; i < 20; i++) {
    145. int x = random.nextInt(w - 1);
    146. int y = random.nextInt(h - 1);
    147. int xl = random.nextInt(6) + 1;
    148. int yl = random.nextInt(12) + 1;
    149. g2.drawLine(x, y, x + xl + 40, y + yl + 20);
    150. }
    151. // 添加噪点
    152. // 噪声率
    153. float yawpRate = 0.05f;
    154. int area = (int) (yawpRate * w * h);
    155. for (int i = 0; i < area; i++) {
    156. int x = random.nextInt(w);
    157. int y = random.nextInt(h);
    158. // 获取随机颜色
    159. int rgb = getRandomIntColor();
    160. image.setRGB(x, y, rgb);
    161. }
    162. // 添加图片扭曲
    163. shear(g2, w, h, c);
    164. g2.setColor(getRandColor(100, 160));
    165. int fontSize = h - 4;
    166. Font font = new Font("Algerian", Font.ITALIC, fontSize);
    167. g2.setFont(font);
    168. char[] chars = code.toCharArray();
    169. for (int i = 0; i < verifySize; i++) {
    170. AffineTransform affine = new AffineTransform();
    171. affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize / 2, h / 2);
    172. g2.setTransform(affine);
    173. g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10);
    174. }
    175. g2.dispose();
    176. ImageIO.write(image, "jpg", os);
    177. }
    178. /**
    179. * 随机颜色
    180. *
    181. * @param fc
    182. * @param bc
    183. * @return
    184. */
    185. private static Color getRandColor(int fc, int bc) {
    186. if (fc > 255) {
    187. fc = 255;
    188. }
    189. if (bc > 255) {
    190. bc = 255;
    191. }
    192. int r = fc + random.nextInt(bc - fc);
    193. int g = fc + random.nextInt(bc - fc);
    194. int b = fc + random.nextInt(bc - fc);
    195. return new Color(r, g, b);
    196. }
    197. private static int getRandomIntColor() {
    198. int[] rgb = getRandomRgb();
    199. int color = 0;
    200. for (int c : rgb) {
    201. color = color << 8;
    202. color = color | c;
    203. }
    204. return color;
    205. }
    206. private static int[] getRandomRgb() {
    207. int[] rgb = new int[3];
    208. for (int i = 0; i < 3; i++) {
    209. rgb[i] = random.nextInt(255);
    210. }
    211. return rgb;
    212. }
    213. private static void shear(Graphics g, int w1, int h1, Color color) {
    214. shearX(g, w1, h1, color);
    215. shearY(g, w1, h1, color);
    216. }
    217. private static void shearX(Graphics g, int w1, int h1, Color color) {
    218. int period = random.nextInt(2);
    219. boolean borderGap = true;
    220. int frames = 1;
    221. int phase = random.nextInt(2);
    222. for (int i = 0; i < h1; i++) {
    223. double d = (double) (period >> 1)
    224. * Math.sin((double) i / (double) period
    225. + (6.2831853071795862D * (double) phase)
    226. / (double) frames);
    227. g.copyArea(0, i, w1, 1, (int) d, 0);
    228. if (borderGap) {
    229. g.setColor(color);
    230. g.drawLine((int) d, i, 0, i);
    231. g.drawLine((int) d + w1, i, w1, i);
    232. }
    233. }
    234. }
    235. private static void shearY(Graphics g, int w1, int h1, Color color) {
    236. int period = random.nextInt(40) + 10; // 50;
    237. boolean borderGap = true;
    238. int frames = 20;
    239. int phase = 7;
    240. for (int i = 0; i < w1; i++) {
    241. double d = (double) (period >> 1)
    242. * Math.sin((double) i / (double) period
    243. + (6.2831853071795862D * (double) phase)
    244. / (double) frames);
    245. g.copyArea(i, 0, 1, h1, 0, (int) d);
    246. if (borderGap) {
    247. g.setColor(color);
    248. g.drawLine(i, (int) d, i, 0);
    249. g.drawLine(i, (int) d + h1, i, h1);
    250. }
    251. }
    252. }
    253. }

    分析该工具类:

     

     register.jsp:

    1. <%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
    2. <!DOCTYPE html>
    3. <html lang="en">
    4. <head>
    5. <meta charset="UTF-8">
    6. <title>欢迎注册</title>
    7. <link href="css/register.css" rel="stylesheet">
    8. </head>
    9. <body>
    10. <div class="form-div">
    11. <div class="reg-content">
    12. <h1>欢迎注册</h1>
    13. <span>已有帐号?</span> <a href="login.jsp">登录</a>
    14. </div>
    15. <form id="reg-form" action="/brand-demo/registerServlet" method="get">
    16. <table>
    17. <tr>
    18. <td>用户名</td>
    19. <td class="inputs">
    20. <input name="username" type="text" id="username">
    21. <br>
    22. <span id="username_err" class="err_msg">${register_msg}</span>
    23. <%--
    24. ${register_msg} 通过EL表达式拿到RegisterServlet路径下封装的
    25. 数据 (用户名已存在,注册失败)
    26. --%>
    27. </td>
    28. </tr>
    29. <tr>
    30. <td>密码</td>
    31. <td class="inputs">
    32. <input name="password" type="password" id="password">
    33. <br>
    34. <span id="password_err" class="err_msg" style="display: none">密码格式有误</span>
    35. </td>
    36. </tr>
    37. <tr>
    38. <td>验证码</td>
    39. <td class="inputs">
    40. <input name="checkCode" type="text" id="checkCode">
    41. <img id="checkCodeImg" src="/brand-demo/checkCodeServlet">
    42. <a href="#" id="changeImg">看不清?</a>
    43. </td>
    44. </tr>
    45. </table>
    46. <div class="buttons">
    47. <input value="注 册" type="submit" id="reg_btn">
    48. </div>
    49. <br class="clear">
    50. </form>
    51. </div>
    52. <script>
    53. /* 给看不清?添加单击事件 也就是说每点击一次看不清? 就会重新跳转到/brand-demo/checkCodeServlet
    54. * 记得加时间戳 因为存在缓存情况
    55. * */
    56. document.getElementById("changeImg").onclick =function (){
    57. document.getElementById("checkCodeImg").src ="/brand-demo/checkCodeServlet?"+new Date().getMilliseconds();
    58. }
    59. </script>
    60. </body>
    61. </html>

    CheckCodeServlet:

    该资源下的作用:在页面上随机生成4位数的验证码 (位数可修改)

    1. package com.itheima.web;
    2. import com.itheima.util.CheckCodeUtil;
    3. import javax.servlet.ServletException;
    4. import javax.servlet.ServletOutputStream;
    5. import javax.servlet.annotation.WebServlet;
    6. import javax.servlet.http.HttpServlet;
    7. import javax.servlet.http.HttpServletRequest;
    8. import javax.servlet.http.HttpServletResponse;
    9. import java.io.IOException;
    10. @WebServlet("/checkCodeServlet")
    11. public class CheckCodeServlet extends HttpServlet {
    12. @Override
    13. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    14. /**
    15. * 生成验证码
    16. * 通过路径的访问 展示在注册页面上 (后面的数字4 代表验证码个数 可修改)
    17. */
    18. ServletOutputStream os =response.getOutputStream();
    19. String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, os, 4);
    20. }
    21. @Override
    22. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    23. this.doGet(request, response);
    24. }
    25. }

    开启服务器访问register.jsp:

     验证码的校验:

    CheckCodeServlt: 

    程序生成的验证码:

    将程序生成的验证码存储在Session域中 (服务器之间资源共享)

    1. package com.itheima.web;
    2. import com.itheima.util.CheckCodeUtil;
    3. import javax.servlet.ServletException;
    4. import javax.servlet.ServletOutputStream;
    5. import javax.servlet.annotation.WebServlet;
    6. import javax.servlet.http.HttpServlet;
    7. import javax.servlet.http.HttpServletRequest;
    8. import javax.servlet.http.HttpServletResponse;
    9. import javax.servlet.http.HttpSession;
    10. import java.io.IOException;
    11. @WebServlet("/checkCodeServlet")
    12. public class CheckCodeServlet extends HttpServlet {
    13. @Override
    14. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    15. // 程序随机生成验证码
    16. ServletOutputStream os =response.getOutputStream();
    17. String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, os, 4);
    18. // 把程序随机生成的验证码存入Session域中 (用于和用户输入的验证码比对)
    19. HttpSession session =request.getSession();
    20. session.setAttribute("checkCodeGen",checkCode);
    21. }
    22. @Override
    23. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    24. this.doGet(request, response);
    25. }
    26. }

    RegisterServlet:(认真看代码 有细节)

    1. package com.itheima.web;
    2. import com.itheima.pojo.User;
    3. import com.itheima.service.UserService;
    4. import javax.servlet.ServletException;
    5. import javax.servlet.annotation.WebServlet;
    6. import javax.servlet.http.HttpServlet;
    7. import javax.servlet.http.HttpServletRequest;
    8. import javax.servlet.http.HttpServletResponse;
    9. import javax.servlet.http.HttpSession;
    10. import java.io.IOException;
    11. @WebServlet("/registerServlet")
    12. public class RegisterServlet extends HttpServlet {
    13. @Override
    14. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    15. // 1.获取用户注册的用户名和密码
    16. String username =request.getParameter("username");
    17. String password =request.getParameter("password");
    18. // 获取用户输入的验证码
    19. String checkCode =request.getParameter("checkCode");
    20. // 获取程序随机生成的验证码 (根据key获取值)
    21. HttpSession session =request.getSession();
    22. // Object类型转换成String类型 (用户输入的为String类型 转成String做对比)
    23. String checkCodeGen = (String) session.getAttribute("checkCodeGen");
    24. // 注册之前作对比 如果验证码都不正确就没必要再判断往下走代码 判断查询的user对象是否为null了
    25. // 程序生成的验证码肯定不会为null 所以比较的时候放在最前面 作用:防止空指针异常
    26. // equalsIgnoreCase : 是不区分大小写比较的方法 (验证码一般不需要比较大小写)
    27. if (!checkCodeGen.equalsIgnoreCase(checkCode)){
    28. request.setAttribute("register_msg","验证码错误");
    29. request.getRequestDispatcher("/register.jsp").forward(request,response);
    30. return; // 直接结束代码即可
    31. }
    32. // 2. 传递用户名
    33. UserService userService =new UserService();
    34. User user =userService.register_u(username);
    35. // 3. 判断查询的user对象是否为null 不为null说明查询到该用户了 说明已经注册过了
    36. if (user !=null){
    37. // 用户已经存在了,说明注册失败了
    38. // 注册失败后,还跳转到注册的页面
    39. request.setAttribute("register_msg","用户名已存在,注册失败");
    40. request.getRequestDispatcher("/register.jsp").forward(request,response);
    41. }else {
    42. // 用户不存在,把封装到User对象中的数据insert注册到数据库中
    43. User user1 =new User();
    44. user1.setUsername(username);
    45. user1.setPassword(password);
    46. userService.register_i(user1);
    47. // 注册成功后,跳转到登录页面
    48. request.setAttribute("register_msg","注册成功,请登录");//数据储存到request域中
    49. request.getRequestDispatcher("/login.jsp").forward(request,response);
    50. }
    51. }
    52. @Override
    53. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    54. this.doGet(request, response);
    55. }
    56. }

    register.jsp:

    1. <%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
    2. <!DOCTYPE html>
    3. <html lang="en">
    4. <head>
    5. <meta charset="UTF-8">
    6. <title>欢迎注册</title>
    7. <link href="css/register.css" rel="stylesheet">
    8. </head>
    9. <body>
    10. <div class="form-div">
    11. <div class="reg-content">
    12. <h1>欢迎注册</h1>
    13. <span>已有帐号?</span> <a href="login.jsp">登录</a>
    14. </div>
    15. <form id="reg-form" action="/brand-demo/registerServlet" method="post">
    16. <table>
    17. <tr>
    18. <td>用户名</td>
    19. <td class="inputs">
    20. <input name="username" type="text" id="username">
    21. <br>
    22. <span id="username_err" class="err_msg" >${register_msg}</span>
    23. <%--
    24. ${register_msg} 通过EL表达式拿到RegisterServlet路径下封装转发过来的
    25. 数据 (用户名已存在,注册失败)
    26. --%>
    27. </td>
    28. </tr>
    29. <tr>
    30. <td>密码</td>
    31. <td class="inputs">
    32. <input name="password" type="password" id="password">
    33. <br>
    34. <span id="password_err" class="err_msg" style="display: none">密码格式有误</span>
    35. </td>
    36. </tr>
    37. <tr>
    38. <td>验证码</td>
    39. <td class="inputs">
    40. <input name="checkCode" type="text" id="checkCode">
    41. <img id="checkCodeImg" src="/brand-demo/checkCodeServlet">
    42. <a href="#" id="changeImg" >看不清?</a>
    43. </td>
    44. </tr>
    45. </table>
    46. <div class="buttons">
    47. <input value="注 册" type="submit" id="reg_btn">
    48. </div>
    49. <br class="clear">
    50. </form>
    51. </div>
    52. <script>
    53. /* 给看不清?添加单击事件 也就是说每点击一次看不清? 就会重新跳转到/brand-demo/checkCodeServlet
    54. * 记得加时间戳 因为存在缓存情况
    55. * */
    56. document.getElementById("changeImg").onclick = function () {
    57. document.getElementById("checkCodeImg").src = "/brand-demo/checkCodeServlet?"+new Date().getMilliseconds();
    58. }
    59. </script>
    60. </body>
    61. </html>

    开启服务器访问register.jsp: 

     

     

  • 相关阅读:
    【数据结构】优先级队列 - 堆
    LeetCode(力扣)455. 分发饼干Python
    ssm+vue+elementUI 医药进出口交易系统-#毕业设计
    【Java笔记+踩坑】设计模式——原型模式
    简记C语言清空输入残留内容
    反射
    docker ,k8s 以及 k8s 和 docker的关系
    通过nginx访问另一台服务器上的图片文件
    【Java面试宝典】线程安全问题|线程死锁的出现|线程安全的集合类
    流式计算中的 Window 计算
  • 原文地址:https://blog.csdn.net/lwj_07/article/details/125314743