springboot图片验证码功能模块怎么实现
springboot图片验证码功能模块怎么实现
本篇内容主要讲解“springboot图片验证码功能模块怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“springboot图片验证码功能模块怎么实现”吧!
具体效果如下:
第一步:工具类
该工具类为生成验证码图片的核心,直接拷贝到项目即可,无需做修改;可个性化的参数全部对外提供的API,比如 字体大小
,背景颜色
,干扰线数量
,高宽
等都可以根据自己的需求设置对应参数;
代码几乎每一行都加了详细的注释;如果遇上特殊的个性化需求,调整一下这个工具类即可实现。
packagecom.feng.util;/***@returnnull*@authorLadidol*@description*@date2022/4/1122:15*/importjava.awt.*;importjava.awt.geom.AffineTransform;importjava.awt.image.BufferedImage;importjava.util.Random;/***图形验证码生成*/publicclassVerifyUtil{//默认验证码字符集privatestaticfinalchar[]chars={'0','1','2','3','4','5','6','7','8','9','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','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'};//默认字符数量privatefinalIntegerSIZE;//默认干扰线数量privatefinalintLINES;//默认宽度privatefinalintWIDTH;//默认高度privatefinalintHEIGHT;//默认字体大小privatefinalintFONT_SIZE;//默认字体倾斜privatefinalbooleanTILT;privatefinalColorBACKGROUND_COLOR;/***初始化基础参数**@parambuilder*/privateVerifyUtil(Builderbuilder){SIZE=builder.size;LINES=builder.lines;WIDTH=builder.width;HEIGHT=builder.height;FONT_SIZE=builder.fontSize;TILT=builder.tilt;BACKGROUND_COLOR=builder.backgroundColor;}/***实例化构造器对象**@return*/publicstaticBuildernewBuilder(){returnnewBuilder();}/***@return生成随机验证码及图片*Object[0]:验证码字符串;*Object[1]:验证码图片。*/publicObject[]createImage(){StringBuffersb=newStringBuffer();//创建空白图片BufferedImageimage=newBufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);//获取图片画笔Graphics2Dgraphic=image.createGraphics();//设置抗锯齿graphic.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);//设置画笔颜色graphic.setColor(BACKGROUND_COLOR);//绘制矩形背景graphic.fillRect(0,0,WIDTH,HEIGHT);//画随机字符Randomran=newRandom();//graphic.setBackground(Color.WHITE);//计算每个字符占的宽度,这里预留一个字符的位置用于左右边距intcodeWidth=WIDTH/(SIZE+1);//字符所处的y轴的坐标inty=HEIGHT*3/4;for(inti=0;i<SIZE;i++){//设置随机颜色graphic.setColor(getRandomColor());//初始化字体Fontfont=newFont(null,Font.BOLD+Font.ITALIC,FONT_SIZE);if(TILT){//随机一个倾斜的角度-45到45度之间inttheta=ran.nextInt(45);//随机一个倾斜方向左或者右theta=(ran.nextBoolean()==true)?theta:-theta;AffineTransformaffineTransform=newAffineTransform();affineTransform.rotate(Math.toRadians(theta),0,0);font=font.deriveFont(affineTransform);}//设置字体大小graphic.setFont(font);//计算当前字符绘制的X轴坐标intx=(i*codeWidth)+(codeWidth/2);//取随机字符索引intn=ran.nextInt(chars.length);//得到字符文本Stringcode=String.valueOf(chars[n]);//画字符graphic.drawString(code,x,y);//记录字符sb.append(code);}//画干扰线for(inti=0;i<LINES;i++){//设置随机颜色graphic.setColor(getRandomColor());//随机画线graphic.drawLine(ran.nextInt(WIDTH),ran.nextInt(HEIGHT),ran.nextInt(WIDTH),ran.nextInt(HEIGHT));}//返回验证码和图片returnnewObject[]{sb.toString(),image};}/***随机取色*/privateColorgetRandomColor(){Randomran=newRandom();Colorcolor=newColor(ran.nextInt(256),ran.nextInt(256),ran.nextInt(256));returncolor;}/***构造器对象*/publicstaticclassBuilder{//默认字符数量privateintsize=4;//默认干扰线数量privateintlines=10;//默认宽度privateintwidth=80;//默认高度privateintheight=35;//默认字体大小privateintfontSize=25;//默认字体倾斜privatebooleantilt=true;//背景颜色privateColorbackgroundColor=Color.LIGHT_GRAY;publicBuildersetSize(intsize){this.size=size;returnthis;}publicBuildersetLines(intlines){this.lines=lines;returnthis;}publicBuildersetWidth(intwidth){this.width=width;returnthis;}publicBuildersetHeight(intheight){this.height=height;returnthis;}publicBuildersetFontSize(intfontSize){this.fontSize=fontSize;returnthis;}publicBuildersetTilt(booleantilt){this.tilt=tilt;returnthis;}publicBuildersetBackgroundColor(ColorbackgroundColor){this.backgroundColor=backgroundColor;returnthis;}publicVerifyUtilbuild(){returnnewVerifyUtil(this);}}}
第二步:图片生成:
使用默认参数:
//生成图片验证码Object[]verify=VerifyUtil.newBuilder().build().createImage();
自定义参数生成:
//这个根据自己的需要设置对应的参数来实现个性化//返回的数组第一个参数是生成的验证码,第二个参数是生成的图片Object[]objs=VerifyUtil.newBuilder().setWidth(120)//设置图片的宽度.setHeight(35)//设置图片的高度.setSize(6)//设置字符的个数.setLines(10)//设置干扰线的条数.setFontSize(25)//设置字体的大小.setTilt(true)//设置是否需要倾斜.setBackgroundColor(Color.WHITE)//设置验证码的背景颜色.build()//构建VerifyUtil项目.createImage();//生成图片
整合到springboot项目中:
需要引入的maven依赖:
<!--redis相关配置--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--redis连接池--><!--新版本连接池lettuce--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!--图形验证码--><dependency><groupId>net.jodah</groupId><artifactId>expiringmap</artifactId><version>0.5.10</version></dependency>
获取相关的验证码:
service层:
packagecom.feng.service;importorg.cuit.epoch.result.Result;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;/***@returnnull*@authorLadidol*@description*@date2022/4/1122:15*/publicinterfaceVerifyService{/***创建图片验证码*@paramresponse*@paramrequest*@throwsIOException*/voidcreateCode(HttpServletResponseresponse,HttpServletRequestrequest)throwsIOException;/***检查图片验证码*@param*@param*@throwsIOException*/Result<String>checkCode(StringverificationCode);}
serviceimpl层:
packagecom.feng.service.impl;importcom.feng.service.VerifyService;importcom.feng.util.RedisServiceImpl;importcom.google.common.net.HttpHeaders;importcom.feng.util.VerifyUtil;importorg.springframework.http.ResponseCookie;importorg.springframework.stereotype.Service;importjavax.annotation.Resource;importjavax.imageio.ImageIO;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.servlet.http.HttpSession;importjava.awt.image.BufferedImage;importjava.io.IOException;importjava.io.OutputStream;importjava.time.Duration;/***@returnnull*@authorLadidol*@description*@date2022/4/1122:15*/@ServicepublicclassVerifyServiceImplimplementsVerifyService{@ResourceRedisServiceImplredisUtil;/***生成图片验证码*@paramresponse*@paramrequest*@throwsIOException*/@OverridepublicvoidcreateCode(HttpServletResponseresponse,HttpServletRequestrequest)throwsIOException{//获取sessionHttpSessionsession=request.getSession();//获得sessionIdStringid=session.getId();System.out.println();ResponseCookiecookie=ResponseCookie.from("JSESSIONID",id).secure(true).domain("").path("/").maxAge(Duration.ofHours(1)).sameSite("None").build();//清除之前缓存的图片验证码if(!String.valueOf(request.getSession().getAttribute("SESSION_VERIFY_CODE_"+id)).isEmpty()){StringgetVerify=String.valueOf(request.getSession().getAttribute("SESSION_VERIFY_CODE_"+id));redisUtil.del(getVerify);System.out.println("清除成功");}//生成图片验证码,用的默认参数Object[]verify=VerifyUtil.newBuilder().build().createImage();//将验证码存入sessionsession.setAttribute("SESSION_VERIFY_CODE_"+id,verify[0]);//打印验证码System.out.println(verify[0]);//将验证码存入redisredisUtil.set((String)verify[0],id,5*60);//将图片传给浏览器BufferedImageimage=(BufferedImage)verify[1];response.setContentType("image/png");response.setHeader(HttpHeaders.SET_COOKIE,cookie.toString());OutputStreamops=response.getOutputStream();ImageIO.write(image,"png",ops);}@OverridepublicResult<String>checkCode(StringverificationCode){if(!redisUtil.hasKey(verificationCode)){returnnewResult<>(false,"验证码错误");}redisUtil.del(verificationCode);returnR.success();}}
这里面还会用到redis相关的工具类,我就不列出来了,想要的话可以看我以前的博客工具类戳这里
controller层:
这里有用到@RequiredArgsConstructor, 就是简单的注入而已, 如果想要详细了解戳这里
packagecom.feng.controller;importlombok.RequiredArgsConstructor;importcom.feng.annotation.LimitRequest;importcom.feng.service.VerifyService;importorg.springframework.web.bind.annotation.*;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;/***@returnnull*@authorLadidol*@description这里主要就是多种验证码和登录相关的东西*@date2022/4/1121:46*/@RestController@RequestMapping("/verify")@RequiredArgsConstructor//这是在lombok工具给的注入方式,真帅publicclassVerifyController{privatefinalVerifyServiceverifyService;/***获取图片验证码*/@LimitRequest(count=5)//这个注解就是表示,你在限制时间里(我们这里默认是六秒钟),只能请求五次@GetMapping("/getCode")publicvoidgetCode(HttpServletResponseresponse,HttpServletRequestrequest)throwsIOException{verifyService.createCode(response,request);}@LimitRequest(count=5)//这个注解就是表示,你在限制时间里(我们这里默认是六秒钟),只能请求五次@GetMapping("/checkCode")publicResult<String>checkCode(Stringcode){returnverifyService.checkCode(code);}}
这里为了不被一直无限制的访问该服务, 我们用了一个限制ip访问次数的注解@LimitRequest
annotion包下的注解类:
packagecom.feng.annotation;importjava.lang.annotation.*;/***@returnnull*@authorLadidol*@description限制ip访问次数注解*@date2022/4/1122:15*/@Documented@Target(ElementType.METHOD)//说明该注解只能放在方法上面@Retention(RetentionPolicy.RUNTIME)public@interfaceLimitRequest{longtime()default6000;//限制时间单位:毫秒intcount()default3;//允许请求的次数}
aspect包下的切面类:
packagecom.feng.aspect;importnet.jodah.expiringmap.ExpirationPolicy;importnet.jodah.expiringmap.ExpiringMap;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Pointcut;importcom.feng.annotation.LimitRequest;importorg.cuit.epoch.exception.AppException;importorg.springframework.stereotype.Component;importorg.springframework.web.context.request.RequestAttributes;importorg.springframework.web.context.request.RequestContextHolder;importorg.springframework.web.context.request.ServletRequestAttributes;importjavax.servlet.http.HttpServletRequest;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.TimeUnit;/***@returnnull*@authorLadidol*@description*@date2022/4/1122:15*/@Aspect@ComponentpublicclassLimitRequestAspect{privatestaticConcurrentHashMap<String,ExpiringMap<String,Integer>>book=newConcurrentHashMap<>();//定义切点//让所有有@LimitRequest注解的方法都执行切面方法@Pointcut("@annotation(limitRequest)")publicvoidexcudeService(LimitRequestlimitRequest){}@Around("excudeService(limitRequest)")publicObjectdoAround(ProceedingJoinPointpjp,LimitRequestlimitRequest)throwsThrowable{//获得request对象RequestAttributesra=RequestContextHolder.getRequestAttributes();ServletRequestAttributessra=(ServletRequestAttributes)ra;HttpServletRequestrequest=sra.getRequest();//获取Map对象,如果没有则返回默认值//第一个参数是key,第二个参数是默认值ExpiringMap<String,Integer>uc=book.getOrDefault(request.getRequestURI(),ExpiringMap.builder().variableExpiration().build());IntegeruCount=uc.getOrDefault(request.getRemoteAddr(),0);if(uCount>=limitRequest.count()){//超过次数,不执行目标方法System.out.println("接口请求超过次数!");thrownewAppException("接口请求超过次数!");}elseif(uCount==0){//第一次请求时,设置有效时间//uc.put(request.getRemoteAddr(),uCount+1,ExpirationPolicy.CREATED,limitRequest.time(),TimeUnit.MILLISECONDS);}else{//未超过次数,记录加一uc.put(request.getRemoteAddr(),uCount+1);}book.put(request.getRequestURI(),uc);//result的值就是被拦截方法的返回值Objectresult=pjp.proceed();returnresult;}}
为了捕获全局的异常抛出, 且符合restful规范我们加一个这个处理类:
handle包下面的全局异常类:
packageorg.cuit.epoch.handler;importlombok.extern.log4j.Log4j2;importorg.cuit.epoch.exception.AppException;importorg.cuit.epoch.result.R;importorg.cuit.epoch.result.Result;importorg.springframework.web.bind.annotation.ControllerAdvice;importorg.springframework.web.bind.annotation.ExceptionHandler;importorg.springframework.web.bind.annotation.ResponseBody;@ControllerAdvice@Log4j2publicclassGlobalExceptionHandler{@ExceptionHandler(Exception.class)@ResponseBodypublicResulterror(Exceptione){log.error(e.getMessage());e.printStackTrace();returnR.fail(e.getMessage());}@ExceptionHandler(AppException.class)@ResponseBodypublicResulterror(AppExceptione){log.error(e.getMessage());e.printStackTrace();returnR.fail(e.getMessage());}}
application.yaml文件:
spring:cache:type:redisredis:#redis连接配置host:自己redis的ip地址port:redis端口password:密码jedis:pool:max-active:8max-wait:-1msmax-idle:500min-idle:0lettuce:shutdown-timeout:0ms
最终项目结构如下:
先得到一个验证码:
验证一下是否成功:
成功结果:
验证失败结果:
当请求在规定时间内的请求数超过规定的数量时或有报错:
到此,相信大家对“springboot图片验证码功能模块怎么实现”有了更深的了解,不妨来实际操作一番吧!这里是恰卡编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
推荐阅读
-
vue动态添加删除输入框(springboot vue怎么让数据库显示出来)
springbootvue怎么让数据库显示出来?一般情况下是前端调阅后端接口,来获取到数据库的数据,后端哪里会把数据库的数据整理...
-
springboot实现基于aop的切面日志
本文实例为大家分享了springboot实现基于aop的切面日志的具体代码,供大家参考,具体内容如下通过aop的切面方式实现日志...
-
SpringBoot定时任务功能怎么实现
-
SpringBoot中的@Import注解怎么使用
-
SpringBoot整合Lombok及常见问题怎么解决
-
Springboot+SpringSecurity怎么实现图片验证码登录
-
SpringBoot注解的知识点有哪些
SpringBoot注解的知识点有哪些这篇“SpringBoot注...
-
SpringBoot2.x中management.security.enabled=false无效怎么解决
-
springboot怎么禁用某项健康检查
springboot怎么禁用某项健康检查今天小编给大家分享一下sp...
-
SpringBoot2怎么自定义端点