springmvc使用谷歌captcha生成图片验证码,并将验证码图片以二进制流的方式返回给前端(app和pc端都能调用)

本文详细介绍了一种基于谷歌验证码的接口设计与实现方案,包括使用kaptcha库生成验证码图片,通过Base64编码返回前端,并对每日访问次数进行限制,确保安全性。同时,提供了web端和app端调用接口的具体代码示例。

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

        近期对登录注册与获取短信验证码的接口做了安全限制,其中一部分就用到了谷歌的captcha验证码,比如当用户连续三次登陆失败,那么之后的登录请求就需要用户输入谷歌的图形验证码。由于web端和app端调用的都是同一个获取谷歌验证码的接口,所以后端这个生成图片验证码的接口就与传统的直接将验证码图片写入到前端的方式不同,此时接口返回的应该这个经过base64编码后的验证码图片二进制流,然后由前端自行解析(web和app解析这个图片流的方式有些不一样)显示出来。主要代码可以借鉴这篇https://blog.51cto.com/4925054/2103196,下面分享我借鉴完后,自己做的一些修改。

 

首先是依赖的jar:

		<!-- 谷歌验证码 -->
		<dependency>
			<groupId>com.github.penggle</groupId>
			<artifactId>kaptcha</artifactId>
			<version>2.3.2</version>
		</dependency>

 

然后将生成验证码的com.google.code.kaptcha.impl.DefaultKaptcha类交给spring进行管理,在spring-mvc.xml文件中的配置是:

	<!-- 谷歌验证码 -->
	<bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
        <property name="config">
            <bean class="com.google.code.kaptcha.util.Config">
                <constructor-arg>
                    <props>
                        <prop key="kaptcha.border">yes</prop>
                        <prop key="kaptcha.border.color">105,179,90</prop>
                        <prop key="kaptcha.textproducer.font.color">blue</prop>
                        <prop key="kaptcha.image.width">200</prop>
                        <prop key="kaptcha.image.height">80</prop>
                        <prop key="kaptcha.textproducer.font.size">70</prop>
                        <prop key="kaptcha.session.key">code</prop>
                        <prop key="kaptcha.textproducer.char.length">4</prop>
                        <prop key="kaptcha.textproducer.font.names">宋体,楷体,微软雅黑</prop>
                    </props>
                </constructor-arg>
            </bean>
        </property>
    </bean>

具体的参数配置可以参考这篇博客:https://714501466.iteye.com/blog/1308756 ,我也是直接拿人家的来用的!!!

 

接下来就是大家最喜欢的controller层的接口了:

package demo;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.google.code.kaptcha.Producer;

import sun.misc.BASE64Encoder;
import sy.util.redis.RedisUtils;


/**
 * @author hqq
 * 
 */

@Controller
@RequestMapping("/demo")
public class RegAndLoginController {

	private static final Logger logger = Logger.getLogger(RegAndLoginController.class);

	@Autowired
	private Producer captchaProducer;

	/**
	 * 获取谷歌验证码
	 * 
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping(value = "/captcha")
	@ResponseBody
	public Result getKaptchaImage(@RequestParam("mobile") String mobile, @RequestParam("type") String type,
			HttpServletRequest request) {

		Result result = new Result();
		if (StringUtils.isBlank(mobile) || StringUtils.isBlank(type)) {
			result.setSuccess(false);
			result.setMsg("非法请求,不予处理");
			return result;
		}

		//由于该接口是没有做权限校验的,任何人不登录都可以随意调用,所以此处建议做个每日访问次数的限制
		String ip = IpUtil.getIpAddr(request);
		String visitKey = ip +"_captcha_"+mobile;
		String visitCount = RedisUtils.get(visitKey);
		int count = 1;
		if (visitCount != null) {
			count = new Integer(visitCount);
			if (count >= 100) {
				result.setSuccess(true);
				result.setMsg("您今日调用此接口次数已达上限");
				return result;
			}
			count++;
		}
		RedisUtils.set(visitKey, count+"", DateUtils.getSurplusTime());
    
        if(!",login,register,".contains(","+type+",")){
            result.setSuccess(false);
			result.setMsg("非法请求,传入的参数错误");
			return result;       
        }


		// 生成验证码
		String capText = captchaProducer.createText();
        
        String key=type+"_captcha_"+mobile;

		//当前验证码的时间可以设置的稍长些,防止用户在登录界面停留太长时间导致验证码失效了
		RedisUtils.set(key, capText.toLowerCase(), 60 * 30);//半个小时

		// 向客户端写出
		try {
			BufferedImage bi = captchaProducer.createImage(capText);
			// 生成图片验证码
			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

			ImageIO.write(bi, "jpg", outputStream);

            //高能预警!!!

			// 对字节数组Base64编码
			BASE64Encoder encoder = new BASE64Encoder();
			String img = encoder.encode(outputStream.toByteArray());

			result.setSuccess(true);
			result.setMsg("验证码生成成功");
			result.setImg(img);//返回图片的路径

		} catch (Exception e) {
			result.setSuccess(false);
			result.setMsg("系统异常:" + e.getMessage());
		}

		return result;
	}

}

 

RedisUtils.java类请参照我的另一篇博客:https://blog.youkuaiyun.com/weixin_42023666/article/details/89287418

 

工具类IpUtil.java如下:

package util;

import java.net.InetAddress;

import javax.servlet.http.HttpServletRequest;


public class IpUtil {



	/**
	 * 获取请求的客户端的ip地址
	 * 
	 * @param request
	 * @return
	 */
	public static String getIpAddr(HttpServletRequest request) {
		String ipAddress = request.getHeader("x-forwarded-for");
		if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
			ipAddress = request.getHeader("Proxy-Client-IP");
		}
		if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
			ipAddress = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
			ipAddress = request.getRemoteAddr();
			if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
				// 根据网卡取本机配置的IP
				InetAddress inet = null;
				try {
					inet = InetAddress.getLocalHost();
					ipAddress = inet.getHostAddress();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		
		// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
		if (ipAddress != null && ipAddress.length() > 15 && ipAddress.indexOf(",") > 0 ) { // "***.***.***.***".length()
			ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
		}
		
		return ipAddress;
	}

}

 

web前端的js代码如下:

 //获取图形验证码
	 function getCodeImg(num,mobile) {
		$.ajax({
			type:"post",
			url:root+"/demo/captcha.action",
			async : false,
			data:{
				id:id,
				type:1
			},error:function(err){
				
			},success:function(r){
				if(r.success){
					$("#codeImg").attr('src','data:image/jpeg;base64,'+r.img+'');
				}else{
					pophint(r.msg)
				}
			}
		})
	 }

 

效果如下:

 

app端的调用也是发ajax请求,具体代码我没有,反正是成功显示了,另外附上一篇博客,ios开发可以试试从这里找找:https://blog.youkuaiyun.com/sjl_leaf/article/details/48179299

为了防止该博文失效,截取一部分核心内容:

 

这是他的核心代码,安全起见,此处备份一下:

//参数在这里 + (void) setImageView:(UIImageView *)imageView WithString:(NSString *)avatar
//二进制显示
        /*服务器返回:例如
         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAABnRSTlMAAAAAAABupgeRAAAAhElEQVRYhe3Y0QmAIBhF4YwGaZRGaNRGaJRGaYEyTj/RfTjnVYQPBRXbuh9DXuPfgOtkkWSRZJFkkUJZU3H+tsx3Q5VrLXS1ZJFkkWSRZJFkkWSRZJFkkUJZLfPH5vktX3mtv54buomySLJIskihrNBTvvWHP0V37oDQTZRFkkWSRQplnQDfEjVVBe3tAAAAAElFTkSuQmCC
         */
        NSArray *imageArray = [avatar componentsSeparatedByString:@","];
        NSData *imageData = [[NSData alloc] initWithBase64EncodedString:imageArray[1] options:NSDataBase64DecodingIgnoreUnknownCharacters];
        imageView.image = [UIImage imageWithData:imageData];

 

至此,分享结束,希望能帮到各位!验证码的话,话说腾讯出的防水墙功能好像很牛的样子,大家感受一下:https://007.qq.com/online.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值