要实现验证码功能需要明白:
前端:通过img标签获得图片
后端:首先根据前端请求生成图片,然后前端再次处理前端的登录请求
此工具类用于生成验证码图片
package com.pp.educational_management_system.config;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
public class VerificationCode {
private int width = 100;// 生成验证码图片的宽度
private int height = 30;// 生成验证码图片的高度
private String[] fontNames = { "宋体", "楷体", "隶书", "微软雅黑" };
private Color bgColor = new Color(255, 255, 255);// 定义验证码图片的背景颜色为白色
private Random random = new Random();
private String codes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private String text;// 记录随机字符串
/**
* 获取一个随意颜色
*
* @return
*/
private Color randomColor() {
int red = random.nextInt(150);
int green = random.nextInt(150);
int blue = random.nextInt(150);
return new Color(red, green, blue);
}
/**
* 获取一个随机字体
*
* @return
*/
private Font randomFont() {
String name = fontNames[random.nextInt(fontNames.length)];
int style = random.nextInt(4);
int size = random.nextInt(5) + 24;
return new Font(name, style, size);
}
/**
* 获取一个随机字符
*
* @return
*/
private char randomChar() {
return codes.charAt(random.nextInt(codes.length()));
}
/**
* 创建一个空白的BufferedImage对象
*
* @return
*/
private BufferedImage createImage() {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) image.getGraphics();
g2.setColor(bgColor);// 设置验证码图片的背景颜色
g2.fillRect(0, 0, width, height);
return image;
}
public BufferedImage getImage() {
BufferedImage image = createImage();
Graphics2D g2 = (Graphics2D) image.getGraphics();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 4; i++) {
String s = randomChar() + "";
sb.append(s);
g2.setColor(randomColor());
g2.setFont(randomFont());
float x = i * width * 1.0f / 4;
g2.drawString(s, x, height - 8);
}
this.text = sb.toString();
drawLine(image);
return image;
}
/**
* 绘制干扰线
*
* @param image
*/
private void drawLine(BufferedImage image) {
Graphics2D g2 = (Graphics2D) image.getGraphics();
int num = 5;
for (int i = 0; i < num; i++) {
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
int x2 = random.nextInt(width);
int y2 = random.nextInt(height);
g2.setColor(randomColor());
g2.setStroke(new BasicStroke(1.5f));
g2.drawLine(x1, y1, x2, y2);
}
}
public String getText() {
return text;
}
public static void output(BufferedImage image, OutputStream out) throws IOException {
ImageIO.write(image, "JPEG", out);
}
}
前端:
<img class="verifyCodeImg" :src="imgUrl" @click="resetImg">
imgUrl在数据区定义(new Date()生成随机事件用于更换验证码,防止浏览器缓存):
imgUrl:"http://localhost:9000/home/verifyCode?time="+new Date(),
resetImg方法用于点击更换验证码 图片
resetImg(){
this.imgUrl = "http://localhost:9000/home/verifyCode?time="+new Date();
},
后端:
生成验证码图片返回到前端,并且将验证码的code存在session中
@GetMapping("/verifyCode")
public void verifyCode(HttpServletRequest request, HttpServletResponse resp) throws IOException {
VerificationCode code = new VerificationCode();
BufferedImage image = code.getImage();
String text = code.getText();
HttpSession session = request.getSession(true);
session.setAttribute("verify_code", text);
VerificationCode.output(image,resp.getOutputStream());
}
(前后端分离项目此处有坑!!!!!!)
前端获取验证码的时候将code存在了session中,在登录验证的时候只需要将用户输入的验证码code和session中的code比较就行了。但是因为是前后端分离的项目,每次前端的ajax请求都是新的,每次请求的sessionID都不一样,所以每次使用的session不是同一个,以至于登录时session里面的值为null。
解决方式
在vue的main.js中配置axios,让每次请求都带上浏览器的cookie,这样后端使用的session就是同一个了。
axios.defaults.withCredentials = true
前端:
<template>
<div class="login_container">
<img :src="imgSrc" width="100%" height="100%" />
<!--登录块-->
<div class="login_box">
<!--表单区域-->
<el-form
ref="loginFromRef"
:rules="loginRules"
:model="loginFrom"
class="login_from"
label-width="0px"
>
<h3 class="headline">教学管理系统</h3>
<!--用户名-->
<el-form-item prop="username">
<el-input
v-model="loginFrom.username"
prefix-icon="iconfont icon-denglu"
placeholder="用户名"
></el-input>
</el-form-item>
<!--密码-->
<el-form-item prop="password">
<el-input
v-model="loginFrom.password"
prefix-icon="iconfont icon-mima"
type="password"
placeholder="密码"
></el-input>
</el-form-item>
<el-form-item prop="verifyCode">
<el-col :span="16">
<el-input
v-model="loginFrom.verifyCode"
prefix-icon="el-icon-message"
placeholder="验证码"
class="verifyCode"
></el-input>
</el-col>
<el-col :span="8">
<img class="verifyCodeImg" :src="imgUrl" @click="resetImg">
</el-col>
</el-form-item>
<!--按钮-->
<el-form-item class="btn">
<el-button type="primary" @click="login">登录</el-button>
<!-- <el-button type="info" @click="resetLoginForm">重置</el-button> -->
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data() {
return {
//表单数据
imgUrl:"http://localhost:9000/home/verifyCode?time="+new Date(),
loginFrom: {
username: "admin",
password: "123456",
verifyCode:"",
},
//验证对象
loginRules: {
//校验用户名
username: [
{ required: true, message: "请输入用户名称", trigger: "blur" }, //必填项验证
{
min: 5,
max: 12,
message: "长度在 5 到 12 个字符",
trigger: "blur",
}, //验证长度
],
//校验密码
password: [
{ required: true, message: "请输入用户密码", trigger: "blur" }, //必填项验证
{
min: 6,
max: 10,
message: "长度在 6 到 10 个字符",
trigger: "blur",
}, //验证长度
],
},
imgSrc: require("../assets/bg.png"),
};
},
methods: {
resetLoginForm() {
//重置表单内容
this.$refs.loginFromRef.resetFields();
},
resetImg(){
this.imgUrl = "http://localhost:9000/home/verifyCode?time="+new Date();
},
login() {
//登录请求
this.$refs.loginFromRef.validate(async (validate) => {
//判断是否验证成功
if (!validate) return;
const { data: res } = await this.$http.post(
"home/login",
this.loginFrom
);
if (res.status == "200") {
this.$message.success("登陆成功");
window.sessionStorage.setItem("user", res.id);
const {data:res1} = await this.$http.get("menus",{params:{
id:window.sessionStorage.getItem("user"),
}});
window.sessionStorage.setItem("menu",window.JSON.stringify(res1.menus));
window.sessionStorage.setItem("token", res.token);
//跳转页面
this.$router.push({ path: "/home" });
} else if(res.status == "400"){
this.$message.error("用户名或密码错误");
}else if(res.status == "404"){
this.$message.error("账号未激活,请联系管理员");
}else{
this.$message.error("验证码输入错误");
this.imgUrl = "http://localhost:9000/home/verifyCode?time="+new Date();
}
});
},
},
};
</script>
<style lang="less" scoped>
.login_container {
background-color: #2b4b6b;
height: 100%;
}
.login_box {
background: rgba(78, 102, 112, 0.1);
width: 450px;
height: 330px;
border-radius: 5px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.btn {
width: 100%;
}
.login_from {
position: absolute;
bottom: 0%;
width: 100%;
padding: 0 10px;
box-sizing: border-box;
.verifyCode {
width:"60%",
}
.verifyCodeImg {
margin-top: 5px;
margin-left: 10px;
height: 30px;
width:"60%",
}
}
.el-button {
margin-left: 10%;
width: 80%;
}
.el-form-item {
margin-left: 10%;
width: 80%;
}
.headline {
font-size: 20px;
color: white;
margin-left: 35%;
}
</style>
后端:
package com.pp.educational_management_system.controller;
import com.alibaba.fastjson.JSON;
import com.pp.educational_management_system.anotation.Log;
import com.pp.educational_management_system.config.VerificationCode;
import com.pp.educational_management_system.domain.User;
import com.pp.educational_management_system.service.impl.ShiroService;
import com.pp.educational_management_system.utils.MD5Utils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.ServletRequest;
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.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/home")
public class ShiroController {
public static User user;
private final ShiroService shiroService;
public ShiroController(ShiroService shiroService) {
this.shiroService = shiroService;
}
/**
* 登录
*/
@Log("登录")
@RequestMapping("/login")
public String login(@RequestBody User us, HttpServletRequest request, HttpServletResponse resp) {
Map<String, Object> result = new HashMap<>();
if(request.getSession(true).getAttribute("verify_code")==null||!us.getVerifyCode().toUpperCase().equals(request.getSession(true).getAttribute("verify_code").toString().toUpperCase())){
result.put("status", 300);
result.put("msg", "验证码错误");
return JSON.toJSONString(result);
}
String username = us.getUsername();
String password = us.getPassword();
//用户信息
User user = shiroService.getUserByName(username);
//账号不存在、密码错误
if (user == null || !user.getPassword().equals(MD5Utils.generatePassword(password))) {
result.put("status", 400);
result.put("msg", "账号或密码有误");
}else if(user.getState()==0){
result.put("status", 404);
result.put("msg", "账号未激活,请联系管理员");
}else {
this.user = user;
//生成token,并保存到数据库
result = shiroService.createToken(user.getId());
result.put("status", 200);
result.put("msg", "登陆成功");
result.put("id",user.getId());
}
String res = JSON.toJSONString(result);
return res;
}
/**
* 得到验证码图片
* @param request
* @param resp
* @throws IOException
*/
@GetMapping("/verifyCode")
public void verifyCode(HttpServletRequest request, HttpServletResponse resp) throws IOException {
VerificationCode code = new VerificationCode();
BufferedImage image = code.getImage();
String text = code.getText();
HttpSession session = request.getSession(true);
session.setAttribute("verify_code", text);
VerificationCode.output(image,resp.getOutputStream());
}
/**
* 退出
*/
@RequestMapping("/logout")
public Map<String, Object> logout(@RequestHeader("token")String token) {
Map<String, Object> result = new HashMap<>();
shiroService.logout(token);
result.put("status", 200);
result.put("msg", "您已安全退出系统");
return result;
}
}