在系統的研發過程中,為了增加系統安全性,防止一些不良使用者的惡意攻擊,很多系統都會採用產生並驗證驗證碼的方式、滑動解鎖的方式讓使用者進行一些操作之後才能讓使用者登入,本文我們就簡單講講如何產生圖片驗證碼,如何驗證圖片驗證碼。
一、圖片驗證碼的產生
1、首先我們先產生一個驗證碼,驗證碼的建置規則多種多樣,我們這裡就不在贅述了,可以參考文章http://blog.csdn.net/zyhlwzy/article/details/77850395(驗證碼產生與發送)。
2、提供圖片width, height、imageType參數構建BufferedImage對象,BufferedImage類是具有緩衝區的Image類,Image類是用於描述映像資訊的類。
3、通過構建的BufferedImage對象擷取Graphics對象,該對象可以在映像上進行各種繪製操作。
4、通過Graphics對象繪製幹擾線、畫字串
5、構建ByteArrayOutputStream對象,將繪製好的image資訊寫入ByteArrayOutputStream。
6、將ByteArrayOutputStream轉化成Byte數組寫入到HttpServletResponse,通過流動形式輸出到用戶端。
詳細代碼如下:
public class ImageVerifyCodeUtils { private static Random random = new Random(); private static int width = 80;// 圖片寬 private static int height = 38;// 圖片高 private static int lineSize = 40;// 幹擾線數量 /* * 獲得字型 */ private static Font getFont() { return new Font("Fixedsys", Font.CENTER_BASELINE, 18); } /* * 獲得顏色 */ private static Color getRandColor(int fc, int bc) { if (fc > 255) fc = 255; if (bc > 255) bc = 255; int r = fc + random.nextInt(bc - fc - 16); int g = fc + random.nextInt(bc - fc - 14); int b = fc + random.nextInt(bc - fc - 18); return new Color(r, g, b); } /** * @Comment 繪製幹擾線 * @Author Ron * @Date 2017年9月15日 下午4:31:04 * @return */ private static void drowLine(Graphics g) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(13); int yl = random.nextInt(15); g.drawLine(x, y, x + xl, y + yl); } /** * @Comment 繪製字串 * @Author Ron * @Date 2017年9月15日 下午6:06:25 * @return */ private static void drowString(Graphics g,String vchar,int i) { g.setFont(getFont()); g.setColor(new Color(random.nextInt(101), random.nextInt(111), random .nextInt(121))); g.translate(random.nextInt(3), random.nextInt(3)); g.drawString(vchar, 13 * i, 25); } /** * @Comment 擷取隨機驗證碼 * @Author Ron * @Date 2017年9月15日 下午4:24:02 * @return */ public static void getRandcode(HttpServletRequest request,HttpServletResponse response,String verifyCode) { // BufferedImage類是具有緩衝區的Image類,Image類是用於描述映像資訊的類 BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_BGR); // 產生Image對象的Graphics對象,該對象可以在映像上進行各種繪製操作 Graphics g = image.getGraphics(); g.fillRect(0, 0, width, height); g.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 18)); g.setColor(getRandColor(110, 133)); // 繪製幹擾線 for (int i = 0; i <= lineSize; i++) { drowLine(g); } //畫字串 for (int i = 0; i < verifyCode.length(); i++) { drowString(g,String.valueOf(verifyCode.charAt(i)), i); } g.dispose(); try { ByteArrayOutputStream tmp = new ByteArrayOutputStream(); ImageIO.write(image, "png", tmp); tmp.close(); Integer contentLength = tmp.size(); response.setHeader("content-length", contentLength + ""); response.getOutputStream().write(tmp.toByteArray());// 將記憶體中的圖片通過流動形式輸出到用戶端 } catch (Exception e) { e.printStackTrace(); }finally{ try { response.getOutputStream().flush(); response.getOutputStream().close(); } catch (Exception e2) { e2.printStackTrace(); } } }}
二、Spring Session儲存驗證碼
對於一些比較小型的系統,類似於驗證碼這種臨時的資料,一般情況下都會選擇儲存在Session中。
在很多的應用伺服器中,都會將HTTP session狀態儲存在JVM中,這個JVM與運行應用程式代碼的JVM是同一個,因為這樣易於實現,並且速度很快。當新的應用伺服器執行個體加入或離開叢集時,HTTP session會基於現有的應用伺服器執行個體進行重新平衡。在彈性的雲環境中,我們會擁有上百個應用伺服器執行個體,並且執行個體的數量可能在任意時刻增加或減少,這樣的話,我們就會遇到一些問題: 重平衡HTTP session可能會成為效能瓶頸。 為了儲存大量的session,會需要很大的堆空間,這會導致垃圾收集,從而對效能產生負面影響。 雲基礎設施通常會禁止TCP多播(multicast),但是session管理器常常會使用這種機制來發現哪一個應用伺服器執行個體加入或離開了叢集。
因此,更為高效的辦法是將HTTP session狀態儲存在獨立的資料存放區中,這個儲存位於運行應用程式代碼的JVM之外。例如,我們可以將100個Tomcat執行個體配置為使用Redis來儲存session狀態,當Tomcat執行個體增加或減少的時候,Redis中所儲存的session並不會受到影響。同時,因為Redis是使用C語言編寫的,所以它可以使用上百GB甚至TB層級的RAM,它不會涉及到垃圾收集的問題。
對於像Tomcat這樣的開原始伺服器,很容易找到session管理器的替代方案,這些替代方案可以使用外部的資料存放區,如Redis或Memcached。但是,這些配置過程可能會比較複雜,而且每種應用伺服器都有所差別。對於閉源的產品,如WebSphere和Weblogic,尋找它們的session管理器替代方案不僅非常困難,在有些時候,甚至是無法實現的。
Spring Session提供了一種獨立於應用伺服器的方案,這種方案能夠在Servlet規範之內配置可插拔的session資料存放區,不依賴於任何應用伺服器的特定API。這就意味著Spring Session能夠用於實現了servlet規範的所有應用伺服器之中(Tomcat、Jetty、 WebSphere、WebLogic、JBoss等),它能夠非常便利地在所有應用伺服器中以完全相同的方式進行配置。我們還可以選擇任意最適應需求的外部session資料存放區。這使得Spring Session成為一個很理想的遷移工具,協助我們將傳統的JavaEE應用轉移到雲中,使其成為滿足 12-factor(https://12factor.net/)的應用。
如何整合Spring Session(我們使用Redis儲存資料),分為以下幾步:
2.1、安裝部署Redis
具體參考Redis官網(https://redis.io/)
2.2、配置Spring Session依賴
配置Spring Session依賴很簡單,直接在pom.xml中增加Spring Session依賴即可,因為我們使用的是Redis儲存資料,所以我們只要依賴spring-session-data-redis。
<!-- spring-session-data-redis依賴 --><dependency><groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>${spring_session_data_redis_version}</version></dependency>
2.3、整合Redis用戶端,配置Redis參數
在pom.xml中添加Redis用戶端依賴
<!-- Redis用戶端配置 --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>${redis_clients_version}</version></dependency>
在resources目錄下的config.properties檔案中寫入Redis資訊(主機地址,連接埠等)
#Redis資訊配置#綁定的主機地址 redis.host=127.0.0.1#指定Redis監聽連接埠,預設連接埠為6379 redis.port=6379#授權密碼(本例子沒有使用) redis.password=123456#最大空閑數:空閑連結數大於maxIdle時,將進行回收 redis.maxIdle=100#最大串連數:能夠同時建立的“最大連結個數” redis.maxTotal=300#最大等待時間:單位ms redis.maxWait=1000#使用串連時,檢測串連是否成功 redis.testOnBorrow=true#當用戶端閑置多長時間後關閉串連,如果指定為0,表示關閉該功能 redis.timeout=10000
在resources目錄下建立一個spring-context-redis.xml檔案,開始配置Redis。
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <context:annotation-config/> <!-- 引入properties設定檔 --> <context:property-placeholder ignore-unresolvable="true" location="classpath:config.properties" /> <bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"> <property name="maxInactiveIntervalInSeconds" value="600" /> </bean> <!-- jedis 配置 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" > <property name="maxTotal" value="${redis.maxTotal}"/> <property name="maxIdle" value="${redis.maxIdle}" /> <property name="maxWaitMillis" value="${redis.maxWait}" /> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <!-- redis伺服器中心 --> <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" > <property name="poolConfig" ref="poolConfig" /> <property name="port" value="${redis.port}" /> <property name="hostName" value="${redis.host}" /> <property name="timeout" value="${redis.timeout}" ></property> </bean></beans>
因為我們在web.xml中已經配置,所有以spring-context開頭的xml檔案,在上下文初始化時都會載入,所以上下文初始化時,Redis的相關內容也會載入並進行初始化。
2.4、配置Spring Session過濾器
在web.xml中,加入如下配置:
<!-- Spring Session 過濾器start --><filter><filter-name>springSessionRepositoryFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><filter-mapping><filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping><!-- Spring Session 過濾器end -->
所有資訊的配置完成之後,我們使用Spring Session的方式和傳統的使用方法基本一致,比如我們本執行個體中我們需要儲存驗證碼,我們只需要按照如下方式操作就行:
request.getSession().setAttribute("loginVerifyCode", 驗證碼);
擷取驗證碼:
request.getSession().getAttribute("loginVerifyCode");
項目源碼:https://github.com/Ron-Zheng/blog-system
參考文獻:https://www.infoq.com/articles/Next-Generation-Session-Management-with-Spring-Session