本文分三個步驟介紹驗證碼圖片生成以及與Struts2結合使用。
Step 1.隨機驗證碼
一步一步來,要生成驗證碼圖片,首先要有驗證碼,然後才能在畫在圖片上。爲了能夠靈活控制驗證碼,特別編寫了SecurityCode類,它向 外提供隨機字符串。並且可以控制字符串的長度和難度。SecurityCode類中提供的驗證碼分三個難度,易(全數字)、中(數字+小寫英文)、難(數 字+大小寫英文)。難度使用枚舉SecurityCodeLevle表示,避免使用1、2、3這樣沒有明確意義的數字來區分。
同時,還控制了能否出現重複的字符。爲了能夠方便使用,方法設計爲static。
SecurityCode類:
import java.util.Arrays;
public class SecurityCode {
public enum SecurityCodeLevel {
Simple, Medium, Hard
};
public static String getSecurityCode() {
return getSecurityCode(4, SecurityCodeLevel.Medium, false);
}
public static String getSecurityCode(int length, SecurityCodeLevel level,
boolean isCanRepeat) {
// 隨機抽取len個字符
int len = length;
// 字符集合(除去易混淆的數字0、數字1、字母l、字母o、字母O)
char[] codes = { '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B',
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
// 根據不同的難度截取字符數組
if (level == SecurityCodeLevel.Simple) {
codes = Arrays.copyOfRange(codes, 0, 9);
} else if (level == SecurityCodeLevel.Medium) {
codes = Arrays.copyOfRange(codes, 0, 33);
}
// 字符集合長度
int n = codes.length;
// 拋出運行時異常
if (len > n && isCanRepeat == false) {
throw new RuntimeException(String.format("調用SecurityCode.getSecurityCode(%1$s,%2$s,%3$s)出現異常,當isCanRepeat爲%3$s時,傳入參數%1$s不能大於%4$s", len, level, isCanRepeat, n));
}
// 存放抽取出來的字符
char[] result = new char[len];
// 判斷能否出現重複的字符
if (isCanRepeat) {
for (int i = 0; i < result.length; i++) {
// 索引 0 and n-1
int r = (int) (Math.random() * n);
// 將result中的第i個元素設置爲codes[r]存放的數值
result[i] = codes[r];
}
} else {
for (int i = 0; i < result.length; i++) {
// 索引 0 and n-1
int r = (int) (Math.random() * n);
// 將result中的第i個元素設置爲codes[r]存放的數值
result[i] = codes[r];
// 必須確保不會再次抽取到那個字符,因爲所有抽取的字符必須不相同。
// 因此,這裏用數組中的最後一個字符改寫codes[r],並將n減1
codes[r] = codes[n - 1];
n--;
}
}
return String.valueOf(result);
}
}
Step 2.圖片
第一步已經完成,有了上面SecurityCode類提供的驗證碼,就應該考慮怎麼在圖片上寫字符串了。在Java中操作圖片,需要使用 BufferedImage類,它代表內存中的圖片。寫字符串,就需要從圖片BufferedImage上得到繪圖圖面Graphics,然後在圖面上 drawString。
爲了使驗證碼有一定的干擾性,也繪製了一些噪點。調用Graphics類的drawRect繪製1*1大小的方塊就可以了。
特別說明一下,由於後面要與Strtus2結合使用,而在Struts2中向前臺返回圖片數據使用的是數據流的形式。所以提供了從圖片向流的轉換方法convertImageToStream。
SecurityImage類:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Random;
import com.sun.image.codec.jpeg.ImageFormatException;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
public class SecurityImage {
public static BufferedImage createImage(String securityCode) {
// 驗證碼長度
int codeLength = securityCode.length();
// 字體大小
int fSize = 15;
int fWidth = fSize + 1;
// 圖片寬度
int width = codeLength * fWidth + 6;
// 圖片高度
int height = fSize * 2 + 1;
// 圖片
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.createGraphics();
// 設置背景色
g.setColor(Color.WHITE);
// 填充背景
g.fillRect(0, 0, width, height);
// 設置邊框顏色
g.setColor(Color.LIGHT_GRAY);
// 邊框字體樣式
g.setFont(new Font("Arial", Font.BOLD, height - 2));
// 繪製邊框
g.drawRect(0, 0, width - 1, height - 1);
// 繪製噪點
Random rand = new Random();
// 設置噪點顏色
g.setColor(Color.LIGHT_GRAY);
for (int i = 0; i < codeLength * 6; i++) {
int x = rand.nextInt(width);
int y = rand.nextInt(height);
// 繪製1*1大小的矩形
g.drawRect(x, y, 1, 1);
}
// 繪製驗證碼
int codeY = height - 10;
// 設置字體顏色和樣式
g.setColor(new Color(19, 148, 246));
g.setFont(new Font("Georgia", Font.BOLD, fSize));
for (int i = 0; i < codeLength; i++) {
g.drawString(String.valueOf(securityCode.charAt(i)), i * 16 + 5,
codeY);
}
// 關閉資源
g.dispose();
return image;
}
public static ByteArrayInputStream getImageAsInputStream(String securityCode) {
BufferedImage image = createImage(securityCode);
return convertImageToStream(image);
}
private static ByteArrayInputStream convertImageToStream(BufferedImage image) {
ByteArrayInputStream inputStream = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
JPEGImageEncoder jpeg = JPEGCodec.createJPEGEncoder(bos);
try {
jpeg.encode(image);
byte[] bts = bos.toByteArray();
inputStream = new ByteArrayInputStream(bts);
} catch (ImageFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return inputStream;
}
}
Step 3.驗證碼與Struts 2結合
1)Action
有了上面兩步操作作爲鋪墊,含有驗證碼的圖片就不成問題了,下面就可以使用Struts2的Action向前臺返回圖片數據了。
SecurityCodeImageAction類:
import java.io.ByteArrayInputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.struts2.utils.SecurityCode;
import com.struts2.utils.SecurityImage;
public class ValidCodeAction extends ActionSupport {
private static final long serialVersionUID = 5633780176793520460L;
//圖片流
private ByteArrayInputStream imageStream;
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
try {
HttpServletRequest request = ServletActionContext.getRequest();
String securityCode = SecurityCode.getSecurityCode();
imageStream = SecurityImage.getImageAsInputStream(securityCode);
//放入session中
//session.put("securityCode", securityCode);
System.out.println("securityCode = " + securityCode);
request.getSession().setAttribute("securityCode", securityCode);
} catch (Exception e) {
e.printStackTrace();
}
return SUCCESS;
}
public ByteArrayInputStream getImageStream() {
return imageStream;
}
public void setImageStream(ByteArrayInputStream imageStream) {
this.imageStream = imageStream;
}
}
2)Struts.xml
在 Struts.xml配置文件中,需要配置SecurityCodeImageAction,由於現在返回的是流,就不應該再使用普通的方式了,應該在result上加上type="stream"。
同時這一項的值,應該與SecurityCodeImageAction中的圖片流名稱一致。
Struts.xml:
1 <</span>action name="SecurityCodeImageAction" class="securityCodeImageAction"> 2 <</span>resultname="success" type="stream"> 3 <</span>param name="contentType">image/jpeg</</span>param> 4<</span>param name="inputName">imageStream</</span>param> 5 <</span>paramname="bufferSize">2048</</span>param> 6 </</span>result> 7 </</span>action>
3)前臺JSP
定義一個img元素,將src指向SecurityCodeImageAction就可以了,瀏覽器向Action發送請求,服務器將圖片流返回,圖片就能夠顯示了。
1 <</span>img src="SecurityCodeImageAction" id="Verify" style="cursor:hand;" alt="看不清,換一張"/>
4)JS
驗證碼一般都有點擊刷新的功能,這個也容易實現,點擊圖片,重新給圖片的src賦值。但是這時,瀏覽器會有緩存問題,如果瀏覽器發現src中的url不 變,就認爲圖片沒有改變,就會使用緩存中的圖片,而不是重新向服務器請求。解決辦法是在url後面加上一個時間戳,每次點擊時,時間戳都不一樣,瀏覽器就 認爲是新的圖片,然後就發送請求了。
jQuery:
1 $(function () { 2 //點擊圖片更換驗證碼 3 $("#Verify").click(function(){ 4$(this).attr("src","SecurityCodeImageAction?timestamp="+new Date().getTime()); 5 }); 6 });
JavaScript:
1 window.οnlοad=function(){ 2 var verifyObj = document.getElementByIdx_x("Verify"); 3verifyObj.οnclick=function(){ 4 this.src="SecurityCodeImageAction?timestamp="+newDate().getTime(); 5 }; 6 }
5)效果
生成的驗證碼圖片如下所示,淺藍色的字體,淺灰色的噪點。
圖:驗證碼
結束: 經過上面的三個步驟,Struts2 結合驗證碼的例子就介紹完了。
自己總結: