开发web应用时firefox获取小的静态图片时重复提交请求的bug

在开发web应用过程中,遇到Firefox浏览器在获取小尺寸静态验证码图片时会重复提交请求的问题。通过调整图片大小,发现当图片尺寸增大时,问题消失。这表明问题可能与Firefox对小图片的处理方式有关。文章探讨了这个问题的本质原因,并提供了可能的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近项目需要图形验证码,于是乎着手写了一个,代码如下:
/**
省略import
*/
@Controller
public class CaptchaController {
	private static final Logger logger = Logger
			.getLogger(CaptchaController.class);
	private static final String appCaptchaCookieKey = "APPCAPTCHACOOKIE";
	@Autowired
	private Producer captchaProducer;
	
	@Autowired
	private RedisComponent redisComponent;
	@Value("${CAPTCHA_EXPIRE_TIME}")
	private int captchaExpireTime;
	/**
	 * @param request
	 * @param response
	 * @throws IOException 
	 */
	@RequestMapping(value = "/captcha.jpg", method = RequestMethod.GET)
	public void getCaptcha(HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		response.setDateHeader("Expires", 0);
		response.setHeader("Cache-Control",
				"no-store, no-cache, must-revalidate");
		response.addHeader("Cache-Control", "post-check=0, pre-check=0");
		response.setHeader("Pragma", "no-cache");
		response.setContentType("image/jpeg");
		ServletOutputStream out = null;
		try {
			String uniqCode = UUID.randomUUID().toString().replaceAll("-", "");
			logger.info("生成的图形验证码唯一键:"+uniqCode);
			String capText = captchaProducer.createText();
			// 存储验证码文字到redis
			logger.info("生成的验证码文字:"+capText);
			redisComponent.set(ITMIConstants.CAPTCHA_PREFIX+uniqCode, capText.toLowerCase(), captchaExpireTime, TimeUnit.SECONDS);
			logger.info("成功存入redis的验证码:"+redisComponent.get(ITMIConstants.CAPTCHA_PREFIX+uniqCode));
			//将uniqCode添加到用户cookie
			addCookie(response, appCaptchaCookieKey, uniqCode);
			//生成验证码图片并返回给页面
			BufferedImage bi = captchaProducer.createImage(capText);
			out = response.getOutputStream();
			ImageIO.write(bi, "jpg", out);
			out.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != out) {
				out.close();
			}
		}
	}
	/**
	 * 校验验证码
	 * @param request
	 * @param response
	 * @throws Exception 
	 */
	@RequestMapping(value = "/checkCaptcha.shtml", method = RequestMethod.POST)
	public void checkCaptcha(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		response.setDateHeader("Expires", 0);
		response.setHeader("Cache-Control",
				"no-store, no-cache, must-revalidate");
		response.addHeader("Cache-Control", "post-check=0, pre-check=0");
		response.setHeader("Pragma", "no-cache");
		response.setContentType("application/json");
		ResDto res = new ResDto();
		String captcha = request.getParameter("captcha");
		logger.info("入参-----验证码:"+captcha);
		if (captcha == null || captcha.equals("")) {
			res.setMsg("请求缺少必要的参数");
			res.setSuccess(false);
		}else{
			String cookieUniqCode = getCookie(request,appCaptchaCookieKey);
			logger.info("cookie中获取到的图形验证码唯一键:"+cookieUniqCode);
			String redisCode = redisComponent.get(ITMIConstants.CAPTCHA_PREFIX+cookieUniqCode);
			logger.info("redis中取到的验证码:"+redisCode);
			if(redisCode == null || redisCode.equals("")){
				res.setMsg("验证码已过期,请重新获取验证码");
				res.setSuccess(false);
			}else{
				if(redisCode.equals(captcha.toLowerCase())){
					res.setMsg("成功");
					res.setSuccess(true);
				}else{
					res.setMsg("验证码错误");
					res.setSuccess(false);
				}
			}
		}
		String resStr = JSON.toJSONString(res);
		logger.info("返回结果:"+resStr);
		try {
			 PrintWriter out = response.getWriter(); 
			 out.write(resStr);
		} catch (Exception e) {
			logger.error("检查验证码接口调用失败"+e.getMessage());
			e.printStackTrace();
		}
	}
	/**
	 * 添加cookie 浏览器关闭即失效
	 * @param response
	 * @param name
	 * @param value
	 */
	public static void addCookie(HttpServletResponse response, String name,String value){
		Cookie cookie = new Cookie(name, value);
		cookie.setMaxAge(-1);
		cookie.setPath("/");
		response.addCookie(cookie);
	}
	/**
	 * 根据name获取cookie值
	 * 
	 * @param request
	 * @param name
	 * @return
	 */
	public static String getCookie(HttpServletRequest request, String name) {
		Map<String, Cookie> cookieMap = getCookieMap(request);
		Cookie cookie = cookieMap.get(name);
		if (null != cookie) {
			return cookie.getValue();
		}
		return null;
	}
	/**
	 * 根据请求request获取所有cookie
	 * @param request
	 * @return
	 */
	public static Map<String, Cookie> getCookieMap(HttpServletRequest request) {
		Map<String, Cookie> cookieMap = new HashMap<String, Cookie>();
		Cookie[] cookies = request.getCookies();
		if (null != cookies) {
			for (int i = 0; i < cookies.length; i++) {
				cookieMap.put(cookies[i].getName(), cookies[i]);
			}
		}
		return cookieMap;
	}
	/**
	 * 检查验证码返回实体
	 * @author 201510260176
	 */
	class ResDto{
		private boolean success;
		
		public boolean isSuccess() {
			return success;
		}
		public void setSuccess(boolean success) {
			this.success = success;
		}
		private String msg;
		public String getMsg() {
			return msg;
		}
		public void setMsg(String msg) {
			this.msg = msg;
		}
		
	}
}

这个借助了开源的kaptcha来生成验证码图片,好处是可以在xml进行配置生成的验证码图片外观,我的配置如下:

<bean name="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
		<property name="config">
			<bean class="com.google.code.kaptcha.util.Config">
				<constructor-arg type="java.util.Properties">
					<props>
						<prop key="kaptcha.textproducer.char.length">4</prop>
						<prop key="kaptcha.textproducer.char.string">ABCD123456789EFGHIjkmnpqrstuvwxyz</prop>
						<prop key="kaptcha.textproducer.font.size">36</prop>
						<prop key="kaptcha.textproducer.char.space">16</prop>
						<prop key="kaptcha.image.width">220</prop>
                                                <prop key="kaptcha.noise.color">blue</prop>
						
						<prop key="kaptcha.textproducer.font.names">宋体,微软雅黑</prop>
					</props>
				</constructor-arg>
			</bean>
		</property>
	</bean>

需要引入如下jar包:

<!-- kaptcha -->
		<dependency>
			<groupId>com.github.axet</groupId>
			<artifactId>kaptcha</artifactId>
			<version>0.0.9</version>
		</dependency>

完事之后一切都ok,就是出了个很诡异的问题,只要我在firox里面输入获取图像验证码的url,在我的controller里面就会执行两次,在chrome和ie以及其他浏览器里面都没有这个问题,后来查了半天才知道这算是firfox的一个Bug,当从服务器获取静态小的图片时,就会有这个问题,必须满足两个条件:

(1)获取静态图片(服务器返回静态图片)

(2)小的图片(到底多小?自己上网查)

我测试了一下,给配置的验证码图片大小改大了这个问题就没了,所以网上的说法还是很靠谱的,下面就来看看本质的原因是啥。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值