前端
<template>
<el-form
:inline="true"
ref="ruleFormRef"
:model="ruleForm"
status-icon
:rules="rules"
label-width="120px"
class="demo-ruleForm">
<!-- 验证码输入框 -->
<el-form-item label="验证码" prop="verifiCode">
<el-input v-model="ruleForm.verifiCode" type="text" autocomplete="off" />
</el-form-item>
<!-- 验证码图片 -->
<el-form-item>
<el-image @click="getVerifiCode" :src="url" />
</el-form-item>
<!-- 按钮 -->
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)">提交</el-button>
<el-button @click="resetForm(ruleFormRef)">重置</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { FormInstance } from 'element-plus'
import axios from 'axios'
//前端必须设置此项,后台session才能获取到值
axios.defaults.withCredentials = true;
const ruleFormRef = ref(FormInstance)
//记录前端输入的验证码变量
const ruleForm = reactive({
verifiCode: '',
})
//页面加载时获取图片
const url = ref('http://localhost:8089/getVerifiCode')
//点击图片时获取新验证码
const getVerifiCode = () => {
//让参数随机可切换验证码(重新生成,避免浏览器缓存)
url.value = 'http://localhost:8089/getVerifiCode?' + new Date().getTime();
}
const checkVerifiCode = (rule, value, callback) => {
if (!value) {
return callback(new Error('请输入验证码'))
} else {
//验证码不为空时去后台验证
axios.post('http://localhost:8089/checkVerifiCode?verifiCode=' + ruleForm.verifiCode).then((res) => {
if (res.data == "error") {
return callback(new Error('验证码错误'))
}else{
callback()
}
})
}
}
//校验
const rules = reactive({
verifiCode: [{ validator: checkVerifiCode }]
})
//提交
const submitForm = (formEl) => {
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
alert('提交成功')
} else {
alert('提交失败')
return false
}
})
}
//重置
const resetForm = (formEl) => {
if (!formEl) return
formEl.resetFields()
}
</script>
后端依赖pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
后端controller
package com.xx.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.OutputStream;
@Controller
public class CodeController {
@ResponseBody
@RequestMapping("/checkVerifiCode")
public String checkVerifiCode(String verifiCode, HttpServletRequest request ) {
//获取session中的验证码字符串
String code = (String) request.getSession().getAttribute("verificode");
if(code.equals(verifiCode)) {
return "ok" ;
}else {
return "error" ;
}
}
//获取验证码
@ResponseBody
@RequestMapping("/getVerifiCode")
public String getVerifiCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
final int width = 200; // 图片宽度
final int height = 100; // 图片高度
final String imgType = "jpeg"; // 指定图片格式 (不是指MIME类型)
// 获得 当前请求 对应的 会话对象(需要在输出流之前创建)
HttpSession session = request.getSession();
// 获得可以向客户端返回图片的输出流
final OutputStream output = response.getOutputStream();
// 创建验证码图片并返回图片上的字符串
String code = GraphicHelper.create(width, height, imgType, output);
// 存储到当前会话对象的属性中
session.setAttribute("verificode", code);
//返回验证码(字符串)
return code;
}
}
验证码工具类
public class GraphicHelper {
/**
* 以字符串形式返回生成的验证码,同时输出一个图片
*
* @param width 图片的宽度
* @param height 图片的高度
* @param imgType 图片的类型
* @param output 图片的输出流(图片将输出到这个流中)
* @return 返回所生成的验证码(字符串)
*/
public static String create(final int width, final int height, final String imgType, OutputStream output) {
StringBuffer sb = new StringBuffer();
Random random = new Random();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics graphic = image.getGraphics();
graphic.setColor(Color.getColor("F8F8F8"));
graphic.fillRect(0, 0, width, height);
Color[] colors = new Color[] { Color.BLUE, Color.GRAY, Color.GREEN, Color.RED, Color.BLACK, Color.ORANGE,
Color.CYAN };
// 在 "画板"上生成干扰线条 ( 50 是线条个数)
for (int i = 0; i < 50; i++) {
graphic.setColor(colors[random.nextInt(colors.length)]);
final int x = random.nextInt(width);
final int y = random.nextInt(height);
final int w = random.nextInt(20);
final int h = random.nextInt(20);
final int signA = random.nextBoolean() 1 : -1;
final int signB = random.nextBoolean() 1 : -1;
graphic.drawLine(x, y, x + w * signA, y + h * signB);
}
// 在 "画板"上绘制字母
graphic.setFont(new Font("Comic Sans MS", Font.BOLD, 50));
for (int i = 0; i < 6; i++) {
final int temp = random.nextInt(26) + 97;
String s = String.valueOf((char) temp);
sb.append(s);
graphic.setColor(colors[random.nextInt(colors.length)]);
graphic.drawString(s, i * (width / 6), height - (height / 3));
}
graphic.dispose();
try {
ImageIO.write(image, imgType, output);
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
}
注意:后端配置类设置:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("*")
.allowedHeaders("*")
.maxAge(36000);
}
}
或者后端滤过器设置
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
response.setHeader("Access-Control-Allow-Credentials","true"); //这个很重要
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); //这块不能直接写 "*"
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");
前端axios应设置:
axios.defaults.withCredentials=true;