一、前言
原来对于单应用将生成的图形验证码存在Session会话中,但是在分布式环境在,当请求切换的cluster的备机,备机上没有UUID关联的验证码数据。因此,在分布式环境中,需要将UUID的验证码存储在Redis中,提交的时候统一根据UUID从内存中加载并验证,另外生成的图形验证码通过Base64.encodeBase64String转码后和UUID一起以JSON数据格式返回前端,这样生成的base64的验证码的数据可以直接贴在img标签的src属性中渲染显示。
二、代码示例
1.VerificationCodeController中通过接口2个接口分别获取返回图形的UUID及图形的Base64加密码接口、通过Img标签的src属性赋值图形加密串渲染出图形
import java.awt.Color;@b@import java.awt.Font;@b@import java.awt.Graphics;@b@import java.awt.image.BufferedImage;@b@import java.io.ByteArrayOutputStream;@b@import java.io.IOException;@b@import java.io.PrintWriter;@b@import java.util.Random;@b@import java.util.UUID;@b@@b@import javax.imageio.ImageIO;@b@import javax.servlet.ServletException;@b@import javax.servlet.http.HttpServletRequest;@b@import javax.servlet.http.HttpServletResponse;@b@@b@import org.apache.commons.codec.binary.Base64;@b@import org.springframework.stereotype.Controller;@b@import org.springframework.ui.ModelMap;@b@import org.springframework.web.bind.annotation.RequestMapping;@b@import org.springframework.web.bind.annotation.RequestMethod;@b@import org.springframework.web.bind.annotation.ResponseBody;@b@@b@@b@/**@b@ * @描述:一次性校验码@b@ * @author@b@ */@b@@Controller@b@public class VerificationCodeController{@b@ @b@ // 渲染随机背景颜色@b@ private Color getRandColor(int fc,int bc){@b@ Random random = new Random();@b@ if(fc>255) fc=255;@b@ if(bc>255) bc=255;@b@ int r=fc+random.nextInt(bc-fc);@b@ int g=fc+random.nextInt(bc-fc);@b@ int b=fc+random.nextInt(bc-fc);@b@ return new Color(r,g,b);@b@ }@b@ @b@ //渲染固定背景颜色@b@ private Color getBgColor(){@b@ return new Color(200,200,200);@b@ }@b@ @b@ @b@ private BufferedImage drawImg(){@b@ return drawImg(false);@b@ }@b@ @b@ /**@b@ * 画验证码图形@b@ * @param isDrawLine@b@ * @return@b@ */@b@ private BufferedImage drawImg(boolean isDrawLine){@b@ // 在内存中创建图象@b@ int width=120, height=20;@b@ BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);@b@ // 获取图形上下文@b@ Graphics g = image.getGraphics();@b@ Random random = new Random();@b@ // 设定背景色@b@ g.setColor(getBgColor());@b@ g.fillRect(0, 0, width, height);@b@ // 设定字体@b@ g.setFont(new Font("Times New Roman",Font.PLAIN,18));@b@ // 画边框@b@ //g.setColor(new Color());@b@ //g.drawRect(0,0,width-1,height-1);@b@@b@ /**随机产生155条干扰线,使图象中的认证码不易被其它程序探测到*/@b@ @b@ if(isDrawLine){@b@ g.setColor(getRandColor(160,200));@b@ for (int i=0;i<155;i++){@b@ int x = random.nextInt(width);@b@ int y = random.nextInt(height);@b@ int xl = random.nextInt(12);@b@ int yl = random.nextInt(12);@b@ g.drawLine(x,y,x+xl,y+yl);@b@ }@b@ }@b@ @b@ // 取随机产生的认证码(8位数字和字母混合)@b@ String sRand="";@b@ String verCode="";@b@ char[] seds = new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9'};@b@ for (int i=0;i<8;i++){@b@ int index = random.nextInt(seds.length);@b@ char cc = seds[index];@b@ if((i+1)%2==0){@b@ verCode+=cc;@b@ g.setColor(new Color(255,0,0));@b@ }else{@b@ g.setColor(new Color(0,0,0));@b@ }@b@ sRand+=cc;@b@ // 将认证码随机打印不同的颜色显示出来@b@ //g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));// 调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成@b@ g.drawString(cc+"",13*i+6,16);@b@ }@b@ // 图象生效@b@ g.dispose();@b@ @b@ return image;@b@ }@b@ @b@ private String encodeBase64ImgCode()throws ServletException, IOException {@b@ BufferedImage codeImg=drawImg();@b@ /*// 将认证码存入SESSION@b@ request.getSession().setAttribute("rand",sRand);*/@b@ // 将认证码存入redis@b@ // RedisUtil.saveValue(redis, uuid.getBytes(),verCode.getBytes(), 60 * 5 * 1);@b@ ByteArrayOutputStream out = new ByteArrayOutputStream();@b@ boolean flag = ImageIO.write(codeImg, "JPEG", out);@b@ byte[] b = out.toByteArray();@b@ String imgString = Base64.encodeBase64String(b);@b@ return "data:image/JPEG;base64," + imgString;@b@ }@b@ @b@ @b@ /**@b@ * @描述:生成校验码@b@ * @return@b@ * @param form@b@ * @throws IOException @b@ */@b@ @RequestMapping(value="/generateCode.do",method={RequestMethod.POST,RequestMethod.GET})@b@ public @ResponseBody ModelMap generateCode(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {@b@ ModelMap model = new ModelMap();@b@ @b@ //生成验证码uuid@b@ model.put("id", UUID.randomUUID().toString());@b@ @b@ // 设置页面不缓存@b@ response.setHeader("Pragma","No-cache");@b@ response.setHeader("Cache-Control","no-cache");@b@ response.setDateHeader("Expires", 0);@b@ @b@ model.put("img",encodeBase64ImgCode());//获取通过base64加密后图形码字符串@b@ return model;@b@ }@b@ @b@ @b@ @RequestMapping("/hello")@b@ public @ResponseBody String test() {@b@ return "hello, world! This com from spring!";@b@ }@b@ @b@ @RequestMapping(value="/springmvc/getOutImgCode")@b@ public void getOutImgCode(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {@b@ PrintWriter out = response.getWriter();@b@ response.setContentType("text/html;charset=utf-8"); @b@ out.println("<img src=\""+encodeBase64ImgCode()+"\">");@b@ out.close();@b@ }@b@ @b@ @b@ @b@}
2.通过/generateCode.do接口返回JSON的UUID和图形码的加密串,如下图
{@b@ "id" : "ebf73be9-52c4-45b3-ab2f-c7e80a1ad815",@b@ "img" : "data:image/JPEG;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQND ....... 6ipqUUAFFFFABRRRQAUUUUAFFFFAH/2Q=="@b@}
通过img标签将就可以将base64的加密字符串渲染为图形码图片,如下图所示