需求如下: (相比以前的登陆注册添加的新功能如下框)
一、用户登录:(要求如下图)
用户登录总代码结构如下:
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>
二、记住用户
写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">
<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">
<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: