JavaWeb World (12): Implementation of verification codes and prevention of repeated form submission

Introduction of verification code

Everyone is familiar with verification codes. Under normal circumstances, systems on the Internet use verification codes, but general enterprise intranets do not require verification codes. In order to maliciously crack passwords and maliciously tamper with information, etc.
First write a Servlet to respond to the verification code JSP:

RandomCodeServlet.java:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.Buffer;
import java.util.Random;
import java.util.UUID;

import javax.imageio.ImageIO;
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.swing.plaf.metal.MetalIconFactory.FolderIcon16;
import javax.xml.ws.RespectBinding;

@WebServlet("/randomCode")
public class RandomCodeServlet extends HttpServlet {

	private static final long serialVersionUID = -2717466490897149900L;

	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		//1.生成随机数
		String randomcode = UUID.randomUUID().toString().substring(0, 5);
		//2.将随机数放进session
		req.getSession().setAttribute("RANDOM_IN_SESSION", randomcode);
		show(randomcode, resp);
	}
	
	private void show(String randomcode, HttpServletResponse resp) throws IOException {
		//创建图片对象
		int width = 80;
		int height = 30;
		int imageType = BufferedImage.TYPE_INT_RGB;
		BufferedImage image = new BufferedImage(width, height, imageType);
		
		//画板
		Graphics graphics = image.getGraphics();
		graphics.setColor(Color.YELLOW);
		
		//绘制一个实心矩形
		graphics.fillRect(1, 1, width - 2, height - 2);
		//将随机数画进图片中
		graphics.setColor(Color.BLACK);
		Font font = new Font("宋体", Font.BOLD + Font.ITALIC, 20);
		graphics.setFont(font);
		graphics.drawString(randomcode, 10, 20);
		
		//干扰线
		graphics.setColor(Color.GRAY);
		Random random = new Random();
		for (int i = 0; i < 100; i++) {
			graphics.fillRect(random.nextInt(width), random.nextInt(height), 2, 2);	
		}
		graphics.dispose();
		ImageIO.write(image, "jpg", resp.getOutputStream());
	}
}

login.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Login</title>
<script type="text/javascript">
	function change() {
		//重新设置Img元素的scr属性
		document.getElementById("randomCodeImg").src = "/randomCode?"
				+ new Date().getTime();
	}
</script>

</head>
<body>
	<h3>用户登录</h3>
	<span style="color: red">${error}</span>
	<form action="" method="POST">&emsp;:<input type="text" name="username" required></br>&emsp;:<input type="password" name="password" required></br> 
		验证码:<input type="text" name="randomCode" size="5" maxlength="5"> 
			 <img src="/randomCode" id="randomCodeImg" style="cursor: pointer;"
				title="看不清,换一张" onclick="change()"></br> 
		<input type="submit" value="登录">
	</form>
</body>
</html>

The click event needs to be set for the image, which is realized by embedded JavaScript code, and the content behind each resource is set differently by setting the parameters behind the resource, otherwise the previous cache will be loaded every time.

The final effect is as follows:
Insert picture description here

Verification code implementation

There are several steps:
first , determine whether the verification code is entered correctly: compare the entered verification code with the verification code stored in the Session, and secondly , if the two are not equal, the verification code is wrong or the session expires , and if they are equal, log in Check and destroy the verification code in the session (one verification code can only be used once)

RandomCodeLoginServlet.java:

package com.cherry._03_randomcode;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/randomLogin")
public class RandomCodeLoginServlet extends HttpServlet {

	private static final long serialVersionUID = 9020939103399052034L;

	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		//1.接收请求参数
		String username = req.getParameter("username");
		String password = req.getParameter("password");
		String randomCode = req.getParameter("randomCode");
		
		//获取session中的验证码
		String randomCodeInSession = (String) req.getSession()
				.getAttribute("RANDOMCODE_IN_SESSION");
		
		if (!randomCode.equalsIgnoreCase(randomCodeInSession)) {
			req.setAttribute("error", "验证码不正确或会话已过期");
			req.getRequestDispatcher("/randomcode/login.jsp").forward(req, resp);	
			return;
		}
		
		req.getSession().removeAttribute("RANDOMCODE_IN_SESSION");
		
		//登录判断
		
		//2.调用业务方法
		
		//3.跳转界面
	}
}

The effect is as follows:
Sample

Prevent duplicate form submission

The root cause:
There is no complete request page-> submit page process
phenomenon:

  1. Click the button repeatedly due to the slowness or delay of the server;
  2. Submit again by returning to the original form submission page;
  3. Has been submitted successfully, refresh the successful page.

Solution:
use token mechanism ( token )

We generate a random number in jsp :

<%
	//生成一个随机数(口令)
	String token = UUID.randomUUID().toString();
	//存放于session中,将来用于做判断
	session.setAttribute("TOKEN_IN_SESSION", token);
%>

Hide the display in the form:

<input type="hidden" name="token" value="<%=token%>"/>

Insert picture description here
The principle is: when requesting the form page, generate a random number (password), and then obtain the password in the form in the Servlet, obtain the password in the Session, determine whether they are equal, if they are equal, then destroy, and then perform the operation; if Unequal, wrong password, indicating repeated submission.
Schematic diagram

Complete code:

RepeatLogin.java:

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/repeatLogin")
public class RepeatServlet extends HttpServlet {

	private static final long serialVersionUID = -2618731683900219578L;

	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		
		//表单中的口令
		String tokenInRequest = req.getParameter("token");
		//session中的口令
		String tokenInSession = (String) req.getSession().getAttribute("TOKEN_IN_SESSION");
		if (tokenInRequest.equals(tokenInSession)) {
			//销毁session中的口令
			req.getSession().removeAttribute("TOKEN_IN_SESSION");
			resp.setContentType("text/html;charset=utf-8");
			PrintWriter out = resp.getWriter();
			String num = req.getParameter("num");
			System.out.println("转账:" + num);
			out.print("转账成功!");	
		} else {
			System.out.println("不要重复提交表单!!!");
		}
	}
}

LoginServlet.jsp:

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/repeatLogin")
public class RepeatServlet extends HttpServlet {

	private static final long serialVersionUID = -2618731683900219578L;

	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		
		//表单中的口令
		String tokenInRequest = req.getParameter("token");
		//session中的口令
		String tokenInSession = (String) req.getSession().getAttribute("TOKEN_IN_SESSION");
		if (tokenInRequest.equals(tokenInSession)) {
			//销毁session中的口令
			req.getSession().removeAttribute("TOKEN_IN_SESSION");
			resp.setContentType("text/html;charset=utf-8");
			PrintWriter out = resp.getWriter();
			String num = req.getParameter("num");
			System.out.println("转账:" + num);
			out.print("转账成功!");	
		} else {
			System.out.println("不要重复提交表单!!!");
		}
	}
}

login.jsp:

<%@page import="java.util.UUID"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Login</title>
</head>
<body>
	<span style="color: red">${error}</span>
	<form action="/repeatLogin" method="POST">
		<input type="hidden" name="token" value="${token}"/>
		转账金额: <input type="text" name="num" required></br>
		<input type="submit" value="提交"/>
	</form>
</body>
</html>

The effect is as follows:
Insert picture description here

Published 56 original articles · Like 23 · Visits 20,000+

Guess you like

Origin blog.csdn.net/qq_42650988/article/details/104115580