驗證碼在很多地方都會遇到,實現的方法和形式也有很多,主要的目的就是為了安全,防止一些惡意的攻擊等。說實話那么多年竟然沒注意過這東西,原理很簡單,貼出來給大家做個參考。
1、簡單介紹
一般稍微有些經驗的程序員都不會再自己寫原生驗證碼生成了,因為各種強大的開源組件,足以解決我們大部分的需求。但是,畢竟也是剛接觸這東西,還需要從原理入手的。
項目效果圖:
下面我就簡單介紹下原生和使用開源項目kaptcha生成驗證碼的兩種形式。
2、jdk原生生成驗證碼
效果:
2.1 驗證碼生成的流程
1、定義BufferedImage(圖像數據緩沖區)對象
2、獲得Graphics對象
3、隨機生成驗證碼字母或者數字
4、使用Graphics繪制圖片
5、記錄驗證碼信息到session或數據庫,以便校驗
5、ImageIO輸出圖片到客戶端
2.2 代碼講解
這里我就不整合框架了,簡單用servlet講下步驟,框架中也是這樣做
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException{ //定義BufferedImage(圖像數據緩沖區)對象 BufferedImage bi = new BufferedImage(68,22,BufferedImage.TYPE_INT_RGB); //繪制圖片 Graphics g = bi.getGraphics(); //背景色 Color c = new Color(200,150,255); g.setColor(c); //圖片坐標 g.fillRect(0, 0, 68, 22); //驗證碼選取 char[] ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray(); Random r = new Random(); int len=ch.length,index; StringBuffer sb = new StringBuffer(); for(int i=0; i<4; i++){ index = r.nextInt(len); g.setColor(new Color(r.nextInt(88),r.nextInt(188),r.nextInt(255))); Font ft = new Font(Font.SANS_SERIF, Font.BOLD, 16); g.setFont(ft); g.drawString(ch[index]+"", (i*15)+3, 18); sb.append(ch[index]); }
//打印驗證碼,項目中用日志 System.out.println(sb.toString());
//驗證碼寫到session request.getSession().setAttribute("checkCode", sb.toString()); //ImageIO寫出圖片 ImageIO.write(bi, "JPG", response.getOutputStream()); }
jsp:
<form action="XX" method="get"> 驗證碼:<input type="text" name="checkcode"/> <img alt="點擊更換驗證碼" id="imagecode" onclick="this.src='/servlet/ImageServlet?random='+Math.random();" src="/servlet/ImageServlet"/> <input type="submit" value="提交"> </form>
相信稍微有些經驗的同學看過上面的代碼都能理解其中的原理吧。至於后面的校驗相信大家都會的。
下面我重點講下使用kaptcha開源組件生成驗證碼的流程,這里我會用servlet和springboot+springmvc的方式分別進行下介紹。
3、使用kaptcha組件生成驗證碼
既然說到開源組件,必然功能是強大的,還是先看效果圖!
數字字母組合
數字字母漢字組合
算數計算
3.1 kaptcha的參數詳解
Constant | 描述 | 默認值 |
kaptcha.border | 圖片邊框,合法值:yes , no | yes |
kaptcha.border.color | 邊框顏色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. | black |
kaptcha.border.thickness | 邊框厚度,合法值:>0 | 1 |
kaptcha.image.width | 圖片寬 | 200 |
kaptcha.image.height | 圖片高 | 50 |
kaptcha.producer.impl | 圖片實現類 | com.google.code.kaptcha.impl.DefaultKaptcha |
kaptcha.textproducer.impl | 文本實現類 | com.google.code.kaptcha.text.impl.DefaultTextCreator |
kaptcha.textproducer.char.string | 文本集合,驗證碼值從此集合中獲取 | abcde2345678gfynmnpwx |
kaptcha.textproducer.char.length | 驗證碼長度 | 5 |
kaptcha.textproducer.font.names | 字體 | Arial, Courier |
kaptcha.textproducer.font.size | 字體大小 | 40px |
kaptcha.textproducer.font.color | 字體顏色,合法值: r,g,b 或者 white,black,blue. | black |
kaptcha.textproducer.char.space | 文字間隔 | 2 |
kaptcha.noise.impl | 干擾實現類 | com.google.code.kaptcha.impl.DefaultNoise |
kaptcha.noise.color | 干擾顏色,合法值: r,g,b 或者 white,black,blue. | black |
kaptcha.obscurificator.impl | 圖片樣式: 水紋com.google.code.kaptcha.impl.WaterRipple 魚眼com.google.code.kaptcha.impl.FishEyeGimpy 陰影com.google.code.kaptcha.impl.ShadowGimpy |
com.google.code.kaptcha.impl.WaterRipple |
kaptcha.background.impl | 背景實現類 | com.google.code.kaptcha.impl.DefaultBackground |
kaptcha.background.clear.from | 背景顏色漸變,開始顏色 | light grey |
kaptcha.background.clear.to | 背景顏色漸變,結束顏色 | white |
kaptcha.word.impl | 文字渲染器 | com.google.code.kaptcha.text.impl.DefaultWordRenderer |
kaptcha.session.key | session key | KAPTCHA_SESSION_KEY |
kaptcha.session.date | session date | KAPTCHA_SESSION_DATE |
3.2 代碼講解
1、servlet方式
上圖可以看到,kaptcha處理驗證碼的類是KaptchaServlet。這里我們就可以像原生的方式一樣直接請求這個servlet,這里主要講下使用servlet和使用框架的時候參數配置是不用的,servlet的是配置在web.xml中的,形式如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- 登陸驗證碼Kaptcha 2--> <servlet> <servlet-name>Kaptcha</servlet-name> <servlet-class> com.google.code.kaptcha.servlet.KaptchaServlet </servlet-class> <init-param> <description>圖片邊框,合法值:yes , no</description> <param-name>kaptcha.border</param-name> <param-value>yes</param-value> </init-param> <init-param> <description> 邊框顏色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. </description> <param-name>kaptcha.border.color</param-name> <param-value>black</param-value> </init-param> <init-param> <description>邊框厚度,合法值:>0</description> <param-name>kaptcha.border.thickness</param-name> <param-value>1</param-value> </init-param> <init-param> <description>圖片寬 200</description> <param-name>kaptcha.image.width</param-name> <param-value>200</param-value> </init-param> <init-param> <description>圖片高 50</description> <param-name>kaptcha.image.height</param-name> <param-value>50</param-value> </init-param> <init-param> <description>圖片實現類</description> <param-name>kaptcha.producer.impl</param-name> <param-value> com.google.code.kaptcha.impl.DefaultKaptcha </param-value> </init-param> <init-param> <description>文本實現類</description> <param-name>kaptcha.textproducer.impl</param-name> <param-value> com.google.code.kaptcha.text.impl.DefaultTextCreator </param-value> </init-param> <init-param> <description>驗證碼長度 5</description> <param-name>kaptcha.textproducer.char.length</param-name> <param-value>5</param-value> </init-param> <init-param> <description>字體 Arial, Courier</description> <param-name>kaptcha.textproducer.font.names</param-name> <param-value>Arial, Courier</param-value> </init-param> <init-param> <description>字體大小 40px.</description> <param-name>kaptcha.textproducer.font.size</param-name> <param-value>40</param-value> </init-param> <init-param> <description> 字體顏色,合法值: r,g,b 或者 white,black,blue. </description> <param-name>kaptcha.textproducer.font.color</param-name> <param-value>black</param-value> </init-param> <init-param> <description>文字間隔 2</description> <param-name>kaptcha.textproducer.char.space</param-name> <param-value>2</param-value> </init-param> <init-param> <description>干擾實現類</description> <param-name>kaptcha.noise.impl</param-name> <param-value> com.google.code.kaptcha.impl.DefaultNoise </param-value> </init-param> <init-param> <description> 干擾顏色,合法值: r,g,b 或者 white,black,blue. </description> <param-name>kaptcha.noise.color</param-name> <param-value>black</param-value> </init-param> <init-param> <description> 圖片樣式: 水紋com.google.code.kaptcha.impl.WaterRipple 魚眼com.google.code.kaptcha.impl.FishEyeGimpy 陰影com.google.code.kaptcha.impl.ShadowGimpy </description> <param-name>kaptcha.obscurificator.impl</param-name> <param-value> com.google.code.kaptcha.impl.WaterRipple </param-value> </init-param> <init-param> <description>背景實現類</description> <param-name>kaptcha.background.impl</param-name> <param-value> com.google.code.kaptcha.impl.DefaultBackground </param-value> </init-param> <init-param> <description>背景顏色漸變,開始顏色</description> <param-name>kaptcha.background.clear.from</param-name> <param-value>green</param-value> </init-param> <init-param> <description>背景顏色漸變,結束顏色</description> <param-name>kaptcha.background.clear.to</param-name> <param-value>white</param-value> </init-param> <init-param> <description>文字渲染器</description> <param-name>kaptcha.word.impl</param-name> <param-value> com.google.code.kaptcha.text.impl.DefaultWordRenderer </param-value> </init-param> <init-param> <description> session中存放驗證碼的key鍵 </description> <param-name>kaptcha.session.key</param-name> <param-value>KAPTCHA_SESSION_KEY</param-value> </init-param> <init-param> <description> The date the kaptcha is generated is put into the HttpSession. This is the key value for that item in the session. </description> <param-name>kaptcha.session.date</param-name> <param-value>KAPTCHA_SESSION_DATE</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>Kaptcha</servlet-name> <url-pattern>/randomcode.jpg</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
2、使用springboot+springmvc框架
這里新建一個maven項目,添加依賴
<dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency>
controller:
@Controller public class UserController { @Autowired private Producer captchaProducer; @RequestMapping("/ran/random") public void checkCode(HttpServletRequest request,HttpServletResponse response) throws IOException{ byte[] captchaChallengeAsJpeg = null; ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream(); try { //生產驗證碼字符串並保存到session中 String createText = captchaProducer.createText(); request.getSession().setAttribute("checkCode", createText); //使用生產的驗證碼字符串返回一個BufferedImage對象並轉為byte寫入到byte數組中 BufferedImage challenge = captchaProducer.createImage(createText); ImageIO.write(challenge, "jpg", jpegOutputStream); } catch (IllegalArgumentException e) { response.sendError(response.SC_NOT_FOUND); return; } //定義response輸出類型為image/jpeg類型,使用response輸出流輸出圖片的byte數組 captchaChallengeAsJpeg = jpegOutputStream.toByteArray(); response.setHeader("Cache-Control", "no-store"); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); response.setContentType("image/jpeg"); ServletOutputStream responseOutputStream = response.getOutputStream(); responseOutputStream.write(captchaChallengeAsJpeg); responseOutputStream.flush(); responseOutputStream.close(); } }
配置類CaptchaConfig:
這里@value是為了將相關屬性寫進application.properties,避免硬編碼,為了方便測試我先注掉
package com.allan.base; import java.util.Properties; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.google.code.kaptcha.impl.DefaultKaptcha; import com.google.code.kaptcha.util.Config; /** * 驗證碼配置類 * @author zhangzhuo * */ @Configuration public class CaptchaConfig { @Value("${kaptcha.border}") private String border; @Value("${kaptcha.border.color}") private String borderColor; @Value("${kaptcha.textproducer.font.color}") private String fontColor; @Value("${kaptcha.image.width}") private String imageWidth; @Value("${kaptcha.image.height}") private String imageHeight; @Value("${kaptcha.session.key}") private String sessionKey; @Value("${kaptcha.textproducer.char.length}") private String charLength; @Value("${kaptcha.textproducer.font.names}") private String fontNames; @Bean(name="captchaProducer") public DefaultKaptcha getKaptchaBean(){ DefaultKaptcha defaultKaptcha=new DefaultKaptcha(); Properties properties=new Properties(); /* properties.setProperty("kaptcha.border", border); properties.setProperty("kaptcha.border.color", borderColor); properties.setProperty("kaptcha.textproducer.font.color", fontColor); properties.setProperty("kaptcha.image.width", imageWidth); properties.setProperty("kaptcha.image.height", imageHeight); properties.setProperty("kaptcha.session.key", sessionKey); properties.setProperty("kaptcha.textproducer.char.length", charLength); properties.setProperty("kaptcha.textproducer.font.names", fontNames); properties.setProperty("kaptcha.textproducer.font.size", "30"); */ properties.setProperty("kaptcha.border", "yes"); properties.setProperty("kaptcha.border.color", "105,179,90"); properties.setProperty("kaptcha.textproducer.font.color", "blue"); properties.setProperty("kaptcha.image.width", "90"); properties.setProperty("kaptcha.image.height", "28"); properties.setProperty("kaptcha.textproducer.font.size", "28"); properties.setProperty("kaptcha.session.key", "code"); properties.setProperty("kaptcha.textproducer.char.length", "4"); properties.setProperty("kaptcha.textproducer.char.space", "2"); properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy"); properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise"); properties.setProperty("kaptcha.textproducer.font.names", "宋體,楷體,微軟雅黑"); Config config=new Config(properties); defaultKaptcha.setConfig(config); return defaultKaptcha; } }
啟動類:
package com.allan.server; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.ComponentScan; @EnableAutoConfiguration @ComponentScan(basePackages = {"com.allan.controller","com.allan.service","com.allan.base"}) @MapperScan(basePackages = "com.allan.mapper") public class StartApp { public static void main(String[] args) { SpringApplication.run(StartApp.class, args); } }
jsp:
<p class="main"> <label>驗證碼: </label> <input name="randomCode" onkeyup="enterSubmit(event)" placeholder="驗證碼" style="width: 105px;" maxlength="4" /> <span class="yzm-pic"> <img src="/ran/random" alt="驗證碼,點擊圖片更換" onclick="this.src='/ran/random?random='+Math.random();" /> </span> </p>
最終的效果圖:
最后說下springboot除了上面寫代碼的形式還可以寫成下面的配置文件:
可以定義applicationcontext-check.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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.xsd"> <bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha"> <property name="config"> <bean class="com.google.code.kaptcha.util.Config"> <constructor-arg type="java.util.Properties"> <props> <prop key = "kaptcha.border ">yes</prop> <prop key="kaptcha.border.color">105,179,90</prop> <prop key="kaptcha.textproducer.font.color">blue</prop> <prop key="kaptcha.image.width">100</prop> <prop key="kaptcha.image.height">50</prop> <prop key="kaptcha.textproducer.font.size">27</prop> <prop key="kaptcha.session.key">code</prop> <prop key="kaptcha.textproducer.char.length">4</prop> <prop key="kaptcha.textproducer.font.names">宋體,楷體,微軟雅黑</prop> <prop key="kaptcha.textproducer.char.string">0123456789ABCEFGHIJKLMNOPQRSTUVWXYZ</prop> <prop key="kaptcha.obscurificator.impl">com.google.code.kaptcha.impl.WaterRipple</prop> <prop key="kaptcha.noise.color">black</prop> <prop key="kaptcha.noise.impl">com.google.code.kaptcha.impl.DefaultNoise</prop> <prop key="kaptcha.background.clear.from">185,56,213</prop> <prop key="kaptcha.background.clear.to">white</prop> <prop key="kaptcha.textproducer.char.space">3</prop> </props> </constructor-arg> </bean> </property> </bean> </beans>
如果寫配置文件,這邊在啟動的時候需要引入配置文件
@ImportResource(locations={"classpath:applicationcontext-check.xml"})
基本上就是這些,至於漢字組合或者計算形式的驗證碼,這邊我們只要實現kaptcha.textproducer.impl和com.google.code.kaptcha.servlet.KaptchaServlet這兩個類就行了,大家可以看下源碼,很簡單的。
最后的最后,如果有對於springboot不熟悉的同學可以看下我整理的這些文章,相信會有些幫助的http://www.cnblogs.com/allanzhang/category/1000281.html
jdk版源碼:https://git.oschina.net/allanzhang/checkcode.git