目录
- 引出
- 登陆功能---从html到jsp
- 1.登陆--用post请求
- 2.用html文件的form表单登陆
- (1)index.html页面
- (2)login.html登陆的页面
- (3)LoginServlet.java处理输入信息的代码
- (4)登陆成功:
- 3.如果失败在原页面显示信息----用jsp文件登陆
- (1)login.jsp页面
- (2)LoginServlet.java处理登陆业务---共享msg
- 注册功能---注册验证码
- 4.注册逻辑 和 注册成功后跳转到登陆页面
- 5.如何防止用户恶意注册----用验证码限制
- (1)前端显示验证码
- (2)后端判断验证是否正确
- (3)完整代码
- 登陆成功和退出登陆
- 6.登陆成功和退出登陆---保存session和删除session
- (1)前端页面,comIndex.jsp
- (2)进行登陆,保存session
- (3)退出系统servlet,LogoutServlet.jsp
- 总结
引出
1.验证用户名密码正确的思路;
2.从html文件到jsp文件的登陆进化—静态到动态;
3.用验证码的方式防止不断注册;
4.注册成功后再跳转到登陆页面—重定向;
4.如何保存登陆的用户信息----session;
登陆功能—从html到jsp
1.登陆–用post请求
两种登陆方法:
1.浏览器上,输入用户名&密码进行登陆,比如浏览器 -----B/S架构(Brower Server)
2.在软件上,输入用户名&密码登陆,比如QQ ----- C/S架构
思路:
1.通过username 和 password 查询对象出来,如果对象有,登陆成功,否则失败;
2.通过username 和 password 查询数量,如果数量为1,登陆成功,否则失败;
3.【推荐】SpringSecurity 推荐的方式:用username 查一个对象出来;
(1)查不到,登陆失败(没有注册过,数据库没有这个用户名);
(2)查到了,用户名正确,用查询出来的这一条记录和前端发送过来的密码对比:
如果正确,登陆成功,否则,登陆失败;
dao层的接口和实现:
接口要有:(1)新增一个数据到数据库;(2)根据用户名查询一条记录出来;
dao层的接口:
package com.tianju.dao;
import com.tianju.entity.ComUser;
/**
* 公司用户的Dao类
*/
public interface IComUserDao {
/**
* 新增一个公司用户
* @param comUser 新增用户
* @return 影响行数
*/
Integer add(ComUser comUser);
/**
* 根据用户名查一条记录出来
* @param username 查询的用户名
* @return 查询出的用户
*/
ComUser queryByUsername(String username);
}
dao接口的实现:
package com.tianju.dao.impl;
import com.tianju.dao.IComUserDao;
import com.tianju.entity.ComUser;
import com.tianju.util.DBUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.util.List;
public class ComUserDaoImpl implements IComUserDao {
private JdbcTemplate db = DBUtils.getJdbcTemplate();
public RowMapper<ComUser> rowMapper = new BeanPropertyRowMapper<>(ComUser.class);
@Override
public Integer add(ComUser comUser) {
String sql = "INSERT INTO com_user(id,username,password,sex,birthday)" +
"values (null,?,?,?,?)";
return db.update(sql,comUser.getUsername(),comUser.getPassword(),comUser.getSex(),comUser.getBirthday());
}
@Override
public ComUser queryByUsername(String username) {
String sql = "SELECT * FROM com_user WHERE username=?";
List<ComUser> find = db.query(sql, rowMapper,username);
if (find.size()>0){
return find.get(0);
}
return null;
}
}
2.用html文件的form表单登陆
(1)index.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<img src="img/register.jpg" width="200px"><br>
<a href="/day01/html/login.html" target="_blank">登陆</a><br>
<a href="html/register.html" target="_blank">注册</a><br>
<a href="html/idcard.html" target="_blank">身份证归属地</a><br>
<a href="/day01/html/loginNew.html">新版登陆页面</a><br>
<a href="/day01/bookTypes/list">显示图书类型的表格--前端在java中拼出来</a><br>
<a href="/day01/bookList/jsp">显示图书类型的表格--用jsp</a>
</body>
</html>
(2)login.html登陆的页面
其中密码输入的input框的type设置成 password
密码 :<input type="password" name="password"><br>
action 要用绝对路径 :/项目名/链接
1)有 / 为绝对路径,从ip/端口/day01/开始拼接 ,会从localhost:8080位置开始拼接路径
2)无 / 为相对路径,浏览器会删除最后一级目录,然后把相对目录地址拼接上去
如果是绝对路径,会从localhost:8080位置开始拼接路径
浏览器路径为:localhost:8080/day01/a
<a href="/b">跳转</a>
跳转后路径为:localhost:8080/b
如果是相对路径,浏览器会删除最后一级目录,然后把相对目录地址拼接上去
浏览器路径为:localhost:8080/day01/a
<a href="b">跳转</a>
跳转后路径为:localhost:8080/day01/b
get和post
查询用get:链接里显示输入的值;写到请求头中
登陆用post:链接里不会显示输入的值;写到请求体中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆的页面</title>
</head>
<body>
<form action="/day01/loginUserNew" method="post">
<h1>新版登陆页面</h1>
<img src="../img/register.jpg" width="200px"><br>
用户名:<input type="text" name="username"><br>
密 码:<input type="password" name="password"><br>
<input type="submit" value="登陆">
<input type="reset" value="重置">
</form>
<a href="../index.html">返回主界面</a>
</body>
</html>
(3)LoginServlet.java处理输入信息的代码
业务流程:
1)输入不为空;
2)验证用户名和密码;
2)登陆成功;
/**
* 用户登陆的服务,注意不能用log
* 1.username + password 查询对象出来,对象有,登陆成功,否则失败;
* 2.查询数量出来,1成功,否则失败;
* 3.【建议】springSecurity 推荐,用username 查一个对象;
* 3.1 查不到,登陆失败(没有注册过,数据库无这个用户名);
* 3.2 查到了,用户名正确,就用这一条记录的密码和前端发送的密码对比;
* 如果正确,登陆成功,不登录失败;
*/
@WebServlet("/loginUserNew")
public class LoginServlet extends HttpServlet {
private IUserService userService = new UserServiceImpl();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决编码的问题
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
// 获取前端传来的用户名
String username = req.getParameter("username");
String password = req.getParameter("password");
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)){
resp.getWriter().write("<h1>必填项为空</h1>");
return;
}
// 登陆错误,重新输入
User user = userService.findByUsername(username);
if (user==null || !password.equals(user.getPassword())){
resp.getWriter().write("<h1>用户名|密码错误</h1>");
return;
}
String out = "<h1>用户名"+user.getUsername() +"/"+ user.getNickname() +"登陆成功"+"</h1>";
resp.getWriter().write(out);
}
}
(4)登陆成功:
3.如果失败在原页面显示信息----用jsp文件登陆
(1)login.jsp页面
用一个span框,获取共享的msg信息
<span style="color: darkred">${msg}</span><br>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户登陆页面e</title>
</head>
<body>
<form action="/day06/comUser/login" method="post">
用户名:<input type="text" name="username"><br>
密 码:<input type="password" name="password"><br>
<span style="color: darkred">${msg}</span><br>
<input type="submit" value="登陆">
</form>
</body>
</html>
(2)LoginServlet.java处理登陆业务—共享msg
请求转发,内部转发,jsp本质也是servlet
if (comUser==null || !comUser.getPassword().equals(SecureUtil.md5(password))){
req.setAttribute("msg", "用户名 | 密码错误");
req.getRequestDispatcher("/comUser/login.jsp").forward(req, resp);
return;
}
完整代码:
/**
* 用户登陆的servlet
*/
@WebServlet("/comUser/login")
public class LoginServlet extends HttpServlet {
private IComUserService comUserService = new ComUserServiceImpl();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 编码问题
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
// 输入不为空
String username = req.getParameter("username");
String password = req.getParameter("password");
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)){
// 输入为空
req.setAttribute("msg", "输入为空,请检查");
req.getRequestDispatcher("/comUser/login.jsp").forward(req, resp);
return;
}
// 进行验证
ComUser comUser = comUserService.queryByUsername(username);
if (comUser==null || !comUser.getPassword().equals(SecureUtil.md5(password))){
req.setAttribute("msg", "用户名 | 密码错误");
req.getRequestDispatcher("/comUser/login.jsp").forward(req, resp);
return;
}
// 跳转到操作页面
resp.sendRedirect(req.getContextPath()+"/compMess/comIndex.jsp");
}
}
注册功能—注册验证码
4.注册逻辑 和 注册成功后跳转到登陆页面
用验证码的方式防止恶意注册;
注册成功重定向到登陆页面:
// 5.注册成功跳转到登陆页面
resp.sendRedirect(req.getContextPath()+"/comUser/login.jsp");
5.如何防止用户恶意注册----用验证码限制
(1)前端显示验证码
验证码的图片请求servlet
验证码:<input type="text" name="imgCode"><img src="/day06/register/image/get">
<%-- 如果要让验证码在点击时自动更新--%>
验证码:<input type="text" name="imgCode">
<img src="/day06/register/image/get" οnclick="this.src='/day06/register/image/get?'+new Date().getMilliseconds()">
servlet处理请求:
(1)把验证码—4位数字存到session中;
(2)把验证码的图片放到响应中,发送给前端jsp,进行展示;
@WebServlet("/register/image/get")
public class ImageServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 随机生成验证码
String randomStr = ImageCodeUtils.getRandomStr(4);
// 验证码存到session空间中
HttpSession session = req.getSession();
session.setAttribute("yzm", randomStr);
// 根据验证码在缓存中生成一张图片
BufferedImage bufferedImage = ImageCodeUtils.createImageCode(100, 36, randomStr);
// 把缓存中的图片以png的格式,放到响应的输出流中
ImageIO.write(bufferedImage, "png", resp.getOutputStream());
}
}
注意这里有个错误:共享的时候不能是session,查了好久这个bug
一个卡了一会儿的bug
生成的图片分析:
(2)后端判断验证是否正确
在图片的servlet中,把图片的验证码4位数字存到session空间中;
// 验证码存到session空间中
HttpSession session = req.getSession();
session.setAttribute("yzm", randomStr);
在注册的register的servlet中,获取session,从session中获取验证码数字,进行判断
- 忽略大小写
// +++++判断验证码是否正确
HttpSession session = req.getSession();
// 从session中获取验证码
String yzm = (String)session.getAttribute("yzm");
// 忽略大小写 .equalsIgnoreCase
if (!yzm.equalsIgnoreCase(imgCode)){
req.setAttribute("msg", "验证码不正确");
req.getRequestDispatcher("/comUser/register.jsp").forward(req, resp);
return;
}
验证码对比流程:
(3)完整代码
处理注册业务的RegisterServlet.java文件
@WebServlet("/comUser/register")
public class RegisterServlet extends HttpServlet {
private IComUserService comUserService = new ComUserServiceImpl();
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决编码问题
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
// 用户注册的业务逻辑
String username = req.getParameter("username");
String password = req.getParameter("password");
String rePassword = req.getParameter("rePassword");
String sex = req.getParameter("gender");
String birthday = req.getParameter("birthday");
System.out.println(username+password+rePassword+sex+birthday);
// +++加入+验证码输入
String imgCode = req.getParameter("imgCode");
// 1.输入不为空;
if (StringUtils.isBlank(username)
|| StringUtils.isBlank(password)
|| StringUtils.isBlank(rePassword)
|| StringUtils.isBlank(sex)
|| StringUtils.isBlank(birthday)
|| StringUtils.isBlank(imgCode)){
// 如果为空,显示一条信息;转发给jsp
req.setAttribute("msg", "输入为空,请检查");
req.getRequestDispatcher("/comUser/register.jsp").forward(req, resp);
return;
}
// +++++判断验证码是否正确
HttpSession session = req.getSession();
// 从session中获取验证码
String yzm = (String)session.getAttribute("yzm");
// 忽略大小写 .equalsIgnoreCase
if (!yzm.equalsIgnoreCase(imgCode)){
req.setAttribute("msg", "验证码不正确");
req.getRequestDispatcher("/comUser/register.jsp").forward(req, resp);
return;
}
// 2.用户名不重复;
ComUser comUserDb = comUserService.queryByUsername(username);
if (comUserDb!=null){
// 用户名重复
req.setAttribute("msg", "用户名重复,请重新输入");
req.getRequestDispatcher("/comUser/register.jsp").forward(req, resp);
return;
}
// 3.两次密码输入一致;
if (!password.equals(rePassword)){
// 两次密码不一致
req.setAttribute("msg", "两次密码输入不一致,请检查");
req.getRequestDispatcher("/comUser/register.jsp").forward(req, resp);
return;
}
// 进行密码加密存储
ComUser comUser = new ComUser();
comUser.setPassword(SecureUtil.md5(password));
comUser.setUsername(username);
try {
comUser.setBirthday(sdf.parse(birthday));
} catch (ParseException e) {
throw new RuntimeException(e);
}
comUser.setSex(sex);
System.out.println(comUser);
// 4.保存信息到数据库
comUserService.add(comUser);
// 5.注册成功跳转到登陆页面
resp.sendRedirect(req.getContextPath()+"/comUser/login.jsp");
}
}
生成验证码的ImageServlet.java文件:
/**
* 验证码的servlet
* 随机生成验证码,验证码存到session空间中;
* 把验证码图片以png的格式,放到响应的输出流中;
*/
@WebServlet("/register/image/get")
public class ImageServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 随机生成验证码
String randomStr = ImageCodeUtils.getRandomStr(4);
// 验证码存到session空间中
HttpSession session = req.getSession();
session.setAttribute("yzm", randomStr);
// 根据验证码在缓存中生成一张图片
BufferedImage bufferedImage = ImageCodeUtils.createImageCode(100, 36, randomStr);
// 把缓存中的图片以png的格式,放到响应的输出流中
ImageIO.write(bufferedImage, "png", resp.getOutputStream());
}
}
ImageCodeUtils.java --生成验证码图片的工具类
package com.tianju.util;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
/**
* 生成图片验证码
*/
public class ImageCodeUtils {
//根据传入的位数,生成随机字符
public static String getRandomStr(int length)
{
String s = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
// Random random = new Random(System.currentTimeMillis());
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int i1 = random.nextInt(s.length());
sb.append(s.charAt(i1));
}
return sb.toString();
}
public static void main(String[] args) throws IOException, InterruptedException {
for (int i = 0; i < 100; i++) {
Thread.sleep(10);
String randomStr = getRandomStr(4);
System.out.println(randomStr);
}
// //BuffereImage 图片缓冲流
// BufferedImage bufferedImage = createImageCode(100, 34, "ABCD");//生成图片的缓冲流
// //把缓冲流,写到某个输出流
// ImageIO.write(bufferedImage,"jpg",new FileOutputStream("/Users/edz/Desktop/11.jpg"));
}
public static BufferedImage createImageCode(int width, int height, String code) {
int codeSize = code.length();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Random rand = new Random();
Graphics2D g2 = image.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
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, width, height);
Color c = getRandColor(200, 250);
g2.setColor(c);// 设置背景色
g2.fillRect(0, 2, width, height - 4);
// 绘制干扰线
Random random = new Random();
g2.setColor(getRandColor(160, 200));// 设置线条的颜色
for (int i = 0; i < 20; i++) {
int x = random.nextInt(width - 1);
int y = random.nextInt(height - 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 * width * height);
for (int i = 0; i < area; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int rgb = getRandomIntColor();
image.setRGB(x, y, rgb);
}
shear(g2, width, height, c);// 使图片扭曲
g2.setColor(getRandColor(100, 160));
int fontSize = height - 4;
Font font = new Font("Algerian", Font.ITALIC, fontSize);
g2.setFont(font);
char[] chars = code.toCharArray();
for (int i = 0; i < codeSize; i++) {
AffineTransform affine = new AffineTransform();
affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1),
(width / codeSize) * i + fontSize / 2, height / 2);
g2.setTransform(affine);
g2.drawChars(chars, i, 1, ((width - 10) / codeSize) * i + 5, height / 2 + fontSize / 2 - 10);
}
g2.dispose();
return image;
}
private static Color getRandColor(int fc, int bc) {
Random random = ThreadLocalRandom.current();
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() {
Random random = ThreadLocalRandom.current();
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) {
Random random = ThreadLocalRandom.current();
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 = (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * phase) / 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) {
Random random = ThreadLocalRandom.current();
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 = (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * phase) / 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);
}
}
}
}
登陆成功和退出登陆
6.登陆成功和退出登陆—保存session和删除session
登陆成功后,将登陆成功的用户存到session----用于确定当前的操作是谁做的
// +登陆成功的用户存到session----用于确定当前的操作是谁做的
req.getSession().setAttribute("user", userDb);
退出登陆后,把保存的session信息删除,并重新回到登陆页
HttpSession session = req.getSession();
session.invalidate(); // 清理session
resp.sendRedirect(req.getContextPath()+"/user/login.jsp");
完整的业务逻辑代码:
(1)前端页面,comIndex.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登陆成功的index页面</title>
</head>
<body>
<a href="/day06/company/messList" target="iframe">查询公司所有信息</a>
<a href="/day06/user/logout">退出系统</a>
<iframe width="100%" height="80%" name="iframe"></iframe>
</body>
</html>
(2)进行登陆,保存session
/**
* 用户登陆的servlet
*/
@WebServlet("/comUser/login")
public class LoginServlet extends HttpServlet {
private IComUserService comUserService = new ComUserServiceImpl();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 编码问题
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
// 输入不为空
String username = req.getParameter("username");
String password = req.getParameter("password");
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)){
// 输入为空
req.setAttribute("msg", "输入为空,请检查");
req.getRequestDispatcher("/comUser/login.jsp").forward(req, resp);
return;
}
// 进行验证
ComUser comUser = comUserService.queryByUsername(username);
if (comUser==null || !comUser.getPassword().equals(SecureUtil.md5(password))){
req.setAttribute("msg", "用户名 | 密码错误");
req.getRequestDispatcher("/comUser/login.jsp").forward(req, resp);
return;
}
// ++++++登陆成功的用户对象存储到session中=====用户确定后续的操作是谁做的
req.getSession().setAttribute("user", comUser);
// 跳转到操作页面---重定向
resp.sendRedirect(req.getContextPath()+"/compMess/comIndex.jsp");
}
}
(3)退出系统servlet,LogoutServlet.jsp
/**
* 退出登陆的servlet,清除session缓存
*/
@WebServlet("/user/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
session.invalidate(); // 清除session
// 重定向,回到登陆页面
resp.sendRedirect(req.getContextPath()+"/index.jsp");
}
}
总结
1.验证用户名密码正确:通过username查一条信息出来,对比密码;
2.用jsp文件代替html文件实现共享msg的提醒,比如用户名|密码不正确;
3.用验证码的方式防止不断注册,点击图片自动更细;
4.注册成功后再跳转到登陆页面—重定向;
4.用session空间保存登陆成功的用户对象;