學會這個工具的使用,讓你快速生成驗證碼

要成為王的男人發表於2019-03-05

前言

驗證碼是我們做人機驗證最常用的方式,常用於敏感操作的驗證,比如:登入、註冊、修改等。

驗證碼的原理:不同的客戶端擁有不同的 session 物件,在看到驗證碼圖片的時刻,伺服器後端程式碼生成圖片並將隨機字元儲存到 session 中。這樣客戶端看到的只能是圖片,人工識別圖片後將字元傳送到伺服器與 session 中的字元進行比對。

上面只是簡單的介紹了驗證碼的原理,更多細節還需有 javaweb 相關基礎知識,這篇文章適合有基礎的同學。

最近幾天我翻到了以前生成驗證碼的工具類,使用 Graphics2D 生成的圖片,然後再以流的形式寫出到客戶端,這些程式碼還是有些問題的,都是硬編碼。在以後的使用中我們可能有不同的需求都會導致程式碼重新修改,自定義一些樣式都不是很方便。

所以我找到了 github 上的一個生成驗證碼的工具:kaptcha,下面我就給大家介紹一下 kaptcha 的使用。

kaptcha 的使用

我們就以一個 maven 構建的 web 專案為例

1、依賴 jar 包

在 pom.xml 檔案中新增相關依賴

<dependency>
  <groupId>com.github.penggle</groupId>
  <artifactId>kaptcha</artifactId>
  <version>2.3.2</version>
</dependency>
複製程式碼

2、配置生成驗證碼的 servlet

修改 web.xml,新增 kaptcha 提供的 servlet 並配置對映路徑

<servlet>
    <servlet-name>Kaptcha</servlet-name>
    <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>Kaptcha</servlet-name>
    <url-pattern>/captcha</url-pattern>
</servlet-mapping>
複製程式碼

訪問 http://localhost:8080/captcha 這時發現就已經可以產生驗證碼了,但還有個問題,驗證碼的隨機字元存在哪裡了?

3、探索 kaptcha

我們來檢視 KaptchaServlet 這個類的原始碼,doGet 方法

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException
{
	// Set to expire far in the past.
	resp.setDateHeader("Expires", 0);
	// Set standard HTTP/1.1 no-cache headers.
	resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
	// Set IE extended HTTP/1.1 no-cache headers (use addHeader).
	resp.addHeader("Cache-Control", "post-check=0, pre-check=0");
	// Set standard HTTP/1.0 no-cache header.
	resp.setHeader("Pragma", "no-cache");

	// return a jpeg
	resp.setContentType("image/jpeg");

	// create the text for the image
	String capText = this.kaptchaProducer.createText();

	// store the text in the session
	req.getSession().setAttribute(this.sessionKeyValue, capText);

	// store the date in the session so that it can be compared
	// against to make sure someone hasn't taken too long to enter
	// their kaptcha
	req.getSession().setAttribute(this.sessionKeyDateValue, new Date());

	// create the image with the text
	BufferedImage bi = this.kaptchaProducer.createImage(capText);

	ServletOutputStream out = resp.getOutputStream();

	// write the data out
	ImageIO.write(bi, "jpg", out);
}
複製程式碼

有這樣一段程式碼,獲取字元後存入 session 中,鍵為 sessionKeyValue 這個變數的值

// create the text for the image
String capText = this.kaptchaProducer.createText();
// store the text in the session
req.getSession().setAttribute(this.sessionKeyValue, capText);
複製程式碼

sessionKeyValue 這個變數的值在 init 方法中被賦值

this.sessionKeyValue = config.getSessionKey();
複製程式碼

好我們進入 config.getSessionKey() 中檢視程式碼,發現 session 的鍵為 Constants 這個類中的常量 Constants.KAPTCHA_SESSION_KEY

return this.properties.getProperty(Constants.KAPTCHA_SESSION_CONFIG_KEY, Constants.KAPTCHA_SESSION_KEY);
複製程式碼

4、編寫測試驗證碼是否正常使用的 servlet

我們來驗證一下,編寫一個 servlet

import com.google.code.kaptcha.Constants;

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.io.PrintWriter;

@WebServlet("/test")
public class TestKaptchaServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();
        //獲取傳入的驗證碼
        String captcha = req.getParameter("captcha");
        if (null != captcha) {
            //獲取實際session中的驗證碼
            String code = (String) req.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
            if (null == code){
                resp.sendRedirect("/captcha");
                return;
            }
            if (code.equalsIgnoreCase(captcha)) {
                out.print("驗證輸入正確");
            } else {
                out.print("驗證碼輸入有誤");
            }
        } else {
            out.print("必須輸入驗證碼");
        }
        out.flush();
        out.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
複製程式碼

先訪問驗證碼 http://localhost:8080/captcha 然後訪問 http://localhost:8080/test?captcha=驗證碼

以上這些預設配置能滿足一般業務的使用了,下面通過深入解析 kaptcha 的原始碼自定義配置驗證碼的寬、高、邊框、顏色、字元等

5、深入原始碼自定義配置 kaptcha

再來看一下 init 方法中的程式碼

@Override
public void init(ServletConfig conf) throws ServletException
{
	super.init(conf);

	// Switch off disk based caching.
	ImageIO.setUseCache(false);

	Enumeration<?> initParams = conf.getInitParameterNames();
	while (initParams.hasMoreElements())
	{
		String key = (String) initParams.nextElement();
		String value = conf.getInitParameter(key);
		this.props.put(key, value);
	}

	Config config = new Config(this.props);
	this.kaptchaProducer = config.getProducerImpl();
	this.sessionKeyValue = config.getSessionKey();
	this.sessionKeyDateValue = config.getSessionDate();
}
複製程式碼

這段程式碼獲取 servlet 的初始化引數,並將引數存入 config 物件中,看 config 中的一段程式碼

public boolean isBorderDrawn()
{
	String paramName = Constants.KAPTCHA_BORDER;
	String paramValue = this.properties.getProperty(paramName);
	return this.helper.getBoolean(paramName, paramValue, true);
}
複製程式碼

沒錯,所有的引數名字都是 Constants 類中常量名稱

public class Constants
{
	public final static String KAPTCHA_SESSION_KEY = "KAPTCHA_SESSION_KEY";

	public final static String KAPTCHA_SESSION_DATE = "KAPTCHA_SESSION_DATE";

	public final static String KAPTCHA_SESSION_CONFIG_KEY = "kaptcha.session.key";
	
	//省略其他程式碼
}
複製程式碼

所以,可以給 KaptchaServlet 在 web.xml 配置引數,下面這些配置給大家參考

<servlet>
    <servlet-name>Kaptcha</servlet-name>
    <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>

    <!-- 是否有邊框 -->
    <init-param>
        <param-name>kaptcha.border</param-name>
        <param-value>no</param-value>
    </init-param>
    <!-- 字型顏色 -->
    <init-param>
        <param-name>kaptcha.textproducer.font.color</param-name>
        <param-value>red</param-value>
    </init-param>
    <!-- 圖片寬度 -->
    <init-param>
        <param-name>kaptcha.image.width</param-name>
        <param-value>135</param-value>
    </init-param>
    <!-- 使用哪些字元生成驗證碼 -->
    <init-param>
        <param-name>kaptcha.textproducer.char.string</param-name>
        <param-value>ACDEFHKPRSTWX345679</param-value>
    </init-param>
    <!-- 圖片高度 -->
    <init-param>
        <param-name>kaptcha.image.height</param-name>
        <param-value>50</param-value>
    </init-param>
    <!-- 字型大小 -->
    <init-param>
        <param-name>kaptcha.textproducer.font.size</param-name>
        <param-value>38</param-value>
    </init-param>
    <!-- 干擾線的顏色 -->
    <init-param>
        <param-name>kaptcha.noise.color</param-name>
        <param-value>black</param-value>
    </init-param>
    <!-- 字元個數 -->
    <init-param>
        <param-name>kaptcha.textproducer.char.length</param-name>
        <param-value>6</param-value>
    </init-param>
    <!-- 使用哪些字型 -->
    <init-param>
        <param-name>kaptcha.textproducer.font.names</param-name>
        <param-value>Arial</param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>Kaptcha</servlet-name>
    <url-pattern>/captcha</url-pattern>
</servlet-mapping>
複製程式碼

相關文章