struts2验证码及错误解决

在struts2中生成的验证码用ByteArrayInputStream输出流输出。具体实现如下:

   为了能够灵活控制验证码,特别编写了SecurityCode类,它向外提供随机字符串。并且可以控制字符串的长度和难度。SecurityCode类中提供的验证码分三个难度,易(全数字)、中(数字+小写英文)、难(数字+大小写英文)。难度使用枚举SecurityCodeLevle表示,避免使用1、2、3这样没有明确意义的数字来区分。同时,还控制了能否出现重复的字符。为了能够方便使用,方法设计为static。

SecurityCode类:

packagecn.barathrum.system.util;

importjava.util.Arrays;

public classSecurityCode {

    public enum SecurityCodeLevel {

       Simple, Medium, Hard

    };

    public static String getSecurityCode() {

       return getSecurityCode(4, SecurityCodeLevel.Hard,false);

    }

    public static 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);

       } else if (level == SecurityCodeLevel.Medium){

           codes = Arrays.copyOfRange(codes, 0,33);

       }

       // 字符集合长度

       int n = codes.length;

       // 抛出运行时异常

       if (len > n && isCanRepeat ==false) {

          throw newRuntimeException(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);

    }

}

 

在Java中操作图片,需要使用BufferedImage类,它代表内存中的图片。写字符串,就需要从图片BufferedImage上得到绘图图面Graphics,然后在图面上drawString。

  SecurityImage类:

package cn.barathrum.system.util;

 

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;

importcom.sun.image.codec.jpeg.ImageFormatException;

importcom.sun.image.codec.jpeg.JPEGCodec;

importcom.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 = 34;

 

      // 图片

      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 - 13;

      // 设置字体颜色和样式

      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(StringsecurityCode) {

      BufferedImage image = createImage(securityCode);

      return convertImageToStream(image);

   }

   Private static ByteArrayInputStream convertImageToStream(BufferedImageimage) {

 

      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;

   }

}

 

1)Action

  有了上面两步操作作为铺垫,含有验证码的图片就不成问题了,下面就可以使用Struts2的Action向前台返回图片数据了。

     SecurityCodeImageAction类:

package cn.barathrum.system.action;

 

import java.io.ByteArrayInputStream;

importcn.barathrum.system.util.SecurityCode;

importcn.barathrum.system.util.SecurityImage;

 

public class ValidCodeAction extends SupportAction{

  

   private static final long serialVersionUID = 5633780176793520460L;

      //图片流

   private ByteArrayInputStream imageStream;

 

      publicstatic long getSerialversionuid() {

           returnserialVersionUID;

      }

     

      @Override

   public String execute() throws Exception {

           response.setContentType("image/jpeg"); 

       response.setHeader("Cache-Control","no-cache"); 

       response.setHeader("Pragma", "No-cache"); 

       response.setDateHeader("Expires", 0L); 

           System.out.println("进入验证码");

      // TODO Auto-generated method stub

      try {

            if(session.getAttribute("securityCode")!=null){

                 String s1 = (String)session.getAttribute("securityCode");

                 System.out.println(s1);

            }

            

            String securityCode =SecurityCode.getSecurityCode();

           session.setAttribute("securityCode", securityCode);

           System.out.println("securityCode = " + securityCode);

            imageStream =SecurityImage.getImageAsInputStream(securityCode);

            String s = (String)session.getAttribute("securityCode");

            System.out.println(s);

      } 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"。

  同时<param name="inputName">这一项的值,应该与SecurityCodeImageAction中的图片流名称一致。

    Struts.xml:

<actionname="ValidCodeAction"class="cn.barathrum.system.action.ValidCodeAction">

            <result name="success" type="stream">

              <paramname="contentType">image/jpeg</param>

              <paramname="header">no-cache</param>

              <paramname="inputName">imageStream</param>

              <paramname="bufferSize">2048</param>

          </result>

</action>

 

Jsp页面显示:

 

Jquery:

 

出现的问题及解决方法:

虽然上述的验证码完成了,打开登陆界面确实出现了验证码,但是执行登录时,验证码对不上。经过单元测试后,发现第一次打开登陆界面时,ValidCodeAction没有触发,登录界面的验证码图片是浏览器之前缓存的。此时session所设置ValidCode为null,所以你也完成不了验证。那么,有人说再次刷新页面触发action就行了。这样子做确实触发了action。但是session保存的值是上一个验证码的值,

之所谓会这样是因为页面的刷新与图片的刷新不同步。

解决方法:

1.首先先创建一个临时jsp文件,用与触发验证码action。

2.web.xml修改welcome-file-list

触发后记得跳转回login.jsp.这样子验证码action就随着login.jsp的显示之前就触发了,session保存了对应的验证码字符串。

但是,你如果在jsp页面通过 ${sessionScope.验证码}获取验证码时为null.这意味着你不能在前台进行验证码校验。只能到后台进行验证码进行校验。神奇的是,到后台通过session.getAttribute(“验证码”)之后就可以获取验证了。这样就可以进行验证码校验了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值