//生成验证码的类
publicclass SecurityCode {
/**
*验证码难度级别,Simple只包含数字,Medium包含数字和小写英文,Hard包含数字和大小写英文
*/
publicenum SecurityCodeLevel {Simple,Medium,Hard};
/**
*产生默认验证码,4位高级难度
*@return String验证码
*/
publicstatic StringgetSecurityCode(Map<String, Object> session){
StringsecurityCode=getSecurityCode(4,SecurityCodeLevel.Hard,false);
session.put("securityCode",securityCode);
return securityCode;
}
/**
*产生长度和难度任意的验证码
*@paramlength 长度
*@paramlevel 难度级别
*@paramisCanRepeat 是否能够出现重复的字符,如果为true,则可能出现5578这样包含两个5,如果为false,则不可能出现这种情况
*@return String验证码
*/
publicstatic String getSecurityCode(intlength,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);
}elseif(level==SecurityCodeLevel.Medium){
codes=Arrays.copyOfRange(codes,0,33);
}
//字符集合长度
int n=codes.length;
//抛出运行时异常
if(len>n&&isCanRepeat==false){
thrownew RuntimeException(
String.format("调用SecurityCode.getSecurityCode(%1$s,%2$s,%3$s)出现异常," +
"当isCanRepeat为%3$s时,传入参数%1$s不能大于%4$s",
len,level,isCanRepeat,n));
}
//存放抽取出来的字符
char[] result=newchar[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);
}
}
//生成验证码图片类
publicclass SecurityImage {
/**
*生成验证码图片
*@paramsecurityCode 验证码字符
*@return BufferedImage 图片
*/
publicstatic BufferedImagecreateImage(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=newBufferedImage(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;
}
/**
*返回验证码图片的流格式
*@paramsecurityCode 验证码
*@returnByteArrayInputStream图片流
*/
publicstaticvoid getImageAsInputStream(HttpServletResponseresponse,String securityCode){
try {
//获取图片
BufferedImage image = createImage(securityCode);
//禁止图片缓存
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
ServletOutputStreamstream=response.getOutputStream();
//将图像输出到Servlet输出流中
ImageIO.write(image, "JPG", stream);
stream.close();
} catch (IOException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
//Action调用方法
// 获取验证码图片
public String getSecurityCodeImage() {
// 获取默认难度和长度的验证码
String securityCode = SecurityCode.getSecurityCode(getSession());
// 写出图片到页面
SecurityImage.getImageAsInputStream(getResponse(), securityCode);
returnnull;
}
//因为返回一个图片流所以return null 不用返回视图前端是img控件的src调用 此处需特别注意方法内不能使用session保存数据不然会抛出异常java.lang.IllegalStateException
该异常表示,当前对客户端的响应已经结束,不能在响应已经结束(或说消亡)后再向客户端(实际上是缓冲区)输出任何内容。
response是服务端对客户端请求的一个响应,其中封装了响应头、状态码、内容等服务端在把response提交到客户端之前,会向缓冲区内写入响应头和状态码,然后将所有内容flush。这就标志着该次响应已经committed(提交)。对于当前页面中已经committed(提交)的response,就不能再使用这个response向缓冲区写任何东西(注:同一个页面中的response.XXX()是同一个response的不同方法,只要其中一个已经导致了committed,那么其它类似方式的调用都会导致 IllegalStateException异常)。
我这里的具体原因大概是由于我用session提交了保存了数据后再用response输出图片流到页面。其他情况出现这个异常 大致按照这个思路解决就好
页面调用代码:
<imgid="imgObj" alt="换一张" src="test!getSecurityCodeImage.action"οnclick="changeImg()"></img>(struts2的调用方式)
Js代码:
function changeImg() {
var imgSrc =document.getElementById("imgObj");
imgSrc.src=imgSrc.src+"?temp="+Math.random()
}
//比较懒 没花多大心思写这个js 大概意思是在src最后+?+无用参数=随机值
表示src变了能实现点击变换验证码(我这个是一直加下去?后有N个?号)问题不大
类似test!getSecurityCodeImage.action?temp=0.51564654654?temp=0.5156462352353