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

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

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

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

 

User实体类:

package com.itheima.pojo;

public class User {

    private Integer id;
    private String username;
    private String password;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

UserServlet:

package com.itheima.service;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import com.itheima.util.SqlSessionFactoryUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

/**
 *  业务逻辑层
 */

public class UserService {

    SqlSessionFactory Factory =SqlSessionFactoryUtils.getSqlSessionFactory();
    /**
     *  登录方法
     */
    public User login(String username,String password){

        // 1. 获取SqlSession
        SqlSession sqlSession =Factory.openSession();
        UserMapper userMapper =sqlSession.getMapper(UserMapper.class);
        // 2. 调用UserMapper接口的查询用户和密码方法
        User user =userMapper.select(username,password);
        // 3. 释放资源
        sqlSession.close();
        return user;
    }
}

UserMapper:

package com.itheima.mapper;
import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface UserMapper {


    /**
     * 根据用户名和密码查询用户对象
     * @param username   @param 参数占位符mybatis完成增删改查里面有解释
     * @param password
     * @return
     */
    @Select("select * from tb_user where username = #{username} and password = #{password}")
    User select(@Param("username") String username, @Param("password") String password);

    /**
     * 根据用户名查询用户对象
     * @param username
     * @return
     */
    @Select("select * from tb_user where username = #{username}")
    User selectByUsername(String username);

    /**
     * 添加用户
     * @param user
     */
    @Insert("insert into tb_user values(null,#{username},#{password})")
    void add(User user);
}

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

登录成功:

 

登录失败: 

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

核心代码如下所示:

login.jsp:

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

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>login</title>
    <link href="css/login.css" rel="stylesheet">
</head>
<body>
<div id="loginDiv" style="height: 350px">
    <form action="/brand-demo/loginServlet" id="form">
        <h1 id="loginMsg">LOGIN IN</h1>
        <div id="errorMsg">${login_msg}</div>
        <%--${login_msg} 就是我们在LoginServlet资源下登录失败后转发到login页面把
        登录页面展示给用户,并且把转发时储存到request域当中的数据(用户名或密码错误)拿
        到展示在登录页面上  ${login_msg}:EL表达式 拿储存在域中数据的
        --%>
        <p>Username:<input id="username" name="username" type="text"></p>

        <p>Password:<input id="password" name="password" type="password"></p>
        <p>Remember:<input id="remember" name="remember" type="checkbox"></p>
        <div id="subDiv">
            <input type="submit" class="button" value="login up">
            <input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;
            <a href="register.html">没有账号?</a>
        </div>
    </form>
</div>


</body>

</html>

LoginServlet:

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

package com.itheima.web;
import com.itheima.pojo.User;
import com.itheima.service.UserService;
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 {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 1、接收客户端用户名和密码
            String username =request.getParameter("username");
            String password =request.getParameter("password");

            // 2、调用service层进行查询
            UserService userService =new UserService();
            User user =userService.login(username,password);

            // 3、判断查询是否有结果
            if (user != null){
                // 2. 把user查询出来的数据先封装到Session域当中 (数据保存在了服务器之间共享)
                HttpSession httpSession =request.getSession();
                // 存储到Session域中
                httpSession.setAttribute("user",user);

                // 1.登录成功 (要求:动态重定向到MVC三层架构讲的商品增删改查操作:SelectAllServlet资源下查询所有)
                String path =request.getContextPath();
                response.sendRedirect(path+"/selectAllServlet");

            } else {
                // 登录失败
                // 储存错误提示信息到request域当中 转发给login.jsp
                request.setAttribute("login_msg","用户名或密码错误");
                // 跳转到登录的login.jsp页面
                request.getRequestDispatcher("/login.jsp").forward(request,response);
            }

        }
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }

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

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

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

 

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

package com.itheima.web1;
import com.itheima.pojo.Brand;
import com.itheima.service.BrandService;
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 java.io.IOException;
import java.util.List;

@WebServlet("/selectAllServlet")
public class SelectAllServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1 调用BrandService完成查询
        BrandService brandService =new BrandService();
        List<Brand> brands =brandService.selectAll();

        // 2 把查询到的数据存入request域当中
        request.setAttribute("brands",brands);

        // 3 把存入的数据转发到brand.jsp页面中供用户查看数据
        request.getRequestDispatcher("/brand.jsp").forward(request,response);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

brand.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    <%-- 引入标签库 --%>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<%-- 拿到Session封装的数据  EL表达式通过$符可以拿到Session域中的数据和request域使用方法一样 --%>
<h1>${user.username},欢迎您</h1>

<input type="button" value="新增" id="add"><br>
<hr>
<table border="1" cellspacing="0" width="80%">
    <tr>
        <th>序号</th>
        <th>品牌名称</th>
        <th>企业名称</th>
        <th>排序</th>
        <th>品牌介绍</th>
        <th>状态</th>
        <th>操作</th>

    </tr>


    <c:forEach items="${brands}" var="brand" varStatus="status">
        <tr align="center">
            <td>${status.count}</td>
            <td>${brand.brandName}</td>
            <td>${brand.companyName}</td>
            <td>${brand.ordered}</td>
            <td>${brand.description}</td>
            <c:if test="${brand.status ==1}">
                <td> 启用 </td>
            </c:if>
            <c:if test="${brand.status ==0}">
                <td> 禁止 </td>
            </c:if>

                <%-- ?id 是把id传送给selectByIdServlet路径下的资源当中 --%>
            <td><a href="/brand-demo/selectByIdServlet?id=${brand.id}">修改</a>
                <a href="/brand-demo/deleteById?id=${brand.id}">删除</a></td>
        </tr>

    </c:forEach>

</table>


<script>
    <%-- 这里的 href是添加商品点击按钮后所进入的addBrand.jsp资源路径 --%>
    document.getElementById("add").onclick = function (){
        location.href = "/brand-demo/addBrand.jsp";
    }

</script>
</body>
</html>

二、记住用户

写Cookie数据: 

 login.jsp:

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>login</title>
    <link href="css/login.css" rel="stylesheet">
</head>
<body>
<div id="loginDiv" style="height: 350px">
    <form action="/brand-demo/loginServlet" id="form">
        <h1 id="loginMsg">LOGIN IN</h1>
        <div id="errorMsg">${login_msg}</div>
        <%--${login_msg} 就是我们在LoginServlet资源下登录失败后转发到login页面把
        登录页面展示给用户,并且把转发时储存到request域当中的数据(用户名或密码错误)拿
        到展示在登录页面上  ${login_msg}:EL表达式 拿储存在域中数据的
        --%>
        <p>Username:<input id="username" name="username" type="text"></p>

        <p>Password:<input id="password" name="password" type="password"></p>


        <%-- value 的作用就是在复选框中,假设选中了该复选框那么该复选框的值也就是该value的值
                这里remember是复选框 当我们勾选后 该默认值为“1”
        --%>
        <p>Remember:<input id="remember" name="remember" value="1" type="checkbox"></p>
        <div id="subDiv">
            <input type="submit" class="button" value="login up">
            <input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;
            <a href="register.html">没有账号?</a>
        </div>
    </form>
</div>


</body>

</html>

 LoginServlet:

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

package com.itheima.web;
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 1、接收客户端用户名和密码
            String username =request.getParameter("username");
            String password =request.getParameter("password");

            // 获取复选框数据
            String remember =request.getParameter("remember");

            // 2、调用service层进行查询
            UserService userService =new UserService();
            User user =userService.login(username,password);

            // 3、判断查询是否有结果
            if (user != null){
                // 判断user不为null说明登录成功了

                // 判断用户是否勾选了记住我 remember
                // 这里用:"1".equals(remember) 而不用remember.equals("1")
                // 是为了防止空指针异常 因为remember有可能用户没勾选 为null 然后比较的话会空指针
                if ("1".equals(remember)){
                    // 勾选了,发送Cookie

                    // 1 创建Cookie对象
                    Cookie c_username =new Cookie("username",username);
                    Cookie c_password =new Cookie("password",password);
                    // 设置Cookie数据在客户端存活的时间
                    c_username.setMaxAge(60*60*24*60);
                    c_password.setMaxAge(60*60*24*60);
                    // 2 发送Cookie
                    response.addCookie(c_username);
                    response.addCookie(c_password);

                }

                // 2. 把user查询出来的数据先封装到Session域当中 (数据保存在了服务器之间共享)
                HttpSession httpSession =request.getSession();
                // 存储到Session域中
                httpSession.setAttribute("user",user);

                // 1.登录成功 (要求:动态重定向到MVC三层架构讲的商品增删改查操作:SelectAllServlet资源下查询所有)
                String path =request.getContextPath();
                response.sendRedirect(path+"/selectAllServlet");

            } else {
                // 登录失败
                // 储存错误提示信息到request域当中 转发给login.jsp
                request.setAttribute("login_msg","用户名或密码错误");
                // 跳转到登录的login.jsp页面
                request.getRequestDispatcher("/login.jsp").forward(request,response);
            }

        }
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }

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

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

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

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

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>login</title>
    <link href="css/login.css" rel="stylesheet">
</head>
<body>
<div id="loginDiv" style="height: 350px">
    <form action="/brand-demo/loginServlet" id="form">
        <h1 id="loginMsg">LOGIN IN</h1>
        <div id="errorMsg">${login_msg}</div>
        <%--${login_msg} 就是我们在LoginServlet资源下登录失败后转发到login页面把
        登录页面展示给用户,并且把转发时储存到request域当中的数据(用户名或密码错误)拿
        到展示在登录页面上  ${login_msg}:EL表达式 拿储存在域中数据的
        --%>


        <p>Username:<input id="username" name="username" value="${cookie.username.value}" type="text"></p>

        <p>Password:<input id="password" name="password" value="${cookie.password.value}" type="password"></p>


        <%-- value 的作用就是在复选框中,假设选中了该复选框那么该复选框的值也就是该value的值
                这里remember是复选框 当我们勾选后 该默认值为“1”
        --%>
        <p>Remember:<input id="remember" name="remember" value="1" type="checkbox"></p>
        <div id="subDiv">
            <input type="submit" class="button" value="login up">
            <input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;
            <a href="register.html">没有账号?</a>
        </div>
    </form>
</div>


</body>

</html>

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

 三、用户注册

register.jsp:

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>欢迎注册</title>
    <link href="css/register.css" rel="stylesheet">
</head>
<body>

<div class="form-div">
    <div class="reg-content">
        <h1>欢迎注册</h1>
        <span>已有帐号?</span> <a href="login.jsp">登录</a>
    </div>
    <form id="reg-form" action="/brand-demo/registerServlet" method="get">

        <table>

            <tr>
                <td>用户名</td>
                <td class="inputs">
                    <input name="username" type="text" id="username">
                    <br>
                    <span id="username_err" class="err_msg">${register_msg}</span>
                    <%--
                    ${register_msg} 通过EL表达式拿到RegisterServlet路径下封装转发过来的
                     数据 (用户名已存在,注册失败)
                     --%>
                </td>

            </tr>

            <tr>
                <td>密码</td>
                <td class="inputs">
                    <input name="password" type="password" id="password">
                    <br>
                    <span id="password_err" class="err_msg" style="display: none">密码格式有误</span>
                </td>
            </tr>

        </table>

        <div class="buttons">
            <input value="注 册" type="submit" id="reg_btn">
        </div>
        <br class="clear">
    </form>

</div>
</body>
</html>

RegisterServlet:

package com.itheima.web;
import com.itheima.pojo.User;
import com.itheima.service.UserService;
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 java.io.IOException;

@WebServlet("/registerServlet")
public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1.获取用户注册的用户名和密码
        String username =request.getParameter("username");
        String password =request.getParameter("password");

        // 2. 传递用户名
        UserService userService =new UserService();
        User user =userService.register_u(username);


        // 3. 判断查询的user对象是否为null  不为null说明查询到该用户了 说明已经注册过了
        if (user !=null){
            // 用户已经存在了,说明注册失败了
            // 注册失败后,还跳转到注册的页面
            request.setAttribute("register_msg","用户名已存在,注册失败");
            request.getRequestDispatcher("/register.jsp").forward(request,response);
        }else {
            // 用户不存在,把封装到User对象中的数据insert注册到数据库中
            User user1 =new User();
            user1.setUsername(username);
            user1.setPassword(password);
            userService.register_i(user1);

            // 注册成功后,跳转到登录页面
            request.setAttribute("register_msg","注册成功,请登录");//数据储存到request域中
            request.getRequestDispatcher("/login.jsp").forward(request,response);
        }
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

UserServlet:

package com.itheima.service;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import com.itheima.util.SqlSessionFactoryUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

/**
 *  业务逻辑层
 */

public class UserService {

    SqlSessionFactory Factory =SqlSessionFactoryUtils.getSqlSessionFactory();
    /**
     *  登录方法
     */
    public User login(String username,String password){

        // 1. 获取SqlSession
        SqlSession sqlSession =Factory.openSession();
        UserMapper userMapper =sqlSession.getMapper(UserMapper.class);
        // 2. 调用UserMapper接口的查询用户和密码方法
        User user =userMapper.select(username,password);
        // 3. 释放资源
        sqlSession.close();
        return user;
    }

    /**
     *  注册方法
     *  1 先通过用户传递的用户名查询数据库判断是否有该用户
     */
    // 1. 获取SqlSession
    public User register_u(String username){
        SqlSession sqlSession =Factory.openSession();
        UserMapper userMapper =sqlSession.getMapper(UserMapper.class);
        // 2. 调用UserMapper接口的查询用户方法
        User user =userMapper.selectByUsername(username);
        // 3. 释放资源
        sqlSession.close();
        return user;
    }

    /**
     * 注册方法
     * 2 用户不存在的时候 把用户注册的用户名和密码insert添加到数据库当中
     *  注意:别忘记提交事务
     */
    public void register_i(User user){
        SqlSession sqlSession =Factory.openSession();
        UserMapper userMapper =sqlSession.getMapper(UserMapper.class);
        // 2. 调用UserMapper接口的查询用户方法
        userMapper.add(user);

        sqlSession.commit();    // 提交事务
        // 3. 释放资源
        sqlSession.close();

    }

}

login.jsp:

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>login</title>
    <link href="css/login.css" rel="stylesheet">
</head>
<body>
<div id="loginDiv" style="height: 350px">
    <form action="/brand-demo/loginServlet" id="form">
        <h1 id="loginMsg">LOGIN IN</h1>
        <div id="errorMsg">${login_msg} ${register_msg}</div>
        <%--
        ${login_msg} 就是我们在LoginServlet资源下登录失败后转发到login页面把
        登录页面展示给用户,并且把转发时储存到request域当中的数据(用户名或密码错误)拿
        到展示在登录页面上  ${login_msg}:EL表达式 拿储存在域中数据的

        ${register_msg} 拿到的是RegisterServlet资源下封装到request域当中的数据通过转发过来
        (注册成功,请登录)展示在登录的页面上
        --%>


        <p>Username:<input id="username" name="username" value="${cookie.username.value}" type="text"></p>

        <p>Password:<input id="password" name="password" value="${cookie.password.value}" type="password"></p>


        <%-- value 的作用就是在复选框中,假设选中了该复选框那么该复选框的值也就是该value的值
                这里remember是复选框 当我们勾选后 该默认值为“1”
        --%>
        <p>Remember:<input id="remember" name="remember" value="1" type="checkbox"></p>
        <div id="subDiv">
            <input type="submit" class="button" value="login up">
            <input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;
            <a href="register.html">没有账号?</a>
        </div>
    </form>
</div>


</body>

</html>

 结果演示:

用户已存在时:

 

 用户不存在时:

四、验证码     展示&校验

验证码的展示:

代码如下:

验证码工具类:

package com.itheima.util;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Arrays;
import java.util.Random;

/**
 * 生成验证码工具类
 */
public class CheckCodeUtil {

    public static final String VERIFY_CODES = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static Random random = new Random();


    public static void main(String[] args) throws IOException {
        OutputStream fos = new FileOutputStream("d://a.jpg");
        String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, fos, 4);

        System.out.println(checkCode);
    }


    /**
     * 输出随机验证码图片流,并返回验证码值(一般传入输出流,响应response页面端,Web项目用的较多)
     *
     * @param width 图片宽度
     * @param height 图片高度
     * @param os  输出流
     * @param verifySize 数据长度
     * @return 验证码数据
     * @throws IOException
     */
    public static String outputVerifyImage(int width, int height, OutputStream os, int verifySize) throws IOException {
        String verifyCode = generateVerifyCode(verifySize);
        outputImage(width, height, os, verifyCode);
        return verifyCode;
    }

    /**
     * 使用系统默认字符源生成验证码
     *
     * @param verifySize 验证码长度
     * @return
     */
    public static String generateVerifyCode(int verifySize) {
        return generateVerifyCode(verifySize, VERIFY_CODES);
    }

    /**
     * 使用指定源生成验证码
     *
     * @param verifySize 验证码长度
     * @param sources    验证码字符源
     * @return
     */
    public static String generateVerifyCode(int verifySize, String sources) {
        // 未设定展示源的字码,赋默认值大写字母+数字
        if (sources == null || sources.length() == 0) {
            sources = VERIFY_CODES;
        }
        int codesLen = sources.length();
        Random rand = new Random(System.currentTimeMillis());
        StringBuilder verifyCode = new StringBuilder(verifySize);
        for (int i = 0; i < verifySize; i++) {
            verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1)));
        }
        return verifyCode.toString();
    }

    /**
     * 生成随机验证码文件,并返回验证码值 (生成图片形式,用的较少)
     *
     * @param w
     * @param h
     * @param outputFile
     * @param verifySize
     * @return
     * @throws IOException
     */
    public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException {
        String verifyCode = generateVerifyCode(verifySize);
        outputImage(w, h, outputFile, verifyCode);
        return verifyCode;
    }



    /**
     * 生成指定验证码图像文件
     *
     * @param w
     * @param h
     * @param outputFile
     * @param code
     * @throws IOException
     */
    public static void outputImage(int w, int h, File outputFile, String code) throws IOException {
        if (outputFile == null) {
            return;
        }
        File dir = outputFile.getParentFile();
        //文件不存在
        if (!dir.exists()) {
            //创建
            dir.mkdirs();
        }
        try {
            outputFile.createNewFile();
            FileOutputStream fos = new FileOutputStream(outputFile);
            outputImage(w, h, fos, code);
            fos.close();
        } catch (IOException e) {
            throw e;
        }
    }

    /**
     * 输出指定验证码图片流
     *
     * @param w
     * @param h
     * @param os
     * @param code
     * @throws IOException
     */
    public static void outputImage(int w, int h, OutputStream os, String code) throws IOException {
        int verifySize = code.length();
        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Random rand = new Random();
        Graphics2D g2 = image.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // 创建颜色集合,使用java.awt包下的类
        Color[] colors = new Color[5];
        Color[] colorSpaces = new Color[]{Color.WHITE, Color.CYAN,
                Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
                Color.PINK, Color.YELLOW};
        float[] fractions = new float[colors.length];
        for (int i = 0; i < colors.length; i++) {
            colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
            fractions[i] = rand.nextFloat();
        }
        Arrays.sort(fractions);
        // 设置边框色
        g2.setColor(Color.GRAY);
        g2.fillRect(0, 0, w, h);

        Color c = getRandColor(200, 250);
        // 设置背景色
        g2.setColor(c);
        g2.fillRect(0, 2, w, h - 4);

        // 绘制干扰线
        Random random = new Random();
        // 设置线条的颜色
        g2.setColor(getRandColor(160, 200));
        for (int i = 0; i < 20; i++) {
            int x = random.nextInt(w - 1);
            int y = random.nextInt(h - 1);
            int xl = random.nextInt(6) + 1;
            int yl = random.nextInt(12) + 1;
            g2.drawLine(x, y, x + xl + 40, y + yl + 20);
        }

        // 添加噪点
        // 噪声率
        float yawpRate = 0.05f;
        int area = (int) (yawpRate * w * h);
        for (int i = 0; i < area; i++) {
            int x = random.nextInt(w);
            int y = random.nextInt(h);
            // 获取随机颜色
            int rgb = getRandomIntColor();
            image.setRGB(x, y, rgb);
        }
        // 添加图片扭曲
        shear(g2, w, h, c);

        g2.setColor(getRandColor(100, 160));
        int fontSize = h - 4;
        Font font = new Font("Algerian", Font.ITALIC, fontSize);
        g2.setFont(font);
        char[] chars = code.toCharArray();
        for (int i = 0; i < verifySize; i++) {
            AffineTransform affine = new AffineTransform();
            affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize / 2, h / 2);
            g2.setTransform(affine);
            g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10);
        }

        g2.dispose();
        ImageIO.write(image, "jpg", os);
    }

    /**
     * 随机颜色
     *
     * @param fc
     * @param bc
     * @return
     */
    private static Color getRandColor(int fc, int bc) {
        if (fc > 255) {
            fc = 255;
        }
        if (bc > 255) {
            bc = 255;
        }
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }

    private static int getRandomIntColor() {
        int[] rgb = getRandomRgb();
        int color = 0;
        for (int c : rgb) {
            color = color << 8;
            color = color | c;
        }
        return color;
    }

    private static int[] getRandomRgb() {
        int[] rgb = new int[3];
        for (int i = 0; i < 3; i++) {
            rgb[i] = random.nextInt(255);
        }
        return rgb;
    }

    private static void shear(Graphics g, int w1, int h1, Color color) {
        shearX(g, w1, h1, color);
        shearY(g, w1, h1, color);
    }

    private static void shearX(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(2);

        boolean borderGap = true;
        int frames = 1;
        int phase = random.nextInt(2);

        for (int i = 0; i < h1; i++) {
            double d = (double) (period >> 1)
                    * Math.sin((double) i / (double) period
                    + (6.2831853071795862D * (double) phase)
                    / (double) frames);
            g.copyArea(0, i, w1, 1, (int) d, 0);
            if (borderGap) {
                g.setColor(color);
                g.drawLine((int) d, i, 0, i);
                g.drawLine((int) d + w1, i, w1, i);
            }
        }

    }

    private static void shearY(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(40) + 10; // 50;

        boolean borderGap = true;
        int frames = 20;
        int phase = 7;
        for (int i = 0; i < w1; i++) {
            double d = (double) (period >> 1)
                    * Math.sin((double) i / (double) period
                    + (6.2831853071795862D * (double) phase)
                    / (double) frames);
            g.copyArea(i, 0, 1, h1, 0, (int) d);
            if (borderGap) {
                g.setColor(color);
                g.drawLine(i, (int) d, i, 0);
                g.drawLine(i, (int) d + h1, i, h1);
            }

        }

    }
}

分析该工具类:

 

 register.jsp:

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>欢迎注册</title>
    <link href="css/register.css" rel="stylesheet">
</head>
<body>

<div class="form-div">
    <div class="reg-content">
        <h1>欢迎注册</h1>
        <span>已有帐号?</span> <a href="login.jsp">登录</a>
    </div>
    <form id="reg-form" action="/brand-demo/registerServlet" method="get">

        <table>

            <tr>
                <td>用户名</td>
                <td class="inputs">
                    <input name="username" type="text" id="username">
                    <br>
                    <span id="username_err" class="err_msg">${register_msg}</span>
                    <%--
                    ${register_msg} 通过EL表达式拿到RegisterServlet路径下封装的
                     数据 (用户名已存在,注册失败)
                     --%>
                </td>

            </tr>

            <tr>
                <td>密码</td>
                <td class="inputs">
                    <input name="password" type="password" id="password">
                    <br>
                    <span id="password_err" class="err_msg" style="display: none">密码格式有误</span>
                </td>
            </tr>

            <tr>
                <td>验证码</td>
                <td class="inputs">
                    <input name="checkCode" type="text" id="checkCode">
                    <img id="checkCodeImg" src="/brand-demo/checkCodeServlet">
                    <a href="#" id="changeImg">看不清?</a>
                </td>
            </tr>


        </table>

        <div class="buttons">
            <input value="注 册" type="submit" id="reg_btn">
        </div>
        <br class="clear">
    </form>
</div>

<script>
    /* 给看不清?添加单击事件 也就是说每点击一次看不清? 就会重新跳转到/brand-demo/checkCodeServlet
    *   记得加时间戳  因为存在缓存情况
    * */
    document.getElementById("changeImg").onclick =function (){
        document.getElementById("checkCodeImg").src ="/brand-demo/checkCodeServlet?"+new Date().getMilliseconds();
    }
</script>
</body>
</html>

CheckCodeServlet:

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

package com.itheima.web;

import com.itheima.util.CheckCodeUtil;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

            /**
            *     生成验证码 
            *        通过路径的访问 展示在注册页面上 (后面的数字4 代表验证码个数 可修改)
            */
            ServletOutputStream os =response.getOutputStream();
            String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, os, 4);

        }


        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
}

开启服务器访问register.jsp:

 验证码的校验:

CheckCodeServlt: 

程序生成的验证码:

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

package com.itheima.web;
import com.itheima.util.CheckCodeUtil;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
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("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

            // 程序随机生成验证码
            ServletOutputStream os =response.getOutputStream();
            String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, os, 4);

            // 把程序随机生成的验证码存入Session域中 (用于和用户输入的验证码比对)
            HttpSession session =request.getSession();
            session.setAttribute("checkCodeGen",checkCode);

        }


        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
}

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

package com.itheima.web;
import com.itheima.pojo.User;
import com.itheima.service.UserService;
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("/registerServlet")
public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1.获取用户注册的用户名和密码
        String username =request.getParameter("username");
        String password =request.getParameter("password");

        // 获取用户输入的验证码
        String checkCode =request.getParameter("checkCode");

        // 获取程序随机生成的验证码    (根据key获取值)
        HttpSession session =request.getSession();
        // Object类型转换成String类型 (用户输入的为String类型 转成String做对比)
        String checkCodeGen = (String) session.getAttribute("checkCodeGen");

        // 注册之前作对比 如果验证码都不正确就没必要再判断往下走代码 判断查询的user对象是否为null了
        // 程序生成的验证码肯定不会为null 所以比较的时候放在最前面 作用:防止空指针异常
        // equalsIgnoreCase : 是不区分大小写比较的方法 (验证码一般不需要比较大小写)
        if (!checkCodeGen.equalsIgnoreCase(checkCode)){
            request.setAttribute("register_msg","验证码错误");
            request.getRequestDispatcher("/register.jsp").forward(request,response);
            return; // 直接结束代码即可
        }



        // 2. 传递用户名
        UserService userService =new UserService();
        User user =userService.register_u(username);


        // 3. 判断查询的user对象是否为null  不为null说明查询到该用户了 说明已经注册过了
        if (user !=null){
            // 用户已经存在了,说明注册失败了
            // 注册失败后,还跳转到注册的页面
            request.setAttribute("register_msg","用户名已存在,注册失败");
            request.getRequestDispatcher("/register.jsp").forward(request,response);
        }else {
            // 用户不存在,把封装到User对象中的数据insert注册到数据库中
            User user1 =new User();
            user1.setUsername(username);
            user1.setPassword(password);
            userService.register_i(user1);

            // 注册成功后,跳转到登录页面
            request.setAttribute("register_msg","注册成功,请登录");//数据储存到request域中
            request.getRequestDispatcher("/login.jsp").forward(request,response);
        }
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

register.jsp:

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>欢迎注册</title>
    <link href="css/register.css" rel="stylesheet">
</head>
<body>

<div class="form-div">
    <div class="reg-content">
        <h1>欢迎注册</h1>
        <span>已有帐号?</span> <a href="login.jsp">登录</a>
    </div>
    <form id="reg-form" action="/brand-demo/registerServlet" method="post">

        <table>

            <tr>
                <td>用户名</td>
                <td class="inputs">
                    <input name="username" type="text" id="username">
                    <br>
                    <span id="username_err" class="err_msg" >${register_msg}</span>

                    <%--
                    ${register_msg} 通过EL表达式拿到RegisterServlet路径下封装转发过来的
                     数据 (用户名已存在,注册失败)
                     --%>

                </td>

            </tr>

            <tr>
                <td>密码</td>
                <td class="inputs">
                    <input name="password" type="password" id="password">
                    <br>
                    <span id="password_err" class="err_msg" style="display: none">密码格式有误</span>
                </td>
            </tr>


            <tr>
                <td>验证码</td>
                <td class="inputs">
                    <input name="checkCode" type="text" id="checkCode">
                    <img id="checkCodeImg" src="/brand-demo/checkCodeServlet">
                    <a href="#" id="changeImg" >看不清?</a>
                </td>
            </tr>

        </table>

        <div class="buttons">
            <input value="注 册" type="submit" id="reg_btn">
        </div>
        <br class="clear">
    </form>

</div>

<script>

    /* 给看不清?添加单击事件 也就是说每点击一次看不清? 就会重新跳转到/brand-demo/checkCodeServlet
    *   记得加时间戳  因为存在缓存情况
    * */
    
    document.getElementById("changeImg").onclick = function () {
        document.getElementById("checkCodeImg").src = "/brand-demo/checkCodeServlet?"+new Date().getMilliseconds();
    }

</script>
</body>
</html>

开启服务器访问register.jsp: 

 

 

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值