Spring框架生成图片验证码实例讲解

合集下载

SpringBoot使用Captcha生成验证码

SpringBoot使用Captcha生成验证码

SpringBoot使⽤Captcha⽣成验证码⽬录1. 基本结构2. Kaptcha的依赖3. 配置SpringBoot4. 配置Captcha5. ⼯具类6. 接⼝以及实现类7. Controller8. 前端页⾯的实现⽰例1. 基本结构使⽤Captcha⽣成验证码, 利⽤Redis存储验证码Redis中的结构为, Key是32位的UUID, Value为Captcha的4位随机字母以及数字的集合设定Redis过期时间为1min, 即可实现过期验证码的⾃动失效2. Kaptcha的依赖基本的依赖这⾥不再叙述, 主要说⼀下要导⼊Captcha的依赖<!--Kaptcha--><dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version></dependency>所有的依赖如下<?xml version="1.0" encoding="UTF-8"?><project xmlns="/POM/4.0.0" xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/POM/4.0.0 https:///xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.0</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.wang</groupId><artifactId>spring_security_framework</artifactId><version>0.0.1-SNAPSHOT</version><name>spring_security_framework</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><!--Redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--JDBC--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!--SpringSecurity--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!--Thymeleaf--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!--Validation--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!--SpringBoot Web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--Mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version></dependency><!--SpringSecurity with thymeleaf--><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId> </dependency><!--MySQL connector--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!--Lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--Test--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency><!--Druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.2</version></dependency><!--FastJSON--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.74</version></dependency><!--log4j--><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><!--Swagger2--><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency><!--HuTool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.4.7</version></dependency><!--Kaptcha--><dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>3. 配置SpringBoot配置SpringBoot的配置⽂件, 这⾥主要关注⼀个session的过期时间#Portserver:port: 80servlet:session:timeout: 1spring:application:name: SpringSecurityFramework#dataBase Settingdatasource:username: rootpassword: 123456url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource#Druid Settingdruid:initial-size: 5min-idle: 5max-active: 20max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 30000validation-query: SELECT 1 FROM DUALtest-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: true#Setting For Druid StatView and Filterfilters: stat,wall,log4jmax-pool-prepared-statement-per-connection-size: 20use-global-data-source-stat: trueconnection-properties: druid.stat.mergeSql=true;druid.stat.slowSql#Redis Settingredis:host: 127.0.0.1port: 6379#Thymeleafthymeleaf:cache: false#Mybatismybatis:type-aliases-package: com.wang.entitymapper-locations: classpath:Mybatis/mapper/*.xmlconfiguration:map-underscore-to-camel-case: true其余的配置, 如log4j, druid, SpringSecurity, RedisTemplate,这⾥就不再赘述4. 配置Captcha我们可以通过JAVA的配置类来配置Captcha⽣成验证码的⼀些规则package com.wang.spring_security_framework.config;import com.google.code.kaptcha.impl.DefaultKaptcha;import com.google.code.kaptcha.util.Config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.Properties;//Kaptcha配置@Configurationpublic class KaptchaConfig {@Beanpublic DefaultKaptcha producer() {//Properties类Properties properties = new Properties();// 图⽚边框properties.setProperty("kaptcha.border", "yes");// 边框颜⾊properties.setProperty("kaptcha.border.color", "105,179,90");// 字体颜⾊properties.setProperty("kaptcha.textproducer.font.color", "blue");// 图⽚宽properties.setProperty("kaptcha.image.width", "110");// 图⽚⾼properties.setProperty("kaptcha.image.height", "40");// 字体⼤⼩properties.setProperty("kaptcha.textproducer.font.size", "30");// session keyproperties.setProperty("kaptcha.session.key", "code");// 验证码长度properties.setProperty("kaptcha.textproducer.char.length", "4");// 字体properties.setProperty("s", "宋体,楷体,微软雅⿊");//图⽚⼲扰properties.setProperty("kaptcha.noise.impl","com.google.code.kaptcha.impl.DefaultNoise");//Kaptcha 使⽤上述配置Config config = new Config(properties);//DefaultKaptcha对象使⽤上述配置, 并返回这个BeanDefaultKaptcha defaultKaptcha = new DefaultKaptcha();defaultKaptcha.setConfig(config);return defaultKaptcha;}}5. ⼯具类使⽤UUID作为key, 同时考虑到对验证码的输出结果可能有不同的要求, 这⾥写两个⼯具类来处理它们UUIDUtilpackage com.wang.spring_security_framework.util;import org.springframework.context.annotation.Bean;import ponent;import java.util.UUID;@Component public class UUIDUtil {/** * ⽣成32位的随机UUID * @return 字符形式的⼩写UUID */@Bean public String getUUID32() {return UUID.randomUUID().toString() .replace("-", "").toLowerCase();}}CaptchaUtilpackage com.wang.spring_security_framework.util;import com.google.code.kaptcha.impl.DefaultKaptcha;import com.wang.spring_security_framework.service.CaptchaService;import ty.handler.codec.base64.Base64Encoder;import org.springframework.beans.factory.annotation.Autowired;import ponent;import sun.misc.BASE64Encoder;import javax.imageio.ImageIO;import java.awt.image.BufferedImage;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStream;import java.util.Map;@Component//Captcha ⽣成⼯具public class CaptchaUtil {@Autowiredprivate DefaultKaptcha producer;@Autowiredprivate CaptchaService captchaService;//⽣成catchCreator的mappublic Map<String, Object> catchaImgCreator() throws IOException {//⽣成⽂字验证码String text = producer.createText();//⽣成⽂字对应的图⽚验证码BufferedImage image = producer.createImage(text);//将图⽚写出ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ImageIO.write(image, "jpg", outputStream);//对写出的字节数组进⾏Base64编码 ==> ⽤于传递8⽐特字节码BASE64Encoder encoder = new BASE64Encoder();//⽣成tokenMap<String, Object> token = captchaService.createToken(text);token.put("img", encoder.encode(outputStream.toByteArray()));return token;}}6. 接⼝以及实现类1. 接⼝package com.wang.spring_security_framework.service;import org.springframework.stereotype.Service;import java.io.IOException;import java.util.Map;public interface CaptchaService {//⽣成tokenMap<String, Object> createToken(String captcha);//⽣成captcha验证码Map<String, Object> captchaCreator() throws IOException;//验证输⼊的验证码是否正确String versifyCaptcha (String token, String inputCode);}2. 实现类package com.wang.spring_security_framework.service.serviceImpl;import com.google.code.kaptcha.impl.DefaultKaptcha;import com.wang.spring_security_framework.service.CaptchaService; import com.wang.spring_security_framework.util.CaptchaUtil;import com.wang.spring_security_framework.util.UUIDUtil;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.ValueOperations;import org.springframework.stereotype.Service;import java.io.IOException;import java.util.HashMap;import java.util.Map;import java.util.concurrent.TimeUnit;@Servicepublic class CaptchaServiceImpl implements CaptchaService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Autowiredprivate UUIDUtil uuidUtil;@Autowiredprivate CaptchaUtil captchaUtil;//从SpringBoot的配置⽂件中取出过期时间@Value("${server.servlet.session.timeout}")private Integer timeout;//UUID为key, 验证码为Value放在Redis中@Overridepublic Map<String, Object> createToken(String captcha) {//⽣成⼀个tokenString key = uuidUtil.getUUID32();//⽣成验证码对应的token 以token为key 验证码为value存在redis中ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();valueOperations.set(key, captcha);//设置验证码过期时间redisTemplate.expire(key, timeout, TimeUnit.MINUTES);Map<String, Object> map = new HashMap<>();map.put("token", key);map.put("expire", timeout);return map;}//⽣成captcha验证码@Overridepublic Map<String, Object> captchaCreator() throws IOException {return captchaUtil.catchaImgCreator();}//验证输⼊的验证码是否正确@Overridepublic String versifyCaptcha(String token, String inputCode) {//根据前端传回的token在redis中找对应的valueValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();if (redisTemplate.hasKey(token)) {//验证通过, 删除对应的keyif (valueOperations.get(token).equals(inputCode)) {redisTemplate.delete(token);return "true";} else {return "false";}} else {return "false";}}}这⾥的验证, 只是简单的验证了输⼊是否能从Redis中匹配, 返回了字符串真实的验证中, 我们还要在逻辑中添加⽤户名和密码的考虑7. Controllerpackage com.wang.spring_security_framework.controller;import com.wang.spring_security_framework.service.CaptchaService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import java.io.IOException;import java.util.Map;@RestControllerpublic class LoginController {@AutowiredCaptchaService captchaService;@GetMapping("/captcha")public Map<String, Object> captcha() throws IOException {return captchaService.captchaCreator();}@GetMapping("/login1")public String login(@RequestParam("token") String token,@RequestParam("inputCode") String inputCode) {return captchaService.versifyCaptcha(token, inputCode);}}captcha ⽤于获取⼀个验证码login1 ⽤于接收到前端的请求后验证并返回结果login1 这⾥为了测试简便实⽤了GET⽅法, ⽽实际中最好使⽤POST⽅法, 这样安全性更⾼8. 前端页⾯的实现前端结构如图, 实现了⼀个简单的验证码<!DOCTYPE html><html lang="en" xmlns:th=""><head><meta charset="UTF-8"><title>登录</title><script src="https:///jquery/3.4.1/jquery.js"></script></head><body><div><div><form th:action="@{/login1}" method="get"><input type="text" id="userName" placeholder="请输⼊⽤户名" name="userName"><br><input type="password" id="password" placeholder="请输⼊密码" name="password"><br><input type="text" id="inputCode" placeholder="请输⼊验证码" maxlength="4" name="inputCode"><!--通过隐藏域传递值, 在下⾯的验证码点击事件中, 将值绑定过来, 这样就可以获得最新的验证码对应的值了!--><input id="token" value="" type="hidden" name="token"><input type="submit" value="登录"></form></div><div><!-- 当⽤户链接时,void(0)计算为0,⽤户点击不会发⽣任何效果 --><a href="javascript:void(0);" rel="external nofollow" title="点击更换验证码"><!--this参数, 返回当前的DOM元素--><img src="" alt="更换验证码" id="imgVerify" onclick="getVerify(this)"></a></div></div><script>//获得img对象let imgVerify = $("#imgVerify").get(0);//$(function())等同于$(document).ready(function()) ==> 页⾯加载完毕之后, 才执⾏函数$(function () {getVerify(imgVerify);});//onclick时间绑定的getVerify函数function getVerify(obj) {$.ajax({type: "GET",url: "/captcha",success: function (result) {obj.src = "data:image/jpeg;base64," + result.img;$("#token").val(result.token);}});}</script></body></html>⽤⼀个 a 标签包围 img 标签, 这样如果图⽚没有加载出来也有⼀个超链接, 不过点了以后没有效果(function())等同于(function())等同于(document).ready(function()) ==> 页⾯加载完毕之后, 才执⾏函数, 这⾥必须要写这个函数, 否则第⼀次加载不会调⽤ onclick ⽅法, 也就不会⽣成验证码!我们利⽤隐藏域将验证码的key传递到表单中, 我们在 img 的点击事件对应的函数的ajax回调函数中可以利⽤jQuery操作DOM, 顺带取出key值放到我们的隐藏域中, 这样提交的时候就会提交 key 和⽤户输⼊的 value 了⽰例验证通过以上就是SpringBoot使⽤Captcha⽣成验证码的详细内容,更多关于SpringBoot⽣成验证码的资料请关注其它相关⽂章!。

SpringBoot实现前端验证码图片生成和校验

SpringBoot实现前端验证码图片生成和校验

SpringBoot实现前端验证码图⽚⽣成和校验SpringBoot下实现前端验证码图⽚的⽣成和校验,供⼤家参考,具体内容如下1.效果点击验证码可以获取新的验证码2.原理后台⽣成验证码图⽚,将图⽚传到前台。

后台在session中保存验证码内容。

前台输⼊验证码后传到后台在后台取出session中保存的验证码进⾏校验。

注意,验证码的明⽂是不能传送到前端的。

前端内容都是透明的,不安全。

验证码是⽤来防机器⼈并不是单单防⼈。

如果把验证码明⽂传到前端很容易就会被破解。

3.图⽚⽣成验证码⽣成⼯具类RandomValidateCodeUtilpublic class RandomValidateCodeUtil {public static final String RANDOMCODEKEY= "RANDOMVALIDATECODEKEY";//放到session中的keyprivate String randString = "0123456789";//随机产⽣只有数字的字符串 private String//private String randString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产⽣只有字母的字符串//private String randString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产⽣数字与字母组合的字符串private int width = 95;// 图⽚宽private int height = 25;// 图⽚⾼private int lineSize = 40;// ⼲扰线数量private int stringNum = 4;// 随机产⽣字符数量private static final Logger logger = LoggerFactory.getLogger(RandomValidateCodeUtil.class);private Random random = new Random();/*** 获得字体*/private Font getFont() {return new Font("Fixedsys", Font.CENTER_BASELINE, 18);}/*** 获得颜⾊*/private Color getRandColor(int fc, int bc) {if (fc > 255)fc = 255;if (bc > 255)bc = 255;int r = fc + random.nextInt(bc - fc - 16);int g = fc + random.nextInt(bc - fc - 14);int b = fc + random.nextInt(bc - fc - 18);return new Color(r, g, b);}/*** ⽣成随机图⽚*/public void getRandcode(HttpServletRequest request, HttpServletResponse response) {HttpSession session = request.getSession();// BufferedImage类是具有缓冲区的Image类,Image类是⽤于描述图像信息的类BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);Graphics g = image.getGraphics();// 产⽣Image对象的Graphics对象,改对象可以在图像上进⾏各种绘制操作g.fillRect(0, 0, width, height);//图⽚⼤⼩g.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 18));//字体⼤⼩g.setColor(getRandColor(110, 133));//字体颜⾊// 绘制⼲扰线for (int i = 0; i <= lineSize; i++) {drowLine(g);}// 绘制随机字符String randomString = "";for (int i = 1; i <= stringNum; i++) {randomString = drowString(g, randomString, i);}(randomString);//将⽣成的随机字符串保存到session中session.removeAttribute(RANDOMCODEKEY);session.setAttribute(RANDOMCODEKEY, randomString);g.dispose();try {// 将内存中的图⽚通过流动形式输出到客户端ImageIO.write(image, "JPEG", response.getOutputStream());} catch (Exception e) {logger.error("将内存中的图⽚通过流动形式输出到客户端失败>>>> ", e);}}/*** 绘制字符串*/private String drowString(Graphics g, String randomString, int i) {g.setFont(getFont());g.setColor(new Color(random.nextInt(101), random.nextInt(111), random.nextInt(121)));String rand = String.valueOf(getRandomString(random.nextInt(randString.length())));randomString += rand;g.translate(random.nextInt(3), random.nextInt(3));g.drawString(rand, 13 * i, 16);return randomString;}/*** 绘制⼲扰线*/private void drowLine(Graphics g) {int x = random.nextInt(width);int y = random.nextInt(height);int xl = random.nextInt(13);int yl = random.nextInt(15);g.drawLine(x, y, x + xl, y + yl);}/*** 获取随机的字符*/public String getRandomString(int num) {return String.valueOf(randString.charAt(num));}}在Controller调⽤⽣成验证码图⽚⽅法并将图⽚传到前端/*** ⽣成验证码*/@RequestMapping(value = "/getVerify")public void getVerify(HttpServletRequest request, HttpServletResponse response) {try {response.setContentType("image/jpeg");//设置相应类型,告诉浏览器输出的内容为图⽚response.setHeader("Pragma", "No-cache");//设置响应头信息,告诉浏览器不要缓存此内容 response.setHeader("Cache-Control", "no-cache");response.setDateHeader("Expire", 0);RandomValidateCodeUtil randomValidateCode = new RandomValidateCodeUtil();randomValidateCode.getRandcode(request, response);//输出验证码图⽚⽅法} catch (Exception e) {logger.error("获取验证码失败>>>> ", e);}}前端获取验证码图⽚html<div class="row"><div class="col-xs-6 pull_left"><div class="form-group"><input class="form-control" type="tel" id="verify_input" placeholder="请输⼊验证码" maxlength="4"></div></div><div class="col-xs-6 pull_left"><a href="javascript:void(0);" rel="external nofollow" title="点击更换验证码"><img id="imgVerify" src="" alt="更换验证码" height="36" width="100%" onclick="getVerify(this);"></a></div></div>js//获取验证码function getVerify(obj){obj.src = httpurl + "/sys/getVerify?"+Math.random();}每次点击图⽚重新刷新验证码界⾯初次加载时,调⽤getVerify()⽅法即可。

SpringBoot使用Captcha生成验证码

SpringBoot使用Captcha生成验证码

SpringBoot使用Captcha生成验证码验证码是一种用于验证用户身份的常见技术。

使用验证码可以防止恶意用户自动注册、暴力破解密码、刷票等攻击。

在Spring Boot中,我们可以使用Captcha库来生成验证码。

Captcha是一个Java库,用于生成图像验证码。

它可以生成不同类型的验证码,包括数字、字母、算术运算符等。

首先,我们需要在项目的pom.xml文件中添加Captcha库的依赖:```xml<dependency><artifactId>kaptcha</artifactId><version>2.3.2</version></dependency>```然后,在Spring Boot的配置文件中添加以下配置:```yamlkaptcha:border:color: blacktextproducer:char.length: 4image.width: 150image.height: 50```这些配置可以根据需求进行调整。

例如,可以更改验证码的字符集、长度、图像大小等。

接下来,在Spring Boot的控制器中添加生成验证码的方法:```javapublic void getCaptcha(HttpServletResponse response, HttpSession session) throws IOException//创建验证码生成器DefaultKaptcha captchaProducer = new DefaultKaptcha(;//生成验证码文本String codeText = captchaProducer.createText(;// 将验证码文本保存到session中session.setAttribute("captcha", codeText);//创建验证码图像BufferedImage image = captchaProducer.createImage(codeText);//将图像输出到响应流中response.setContentType("image/jpeg");try (ServletOutputStream outputStream =response.getOutputStream()ImageIO.write(image, "jpeg", outputStream);}```在上面的方法中,我们使用DefaultKaptcha类来创建验证码生成器,并使用createText(方法生成验证码文本。

图形验证码的两种实现方式

图形验证码的两种实现方式

图形验证码的两种实现⽅式情形⼀:图形验证码跟短信验证码⼀起,只需要将后台提供的动态链接填到(id="img")的src中即可⽣成动态验证码。

然后,在需要请求接⼝的地⽅,只需把(id="imgCode")中⽤户输⼊的信息通过ajax传给后台,验证验证码是否正确。

原理(后台):后台通过session存储图⽚上的字符串,和之后前台请求过来的带的输⼊的字符串参数,做⽐较,判断是否⼀样。

<!doctype html><html><head><meta charset="UTF-8"><title>图形验证码</title></head><body><form><div class="imgCodeBox"><label for="imgCode">图形验证码</label><input type="text" placeholder="请输⼊验证码" id="imgCode"><img src="" id="img"></div></form></body></html>情形⼆:⽤cavas,但是没有安全性,考虑到实⽤性的话,还是⽤情形⼀的好<!doctype html><html><head><meta charset="UTF-8"><title>测试</title></head><body><canvas id="canvas" width="120" height="40"></canvas><a href="#" id="changeImg">看不清,换⼀张</a><script>/**⽣成⼀个随机数**/function randomNum(min,max){return Math.floor( Math.random()*(max-min)+min);}/**⽣成⼀个随机⾊**/function randomColor(min,max){var r = randomNum(min,max);var g = randomNum(min,max);var b = randomNum(min,max);return "rgb("+r+","+g+","+b+")";}drawPic();document.getElementById("changeImg").onclick = function(e){e.preventDefault();drawPic();}/**绘制验证码图⽚**/function drawPic(){var canvas=document.getElementById("canvas");var width=canvas.width;var height=canvas.height;var ctx = canvas.getContext('2d');ctx.textBaseline = 'bottom';/**绘制背景⾊**/ctx.fillStyle = randomColor(180,240); //颜⾊若太深可能导致看不清ctx.fillRect(0,0,width,height);/**绘制⽂字**/var str = 'ABCEFGHJKLMNPQRSTWXY123456789';for(var i=0; i<4; i++){var txt = str[randomNum(0,str.length)];ctx.fillStyle = randomColor(50,160); //随机⽣成字体颜⾊ctx.font = randomNum(15,40)+'px SimHei'; //随机⽣成字体⼤⼩var x = 10+i*25;var y = randomNum(25,45);var deg = randomNum(-45, 45);//修改坐标原点和旋转⾓度ctx.translate(x,y);ctx.rotate(deg*Math.PI/180);ctx.fillText(txt, 0,0);//恢复坐标原点和旋转⾓度ctx.rotate(-deg*Math.PI/180);ctx.translate(-x,-y);}/**绘制⼲扰线**/for(var i=0; i<8; i++){ctx.strokeStyle = randomColor(40,180);ctx.beginPath();ctx.moveTo( randomNum(0,width), randomNum(0,height) );ctx.lineTo( randomNum(0,width), randomNum(0,height) );ctx.stroke();}/**绘制⼲扰点**/for(var i=0; i<100; i++){ctx.fillStyle = randomColor(0,255);ctx.beginPath();ctx.arc(randomNum(0,width),randomNum(0,height), 1, 0, 2*Math.PI);ctx.fill();}}</script></body></html>情形⼆转⾃:https:///meishuixingdeququ/article/details/52386542情形三、⽤js产⽣随机数实现1、创建图形码容器<label class="myLabel">图形码:<input type = "button" id="code" onclick="createCode()" style="border: 0;background-color: transparent;padding: 0;"/> </label>2、产⽣验证码并在页⾯加载时和点击时调⽤ // 图形验证码var code ; //在全局定义验证码//产⽣验证码window.onload = function createCode(){code = "";var codeLength = 4;//验证码的长度var checkCode = document.getElementById("code");var random = new Array(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');//随机数for(var i = 0; i < codeLength; i++) {//循环操作var index = Math.floor(Math.random()*36);//取得随机数的索引(0~35)code += random[index];//根据索引取得随机数加到code上}checkCode.value = code;//把code值赋给验证码}。

SpringBoot+Vue实现请求后台获取Base64编码的图片验证码并使用Redis缓。。。

SpringBoot+Vue实现请求后台获取Base64编码的图片验证码并使用Redis缓。。。

SpringBoot+Vue实现请求后台获取Base64编码的图⽚验证码并使⽤Redis缓。

场景前端Vue的登录页⾯,验证码请求后台,后台⽣成验证码照⽚后使⽤Base64编码后,返回给前端,前端进⾏显⽰。

注:实现⾸先看前端页⾯login.vue<el-form-item prop="code"><el-inputv-model="loginForm.code"auto-complete="off"placeholder="验证码"style="width: 63%"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="validCode"class="el-input__icon input-icon" /></el-input><div class="login-code"><img :src="codeUrl" @click="getCode" /></div>这⾥验证码的图⽚的src属性是绑定的codeUrl,此属性来⾃于请求后台,并且绑定点击事件为getCode此⽅法也是请求后台获取验证码。

所以需要提前声明codeUrl属性data() {return {codeUrl: "",然后在页⾯加载完时请求后台接⼝获取验证码图⽚。

所以在created函数中执⾏请求验证码图⽚的⽅法created() {this.getCode();},此⽅法的具体实现methods: {getCode() {getCodeImg().then(res => {this.codeUrl = "data:image/gif;base64," + res.img;this.loginForm.uuid = res.uuid;});},在此⽅法中调⽤了请求后台接⼝获取验证码图⽚编码后的代码并在前⾯拼接上"data:image/gif;base64,"data:image/png;base64的⽤法详解⽹页上有些图⽚的src或css背景图⽚的url后⾯跟了⼀⼤串字符,⽐如:<img src='data:img/jpg;base64,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。

java如何生成验证码呢

java如何生成验证码呢

java如何生成验证码呢java如何生成验证码呢Java生成验证码的流程是:收到请求->生成验证码所用的随机数->使用随机数写出图片->将随机数记录到Session中->输出验证码Java验证验证码的流程是:收到请求->获取用户传过来的验证码数字->验证是否正确->输出验证结果下面通过一个例子来展示验证码的生成流程,该例子使用基本Java Spring框架的'Rest接口,可以使用任何平台来获取验证码:服务器处理验证码的例子:1.接收验证码请求:/*** 接收验证码请求*/@RequestMapping(value="captchacode")public void CaptchaCode(){ try { CaptchaCodeModel captchaCodeModel=new CaptchaCode().getCode(); //将验证码放到Session中HttpServletRequest httpServletRequest=super.getRequest(); httpServletRequest.getSession().setAttribute("captchacodekey", captchaCodeModel.getCaptchaCode()); //将图片写到客户端HttpServletResponse httpServletResponse=super.getResponse(); //禁止缓存httpServletResponse.setHeader("Pragma", "no-cache"); httpServletResponse.setHeader("Cache-Control", "no-cache"); httpServletResponse.setDateHeader("Expires", 0); ServletOutputStreamservletOutputStream=httpServletResponse.getOutputStream(); //输出图片 ImageIO.write(captchaCodeModel.getCaptchaImage(), "jpeg", servletOutputStream); servletOutputStream.close(); } catch (Exception e) { ("验证码生成失败:"+e.getMessage()); }}2.生成验证码并生成图片:public class CaptchaCode {private int width = 90;// 定义图片的widthprivate int height = 20;// 定义图片的heightprivate int codeCount = 4;// 定义图片上显示验证码的个数private int xx = 15;private int fontHeight = 18;private int codeY = 16;char[] codeSequence = { 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, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };【java如何生成验证码呢】。

在JSP页面中动态生成图片验证码的方法实例

在JSP页面中动态生成图片验证码的方法实例

在JSP页⾯中动态⽣成图⽚验证码的⽅法实例在JSP页⾯中动态⽣成图⽚验证码<%@ page language="java" pageEncoding="UTF-8"%><%@ pageimport="java.awt.*,java.awt.image.*,com.sun.image.codec.jpeg.*,java.util.*" %><%@ taglib /tags-bean">/tags-bean"prefix="bean" %><%@ taglib /tags-html">/tags-html"prefix="html" %><%@ taglib /tags-logic">/tags-logic"prefix="logic" %><%@ taglib /tags-tiles">/tags-tiles"prefix="tiles" %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html:html lang="true"><head><html:base /><title>MyJsp.jsp</title><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"><meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"><!--<link rel="stylesheet" type="text/css" href="styles.css" rel="external nofollow" >--></head><body><h3>在jsp页⾯⽣成验证码</h3><hr/><%//out.clear();//response.setContentType("image/jpeg");//设置响应类型//response.addHeader("pragma","NO-cache");//response.addHeader("Cache-Control","no-cache");//response.addDateHeader("Expries",0);int width=400, height=30;//图⽚的⼤⼩(宽和⾼)//构架画布,第⼀个参数表⽰画布的宽,第⼆个参数表⽰画布的⾼,第三个参数的含义有待确定BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics g = image.getGraphics();//实例化画图对象//以下设置背景⾊g.setColor(Color.yellow);Font DeFont=new Font("宋体", Font.ITALIC, 20);g.setFont(DeFont);//将已经设置好的背景颜⾊填充到指定的画布区域g.fillRect(0,0, width, height);//置字体⾊g.setColor(Color.blue);int x=10,y=10,xl=550,yl=15;g.drawLine(x,y,x+xl,y+yl);//在画布中画椭圆形,参数为椭圆的坐标,⽤于确定椭圆的⼤⼩g.drawOval(0,10,200,10);//在画布上输出⽂字信息,第⼀个参数表⽰要显⽰的⽂字,第⼆和第三个参数表⽰起始点的X、Y坐标g.drawString("想要输出的⽂字-我是陈杉",70,20);g.dispose();ServletOutputStream outStream = response.getOutputStream();JPEGImageEncoder encoder =JPEGCodec.createJPEGEncoder(outStream);encoder.encode(image);outStream.close();%></body></html:html>--将该⽂件保存为pic.jsp,该⽂件负责⽣成图⽚!如果要在其他的页⾯显⽰该图⽚只需要写上<img src="pic.jsp"/>仅此⼀句就ok了,适⽤于⽣成各种验证码!总结以上就是这篇⽂章的全部内容了,希望本⽂的内容对⼤家的学习或者⼯作具有⼀定的参考学习价值,谢谢⼤家对的⽀持。

SpringBoot+EasyCaptcha实现验证码功能

SpringBoot+EasyCaptcha实现验证码功能

SpringBoot+EasyCaptcha实现验证码功能⼆、SpringBoot项⽬如何使⽤1、初始化⼀个基本的SpringBoot项⽬2、引⼊EasyCaptcha 依赖<dependency> <groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId><version>1.6.2</version></dependency>3、⽣成验证码@RequestMapping("/captcha")public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception{GifCaptcha gifCaptcha = new GifCaptcha(130,48,4);CaptchaUtil.out(gifCaptcha, request, response);String verCode = gifCaptcha.text().toLowerCase();request.getSession().setAttribute("CAPTCHA",verCode); //存⼊session}4、校验验证码public class LoginController {private static final Logger log = LoggerFactory.getLogger(LoginController.class);@GetMapping("/")public String login() {return"/test";}@PostMapping("/test")public String test(HttpServletRequest request, HttpServletResponse response) {String username = request.getParameter("username");String password = request.getParameter("password");String captcha = request.getParameter("code");String sessionCode = request.getSession().getAttribute("CAPTCHA").toString();if(sessionCode == null ||StringUtils.isEmpty(sessionCode)){return"验证码为空";}if(captcha.equals(sessionCode)){return"验证通过";}return"验证失败";}}5、前端html中页⾯代码<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><script type="text/javascript" src="./lib/jquery/1.9.1/jquery.min.js"></script></head><body><div><form action="http://localhost:8088/test" method="post"><div><label>⽤户名</label><input type="text" name="username" id="username"><label>密码</label><input type="password" name="password" id="password"><label>验证码</label><img src="/captcha" height="48px" width="130px"><input type="text" name="code" id="code"><input type="submit" value="登录"></div></form></div></body></html>ref:。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Spring框架生成图片验证码实例
验证码在很多地方都会遇到,实现的方法和形式也有很多,主要的目的就是为了安全,防止一些恶意的攻击等。

今天在之前搭建好的一个spring框架上写了一个验证码的生成demo,我会贴出细节代码,但是spring的配置就不在介绍了,有需要的可以参考借鉴。

这篇文章会从前台页面到后台实现完整的讲解,下面跟着小编一起来看看。

1、前台的代码,image.jsp
2、后台代码ImageGenController.java
3、生成验证码的工具类RandomValidateCode.java
4、总结
本文的内容到这就结束了,如果对里面的地方有不懂的地方,可以留言讨论。

希望本文的内容对大家的学习工作能有所帮助。

相关文档
最新文档