Java Web 項目關(guān)于 Kaptcha 驗證碼的生成與使用

Web 端中璃俗,為了防止惡意的流量攻擊或者為了防止自動化提交,都會在頁面中引入驗證碼悉默。
驗證碼一般是一些加入了干擾線旧找,做了模糊處理的字符圖片,可能是中文麦牺、字母钮蛛、數(shù)字等。現(xiàn)在一些網(wǎng)站還加入了語音驗證或者手機(jī)驗證剖膳。

驗證碼實現(xiàn)流程如下:

  1. Web 請求驗證碼圖片
  2. 后臺生成隨機(jī)字符魏颓,例如 ABCD,存入 Session 或 Cache
  3. 后臺將 ABCD 做模糊處理吱晒,并以圖片形式返回給前端
  4. 前端提交表單時甸饱,將驗證碼一并帶上
  5. 后臺將前端提交的驗證碼與存在 Session 或 Cache 中的驗證碼比較。如果一致仑濒,則成功叹话。

在實踐中,可以采用自己繪制驗證碼圖片墩瞳,原理就是在圖片上增加干擾線驼壶,干擾點,變形等處理喉酌∪劝迹可以參考這篇博文 java web項目生成驗證碼的解決方案泵喘。

引入 Kaptcha 庫

以下為 Maven 代碼

<!-- Kaptcha驗證碼框架 -->
<dependency>
    <groupId>com.github.axet</groupId>
    <artifactId>kaptcha</artifactId>
    <version>0.0.9</version>
</dependency>

或者去 Google Code 下載,地址為 https://code.google.com/archive/p/kaptcha/

Kaptcha 使用

Kapthca 使用可以有以下兩種方法般妙,一種是直接請求圖片纪铺,驗證碼寫在 Session 中;另一種是作為 WebService 碟渺,由前端主動請求鲜锚。

通過 img 標(biāo)簽請求

通過 img 標(biāo)簽,直接獲取驗證碼圖片苫拍,這個場景適合 JSP 工程這種前后端不完全分離的烹棉,或者僅使用未禁止 Session 和 Cookie 的瀏覽器時。

后臺 web.xml 配置

在根目錄下增加 servlet 和 servlet-mapping

<!-- 登陸驗證碼 Kaptcha -->
<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.textproducer.char.space</param-name>
        <param-value>3</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>Kaptcha</servlet-name>
    <!-- img src 指定格式 -->
    <url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>

前端 img 請求

只需要一行代碼即可怯疤。

注意,此處 kaptcha.jpg 要與 web.xml 中 servlet-mapping 中指定的名字一致催束。

![](http://localhost:7010/kaptcha.jpg)

后臺驗證

**Kaptcha 默認(rèn)會將生成的驗證碼寫入 Session 集峦,因此這需要前端瀏覽器允許記錄 Cookies **,否則可能出現(xiàn) SessionId 不一致使得驗證碼無法正確校驗抠刺。在將 Html5 嵌入 iOS App 時塔淤,由于內(nèi)嵌瀏覽器對 Cookie 做了嚴(yán)格的限制,使得無法正確校驗速妖,才轉(zhuǎn)向了第二種方法高蜂。
因為驗證碼被記錄在 Session 中,因此 Web 端從 Session 中取出即可罕容,至于如何獲得 Session 备恤,視不同項目而定。一般 JSP 項目用這種方法锦秒,其校驗方法如下::

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <%@ page language="java" contentType="text/html; charset=UTF-8" %>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Kaptcha Example</title>
    </head>
    <body>
        Enter in the <a >Kaptcha</a> to see if it matches what is stored in the session attributes.
        <table>
            <tr>
                <td>![](kaptcha.jpg)</td>
                <td valign="top">
                    <form method="POST">
                        <br>sec code:<input type="text" name="kaptchafield"><br />
                        <input type="submit" name="submit">
                    </form>
                </td>
            </tr>
        </table>
        <%
            String c = (String)session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
            String parm = (String) request.getParameter("kaptchafield");
            out.println("Parameter: " + parm + " ? Session Key: " + c + " : ");
            if (c != null && parm != null) {
                if (c.equals(parm)) {
                    out.println("<b>true</b>");
                } else {
                    out.println("<b>false</b>");
                }
            }
        %>
    </body>
</html>

通過 WebService 請求

如果作為 WebService 項目露泊,則無法簡單的通過 img src="kaptcha.jpg" 來獲取驗證碼圖片,而是需要前端主動發(fā)起一次請求旅择,而后臺則是需要將圖片寫入 HttpServletResponse 惭笑。

后臺配置

使用該方法,則不再需要配置 web.xml 生真。
我的項目是 Spring + cxf 的 WebService 項目沉噩,以此為例。首先 Spring 項目柱蟀,需要配置 KaptchaProducer bean 川蒙,其次需要為 WebService 項目接口。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">

    <jaxrs:server id="captcha" address="/captcha">
        <jaxrs:serviceBeans>
            <ref bean="captchaService"/>
        </jaxrs:serviceBeans>
    </jaxrs:server>

    <bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
        <property name="config">
            <bean class="com.google.code.kaptcha.util.Config">
                <constructor-arg>
                    <props>
                        <!-- Kaptcha 配置 -->
                        <prop key="kaptcha.border">no</prop>
                        <prop key="kaptcha.image.width">250</prop>
                        <prop key="kaptcha.textproducer.font.size">80</prop>
                        <prop key="kaptcha.image.height">90</prop>
                        <prop key="kaptcha.session.key">code</prop>
                        <prop key="kaptcha.textproducer.char.length">4</prop>
                    </props>
                </constructor-arg>
            </bean>
        </property>
    </bean>
</beans>

然后編寫驗證碼接口實現(xiàn)长已,生成驗證碼需要一個對應(yīng)到會話的唯一的 ID派歌,可以使用 token 或者 uuid 等弯囊,在緩存中,該 ID 將作為 Map 的 Key 胶果。

@Path(value = "/")
@WebService
//@Produces(value = MediaType.APPLICATION_JSON)
public interface CaptchaService {
    @GET
    @Path(value = "gen")
    void genCaptcha(@Context HttpServletRequest request,
                    @Context HttpServletResponse response,
                    @QueryParam("token") String token) throws IOException;

}
/////////////////////////////////////////////////////////////////////////////////
@Component(value = "captchaService")
public class CaptchaServiceImpl implements CaptchaService {
    private    @Value("${cache.captchaexpire}") int EXPIRE = 5;

    @Override
    public void genCaptcha(HttpServletRequest request, HttpServletResponse response, String token) throws IOException {
        String capText;
        capText = CaptchaUtil.gen(request, response);
        // 自定義的緩存匾嘱,保存圖片驗證碼,保存 5 min 有效
        SessionCacheManager.getInstance().add(token, CacheKey.CAPTCHA_PC, capText, EXPIRE * 60 * 1000, false);
    }

    /**
     * 采用了 Google 提供的 Kaptcha 驗證碼庫生成驗證碼
     *
     * @param request  HttpServletRequest 請求
     * @param response HttpServletResponse 返回早抠,用于輸出驗證碼圖片流
     */
    private static String gen(HttpServletRequest request,
                             HttpServletResponse response) throws IOException {
        response.setDateHeader("Expires", 0);
        // Set standard HTTP/1.1 no-cache headers.
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        // Set standard HTTP/1.0 no-cache header.
        response.setHeader("Pragma", "no-cache");
        // return a jpeg
        response.setContentType("image/jpeg");
        // p3p 跨域
        response.setHeader("P3P", "CP='IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT'");

        // create the text for the image
        String capText = captchaProducer.createText();
        // store the text in the session
        request.getSession().setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);

        // create the image with the text
        BufferedImage bi = captchaProducer.createImage(capText);
        ServletOutputStream out = response.getOutputStream();
        // write the data out
        ImageIO.write(bi, "jpg", out);
        try {
            out.flush();
        } finally {
            out.close();
        }
        return capText;
    }
}

前端請求

前端在獲得 token 后霎烙,再發(fā)起 webservice 請求。改變 img src 后蕊连,返回的圖片將直接顯示悬垃。

<script type="application/javascript">
    document.getElementById('captcha').src=URL+"/captcha/gen?token="+encodeURIComponent(token)+"&r="+Math.random();

    function refresh() {
        document.getElementById('captcha')
            .src=URL+"/captcha/gen?token="+encodeURIComponent(token)+"&r="+Math.random();
    }
</script>
<img id="captcha" style="vertical-align: middle;" title="點擊更換" src="" alt="未預(yù)登錄,無驗證圖片" height="40" width="85" onclick="refresh();">

后臺驗證

因為在生成驗證碼時甘苍,已經(jīng)將驗證碼寫入了緩存尝蠕,此處校驗只需要從緩存取出驗證碼,與前端傳入的驗證碼相比較即可载庭。

Kaptcha 配置

屬性 說明 默認(rèn)值
kaptcha.border 是否有邊框,可以自己設(shè)置yes看彼,no 默認(rèn)為true
kaptcha.border.color 邊框顏色 默認(rèn)為Color.BLACK
kaptcha.border.thickness 邊框粗細(xì)度 默認(rèn)為1
kaptcha.producer.impl 驗證碼生成器 默認(rèn)為DefaultKaptcha
kaptcha.textproducer.impl 驗證碼文本生成器 默認(rèn)為DefaultTextCreator
kaptcha.textproducer.char.string 驗證碼文本字符內(nèi)容范圍 默認(rèn)為abcde2345678gfynmnpwx
kaptcha.textproducer.char.length 驗證碼文本字符長度 默認(rèn)為5
kaptcha.textproducer.font.names 驗證碼文本字體樣式 默認(rèn)為new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
kaptcha.textproducer.font.size 驗證碼文本字符大小 默認(rèn)為40
kaptcha.textproducer.font.color 驗證碼文本字符顏色 默認(rèn)為Color.BLACK
kaptcha.textproducer.char.space 驗證碼文本字符間距 默認(rèn)為2
kaptcha.noise.impl 驗證碼噪點生成對象 默認(rèn)為DefaultNoise
kaptcha.noise.color 驗證碼噪點顏色 默認(rèn)為Color.BLACK
kaptcha.obscurificator.impl 驗證碼樣式引擎 默認(rèn)為WaterRipple
kaptcha.word.impl 驗證碼文本字符渲染 默認(rèn)為DefaultWordRenderer
kaptcha.background.impl 驗證碼背景生成器 默認(rèn)為DefaultBackground
kaptcha.background.clear.from 驗證碼背景顏色漸進(jìn) 默認(rèn)為Color.LIGHT_GRAY
kaptcha.background.clear.to 驗證碼背景顏色漸進(jìn) 默認(rèn)為Color.WHITE
kaptcha.image.width 驗證碼圖片寬度 默認(rèn)為200
kaptcha.image.height 驗證碼圖片高度 默認(rèn)為50

參考資料

  1. Kaptcha 官網(wǎng),https://code.google.com/archive/p/kaptcha/
  2. java web項目生成驗證碼的解決方案囚聚,作者 夜空中苦逼的程序員靖榕, http://blog.csdn.net/chenghui0317/article/details/12526439
  3. java使用kaptcha 驗證碼組件,作者 代碼如瘋顽铸, http://blog.csdn.net/mdcmy/article/details/7733796
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末茁计,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谓松,更是在濱河造成了極大的恐慌星压,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鬼譬,死亡現(xiàn)場離奇詭異租幕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拧簸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門劲绪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盆赤,你說我怎么就攤上這事贾富。” “怎么了牺六?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵颤枪,是天一觀的道長。 經(jīng)常有香客問我淑际,道長畏纲,這世上最難降的妖魔是什么扇住? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮盗胀,結(jié)果婚禮上艘蹋,老公的妹妹穿的比我還像新娘。我一直安慰自己票灰,他們只是感情好女阀,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著屑迂,像睡著了一般浸策。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惹盼,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天庸汗,我揣著相機(jī)與錄音,去河邊找鬼手报。 笑死蚯舱,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的昧诱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼所袁,長吁一口氣:“原來是場噩夢啊……” “哼盏档!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起燥爷,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤蜈亩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后前翎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稚配,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年港华,在試婚紗的時候發(fā)現(xiàn)自己被綠了道川。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡立宜,死狀恐怖冒萄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情橙数,我是刑警寧澤尊流,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站灯帮,受9級特大地震影響崖技,放射性物質(zhì)發(fā)生泄漏逻住。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一迎献、第九天 我趴在偏房一處隱蔽的房頂上張望瞎访。 院中可真熱鬧,春花似錦忿晕、人聲如沸装诡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸦采。三九已至,卻和暖如春咕幻,著一層夾襖步出監(jiān)牢的瞬間渔伯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工肄程, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留锣吼,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓蓝厌,卻偏偏與公主長得像玄叠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拓提,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,072評論 25 707
  • JCaptcha 簡介 CAPTCHA 全稱 Completely Automated Public Turing...
    誰在烽煙彼岸閱讀 716評論 0 0
  • 從三月份找實習(xí)到現(xiàn)在读恃,面了一些公司,掛了不少代态,但最終還是拿到小米寺惫、百度、阿里蹦疑、京東西雀、新浪、CVTE歉摧、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,239評論 11 349
  • 如果問起你人生中最重要的事情是什么的時候叁温,我相信每個人都會有不同的回答豆挽。 年少時候可能你會有各種的夢想,想以后做什...
    紅袖飛揚(yáng)閱讀 8,454評論 0 1
  • 暮剛剛來到這個班的時候券盅,青就坐在她的前面帮哈。那個時候暮和青并不認(rèn)識。暮是一個大大咧咧的女生锰镀,那時娘侍,她經(jīng)常吐槽青活...
    素憶1998閱讀 918評論 0 0