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


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


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">
- <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>

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">
- <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封装的数据了:

此时我们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">
- <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">
- <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:

