Web 端中璃俗,為了防止惡意的流量攻擊或者為了防止自動化提交,都會在頁面中引入驗證碼悉默。
驗證碼一般是一些加入了干擾線旧找,做了模糊處理的字符圖片,可能是中文麦牺、字母钮蛛、數(shù)字等。現(xiàn)在一些網(wǎng)站還加入了語音驗證或者手機(jī)驗證剖膳。
驗證碼實現(xiàn)流程如下:
- Web 請求驗證碼圖片
- 后臺生成隨機(jī)字符魏颓,例如 ABCD,存入 Session 或 Cache
- 后臺將 ABCD 做模糊處理吱晒,并以圖片形式返回給前端
- 前端提交表單時甸饱,將驗證碼一并帶上
- 后臺將前端提交的驗證碼與存在 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 |
參考資料
- Kaptcha 官網(wǎng),https://code.google.com/archive/p/kaptcha/
- java web項目生成驗證碼的解決方案囚聚,作者 夜空中苦逼的程序員靖榕, http://blog.csdn.net/chenghui0317/article/details/12526439
- java使用kaptcha 驗證碼組件,作者 代碼如瘋顽铸, http://blog.csdn.net/mdcmy/article/details/7733796