您好,欢迎访问一九零五行业门户网

springboot图片验证码功能模块怎么实现

具体效果如下:
第一步:工具类
该工具类为生成验证码图片的核心,直接拷贝到项目即可,无需做修改;可个性化的参数全部对外提供的api,比如 字体大小,背景颜色,干扰线数量,高宽等都可以根据自己的需求设置对应参数;
代码几乎每一行都加了详细的注释;如果遇上特殊的个性化需求,调整一下这个工具类即可实现。
package com.feng.util;/** * @return null * @author ladidol * @description * @date 2022/4/11 22:15 */import java.awt.*;import java.awt.geom.affinetransform;import java.awt.image.bufferedimage;import java.util.random;/** * 图形验证码生成 */public class verifyutil { // 默认验证码字符集 private static final char[] 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'}; // 默认字符数量 private final integer size; // 默认干扰线数量 private final int lines; // 默认宽度 private final int width; // 默认高度 private final int height; // 默认字体大小 private final int font_size; // 默认字体倾斜 private final boolean tilt; private final color background_color; /** * 初始化基础参数 * * @param builder */ private verifyutil(builder builder) { size = builder.size; lines = builder.lines; width = builder.width; height = builder.height; font_size = builder.fontsize; tilt = builder.tilt; background_color = builder.backgroundcolor; } /** * 实例化构造器对象 * * @return */ public static builder newbuilder() { return new builder(); } /** * @return 生成随机验证码及图片 * object[0]:验证码字符串; * object[1]:验证码图片。 */ public object[] createimage() { stringbuffer sb = new stringbuffer(); // 创建空白图片 bufferedimage image = new bufferedimage(width, height, bufferedimage.type_int_rgb); // 获取图片画笔 graphics2d graphic = image.creategraphics(); // 设置抗锯齿 graphic.setrenderinghint(renderinghints.key_antialiasing, renderinghints.value_antialias_on); // 设置画笔颜色 graphic.setcolor(background_color); // 绘制矩形背景 graphic.fillrect(0, 0, width, height); // 画随机字符 random ran = new random(); //graphic.setbackground(color.white); // 计算每个字符占的宽度,这里预留一个字符的位置用于左右边距 int codewidth = width / (size + 1); // 字符所处的y轴的坐标 int y = height * 3 / 4; for (int i = 0; i < size; i++) { // 设置随机颜色 graphic.setcolor(getrandomcolor()); // 初始化字体 font font = new font(null, font.bold + font.italic, font_size); if (tilt) { // 随机一个倾斜的角度 -45到45度之间 int theta = ran.nextint(45); // 随机一个倾斜方向 左或者右 theta = (ran.nextboolean() == true) ? theta : -theta; affinetransform affinetransform = new affinetransform(); affinetransform.rotate(math.toradians(theta), 0, 0); font = font.derivefont(affinetransform); } // 设置字体大小 graphic.setfont(font); // 计算当前字符绘制的x轴坐标 int x = (i * codewidth) + (codewidth / 2); // 取随机字符索引 int n = ran.nextint(chars.length); // 得到字符文本 string code = string.valueof(chars[n]); // 画字符 graphic.drawstring(code, x, y); // 记录字符 sb.append(code); } // 画干扰线 for (int i = 0; i < lines; i++) { // 设置随机颜色 graphic.setcolor(getrandomcolor()); // 随机画线 graphic.drawline(ran.nextint(width), ran.nextint(height), ran.nextint(width), ran.nextint(height)); } // 返回验证码和图片 return new object[]{sb.tostring(), image}; } /** * 随机取色 */ private color getrandomcolor() { random ran = new random(); color color = new color(ran.nextint(256), ran.nextint(256), ran.nextint(256)); return color; } /** * 构造器对象 */ public static class builder { // 默认字符数量 private int size = 4; // 默认干扰线数量 private int lines = 10; // 默认宽度 private int width = 80; // 默认高度 private int height = 35; // 默认字体大小 private int fontsize = 25; // 默认字体倾斜 private boolean tilt = true; //背景颜色 private color backgroundcolor = color.light_gray; public builder setsize(int size) { this.size = size; return this; } public builder setlines(int lines) { this.lines = lines; return this; } public builder setwidth(int width) { this.width = width; return this; } public builder setheight(int height) { this.height = height; return this; } public builder setfontsize(int fontsize) { this.fontsize = fontsize; return this; } public builder settilt(boolean tilt) { this.tilt = tilt; return this; } public builder setbackgroundcolor(color backgroundcolor) { this.backgroundcolor = backgroundcolor; return this; } public verifyutil build() { return new verifyutil(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层:
package com.feng.service;import org.cuit.epoch.result.result;import javax.servlet.http.httpservletrequest;import javax.servlet.http.httpservletresponse;import java.io.ioexception;/** * @return null * @author ladidol * @description * @date 2022/4/11 22:15 */public interface verifyservice { /** * 创建图片验证码 * @param response * @param request * @throws ioexception */ void createcode(httpservletresponse response, httpservletrequest request) throws ioexception; /** * 检查图片验证码 * @param * @param * @throws ioexception */ result<string> checkcode(string verificationcode);}
serviceimpl层:
package com.feng.service.impl;import com.feng.service.verifyservice;import com.feng.util.redisserviceimpl;import com.google.common.net.httpheaders;import com.feng.util.verifyutil;import org.springframework.http.responsecookie;import org.springframework.stereotype.service;import javax.annotation.resource;import javax.imageio.imageio;import javax.servlet.http.httpservletrequest;import javax.servlet.http.httpservletresponse;import javax.servlet.http.httpsession;import java.awt.image.bufferedimage;import java.io.ioexception;import java.io.outputstream;import java.time.duration;/** * @return null * @author ladidol * @description * @date 2022/4/11 22:15 */@servicepublic class verifyserviceimpl implements verifyservice { @resource redisserviceimpl redisutil; /** * 生成图片验证码 * @param response * @param request * @throws ioexception */ @override public void createcode(httpservletresponse response, httpservletrequest request) throws ioexception { //获取session httpsession session = request.getsession(); //获得sessionid string id = session.getid(); system.out.println(); responsecookie cookie = 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()){ string getverify = string.valueof(request.getsession().getattribute("session_verify_code_"+id)); redisutil.del(getverify); system.out.println("清除成功"); } //生成图片验证码,用的默认参数 object[] verify = verifyutil.newbuilder().build().createimage(); //将验证码存入session session.setattribute("session_verify_code_" + id, verify[0]); //打印验证码 system.out.println(verify[0]); //将验证码存入redis redisutil.set((string) verify[0],id,5*60); //将图片传给浏览器 bufferedimage image = (bufferedimage) verify[1]; response.setcontenttype("image/png"); response.setheader(httpheaders.set_cookie,cookie.tostring()); outputstream ops = response.getoutputstream(); imageio.write(image,"png",ops); } @override public result<string> checkcode(string verificationcode){ if (!redisutil.haskey(verificationcode)){ return new result<>(false,"验证码错误"); } redisutil.del(verificationcode); return r.success(); }}
这里面还会用到redis相关的工具类,我就不列出来了,想要的话可以看我以前的博客工具类戳这里
controller层:
这里有用到@requiredargsconstructor, 就是简单的注入而已, 如果想要详细了解戳这里
package com.feng.controller;import lombok.requiredargsconstructor;import com.feng.annotation.limitrequest;import com.feng.service.verifyservice;import org.springframework.web.bind.annotation.*;import javax.servlet.http.httpservletrequest;import javax.servlet.http.httpservletresponse;import java.io.ioexception;/** * @return null * @author ladidol * @description 这里主要就是多种验证码和登录相关的东西 * @date 2022/4/11 21:46 */@restcontroller@requestmapping("/verify")@requiredargsconstructor//这是在lombok工具给的注入方式,真帅public class verifycontroller { private final verifyservice verifyservice; /** * 获取图片验证码 */ @limitrequest(count = 5)//这个注解就是表示, 你在限制时间里(我们这里默认是六秒钟), 只能请求五次 @getmapping("/getcode") public void getcode(httpservletresponse response, httpservletrequest request) throws ioexception { verifyservice.createcode(response, request); } @limitrequest(count = 5)//这个注解就是表示, 你在限制时间里(我们这里默认是六秒钟), 只能请求五次 @getmapping("/checkcode") public result<string> checkcode(string code){ return verifyservice.checkcode(code); }}
这里为了不被一直无限制的访问该服务, 我们用了一个限制ip访问次数的注解@limitrequest
annotion包下的注解类:
package com.feng.annotation;import java.lang.annotation.*;/** * @return null * @author ladidol * @description 限制ip访问次数注解 * @date 2022/4/11 22:15 */@documented@target(elementtype.method) // 说明该注解只能放在方法上面@retention(retentionpolicy.runtime)public @interface limitrequest { long time() default 6000; // 限制时间 单位:毫秒 int count() default 3; // 允许请求的次数}
aspect包下的切面类:
package com.feng.aspect;import net.jodah.expiringmap.expirationpolicy;import net.jodah.expiringmap.expiringmap;import org.aspectj.lang.proceedingjoinpoint;import org.aspectj.lang.annotation.around;import org.aspectj.lang.annotation.aspect;import org.aspectj.lang.annotation.pointcut;import com.feng.annotation.limitrequest;import org.cuit.epoch.exception.appexception;import org.springframework.stereotype.component;import org.springframework.web.context.request.requestattributes;import org.springframework.web.context.request.requestcontextholder;import org.springframework.web.context.request.servletrequestattributes;import javax.servlet.http.httpservletrequest;import java.util.concurrent.concurrenthashmap;import java.util.concurrent.timeunit;/** * @return null * @author ladidol * @description * @date 2022/4/11 22:15 */@aspect@componentpublic class limitrequestaspect { private static concurrenthashmap<string, expiringmap<string, integer>> book = new concurrenthashmap<>(); // 定义切点 // 让所有有@limitrequest注解的方法都执行切面方法 @pointcut("@annotation(limitrequest)") public void excudeservice(limitrequest limitrequest) { } @around("excudeservice(limitrequest)") public object doaround(proceedingjoinpoint pjp, limitrequest limitrequest) throws throwable { // 获得request对象 requestattributes ra = requestcontextholder.getrequestattributes(); servletrequestattributes sra = (servletrequestattributes) ra; httpservletrequest request = sra.getrequest(); // 获取map对象, 如果没有则返回默认值 // 第一个参数是key, 第二个参数是默认值 expiringmap<string, integer> uc = book.getordefault(request.getrequesturi(), expiringmap.builder().variableexpiration().build()); integer ucount = uc.getordefault(request.getremoteaddr(), 0); if (ucount >= limitrequest.count()) { // 超过次数,不执行目标方法 system.out.println("接口请求超过次数!"); throw new appexception("接口请求超过次数!"); } else if (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的值就是被拦截方法的返回值 object result = pjp.proceed(); return result; }}
为了捕获全局的异常抛出, 且符合restful规范我们加一个这个处理类:
handle包下面的全局异常类:
package org.cuit.epoch.handler;import lombok.extern.log4j.log4j2;import org.cuit.epoch.exception.appexception;import org.cuit.epoch.result.r;import org.cuit.epoch.result.result;import org.springframework.web.bind.annotation.controlleradvice;import org.springframework.web.bind.annotation.exceptionhandler;import org.springframework.web.bind.annotation.responsebody;@controlleradvice@log4j2public class globalexceptionhandler { @exceptionhandler(exception.class) @responsebody public result error(exception e) { log.error(e.getmessage()); e.printstacktrace(); return r.fail(e.getmessage()); } @exceptionhandler(appexception.class) @responsebody public result error(appexception e) { log.error(e.getmessage()); e.printstacktrace(); return r.fail(e.getmessage()); }}
application.yaml文件:
spring: cache: type: redis redis: #redis连接配置 host: 自己redis的ip地址 port: redis端口 password: 密码 jedis: pool: max-active: 8 max-wait: -1ms max-idle: 500 min-idle: 0 lettuce: shutdown-timeout: 0ms
最终项目结构如下:
先得到一个验证码:
验证一下是否成功:
成功结果:
验证失败结果:
当请求在规定时间内的请求数超过规定的数量时或有报错:
以上就是springboot图片验证码功能模块怎么实现的详细内容。
其它类似信息

推荐信息