前言
在写登录页面时,看很多的项目都会在登录中加入验证码这一功能,那就让我们来看看如何实现
一、导入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
二、安装redis
菜鸟教程 - redis安装 我是直接安装的windows的,安装完成之后,把它启动了
三、编写验证码配置
下面这一段全部复制了粘贴进代码就行了
package org.xjj.xjj.config.kaptcha;
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;
@Configuration
public class KaptchaConfig {
@Bean
public DefaultKaptcha getDefaultKaptcha() {
DefaultKaptcha dk = new DefaultKaptcha();
Properties properties = new Properties();
properties.setProperty("kaptcha.border", "yes");
properties.setProperty("kaptcha.border.color", "105,179,90");
properties.setProperty("kaptcha.textproducer.font.color", "red");
properties.setProperty("kaptcha.image.width", "110");
properties.setProperty("kaptcha.image.height", "40");
properties.setProperty("kaptcha.textproducer.font.size", "30");
properties.setProperty("kaptcha.session.key", "code");
properties.setProperty("kaptcha.textproducer.char.length", "4");
properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
Config config = new Config(properties);
dk.setConfig(config);
return dk;
}
}
四、编写redis的工具类
package org.xjj.xjj.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtils {
@Autowired
public RedisTemplate redisTemplate;
public <T> void set(String key, T value){
redisTemplate.opsForValue().set(key, value);
}
public <T> void set(String key, T value, int timeOut, TimeUnit timeUnit){
redisTemplate.opsForValue().set(key, value, timeOut, timeUnit);
}
public <T> T get(String key){
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
public boolean del(String key){
return redisTemplate.delete(key);
}
}
五、编写UUID的工具类
package org.xjj.xjj.utils;
import java.util.UUID;
public class IdUtils {
public static String randomUUID(){
return UUID.randomUUID().toString();
}
public static String simpleUUID(){
return UUID.randomUUID().toString().replace("-","");
}
}
六、编写验证码的常量类
package org.xjj.xjj.constant;
public class CaptchaConstants {
public static final int CAPTCHA_EXPIRATION = 2;
public static final String CAPTCHA_KEY_PREFIX = "captcha_codes:";
}
七、编写Base64Utils类
package org.xjj.xjj.utils;
public final class Base64Utils {
static private final int BASELENGTH = 128;
static private final int LOOKUPLENGTH = 64;
static private final int TWENTYFOURBITGROUP = 24;
static private final int EIGHTBIT = 8;
static private final int SIXTEENBIT = 16;
static private final int FOURBYTE = 4;
static private final int SIGN = -128;
static private final char PAD = '=';
static final private byte[] base64Alphabet = new byte[BASELENGTH];
static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
static
{
for (int i = 0; i < BASELENGTH; ++i)
{
base64Alphabet[i] = -1;
}
for (int i = 'Z'; i >= 'A'; i--)
{
base64Alphabet[i] = (byte) (i - 'A');
}
for (int i = 'z'; i >= 'a'; i--)
{
base64Alphabet[i] = (byte) (i - 'a' + 26);
}
for (int i = '9'; i >= '0'; i--)
{
base64Alphabet[i] = (byte) (i - '0' + 52);
}
base64Alphabet['+'] = 62;
base64Alphabet['/'] = 63;
for (int i = 0; i <= 25; i++)
{
lookUpBase64Alphabet[i] = (char) ('A' + i);
}
for (int i = 26, j = 0; i <= 51; i++, j++)
{
lookUpBase64Alphabet[i] = (char) ('a' + j);
}
for (int i = 52, j = 0; i <= 61; i++, j++)
{
lookUpBase64Alphabet[i] = (char) ('0' + j);
}
lookUpBase64Alphabet[62] = (char) '+';
lookUpBase64Alphabet[63] = (char) '/';
}
public static String encode(byte[] binaryData)
{
if (binaryData == null)
{
return null;
}
int lengthDataBits = binaryData.length * EIGHTBIT;
if (lengthDataBits == 0)
{
return "";
}
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
char encodedData[] = null;
encodedData = new char[numberQuartet * 4];
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
int encodedIndex = 0;
int dataIndex = 0;
for (int i = 0; i < numberTriplets; i++)
{
b1 = binaryData[dataIndex++];
b2 = binaryData[dataIndex++];
b3 = binaryData[dataIndex++];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
}
if (fewerThan24bits == EIGHTBIT)
{
b1 = binaryData[dataIndex];
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
encodedData[encodedIndex++] = PAD;
encodedData[encodedIndex++] = PAD;
}
else if (fewerThan24bits == SIXTEENBIT)
{
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
encodedData[encodedIndex++] = PAD;
}
return new String(encodedData);
}
}
八、编写获取验证码控制器
package org.xjj.xjj.controller;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.xjj.xjj.constant.CaptchaConstants;
import org.xjj.xjj.utils.IdUtils;
import org.xjj.xjj.utils.RedisUtils;
import org.xjj.xjj.entity.base.AjaxResult;
import org.xjj.xjj.utils.Base64Utils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.concurrent.TimeUnit;
@RestController
public class KaptchaController {
@Autowired
private DefaultKaptcha defaultKaptcha;
@Autowired
private RedisUtils redisCache;
@GetMapping("/captchaImage")
public AjaxResult captchaImage() {
AjaxResult ajax = AjaxResult.success();
ByteArrayOutputStream out = new ByteArrayOutputStream();
String createText = "";
try {
createText = defaultKaptcha.createText();
BufferedImage bi = defaultKaptcha.createImage(createText);
ImageIO.write(bi, "jpg", out);
} catch (Exception e) {
return AjaxResult.error(e.getMessage());
}
byte[] captcha = out.toByteArray();
String uuid = IdUtils.simpleUUID();
String captchaKey = CaptchaConstants.CAPTCHA_KEY_PREFIX + uuid;
redisCache.set(captchaKey, createText, CaptchaConstants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
ajax.put("uuid", uuid);
ajax.put("img", Base64Utils.encode(captcha));
return ajax;
}
}
九、vue前端获取验证码
<template>
<div class="container">
<div id="login-div" v-loading="loading">
<p id="login-title">{{ loginTitle }}</p>
<el-form
:model="loginForm"
:rules="loginRules"
id="login-form"
ref="ruleForm"
>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
placeholder="用户名"
@keyup.enter.native="submitForm('ruleForm')"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
show-password
placeholder="密码"
@keyup.enter.native="submitForm('ruleForm')"
></el-input>
</el-form-item>
<el-form-item prop="code">
<el-row :gutter="10">
<el-col :span="12">
<el-input
v-model="loginForm.code"
placeholder="验证码"
@keyup.enter.native="submitForm('ruleForm')"
></el-input>
</el-col>
<el-col :span="12" style="height: 40px;">
<el-image
:src="codeUrl"
@click="getCode()"
style="width: 100%; height: 40px; cursor: pointer;"
></el-image>
</el-col>
</el-row>
</el-form-item>
<el-form-item style="margin-bottom: 10px">
<el-checkbox-group v-model="loginForm.rememberMe">
<el-checkbox
label="记住我"
name="rememberMe"
></el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item>
<el-button
id="login-submit-button"
type="primary"
@click="submitForm('ruleForm')"
>登录</el-button
>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { getCodeImgAPI } from "@/api/login.js";
import Cookies from "js-cookie";
import { saveLoginData, removeLoginData } from "@/utils/loginUtils.js";
import { encrypt, decrypt } from "@/utils/passwordUtils.js";
export default {
data() {
return {
loginTitle: "闲简居",
loginForm: {
username: "",
password: "",
rememberMe: false,
code: "",
uuid: ""
},
loginRules: {
username: [
{ required: true, message: "请输入用户名", trigger: "blur" }
],
password: [
{ required: true, message: "请输入密码", trigger: "blur" }
],
code: [
{ required: true, message: "请输入验证码", trigger: "blur" }
]
},
codeUrl: "",
loading: false
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate(valid => {
if (valid) {
this.loading = true;
if (this.loginForm.rememberMe) {
let username = this.loginForm.username;
let password = encrypt(this.loginForm.password);
let rememberMe = this.loginForm.rememberMe;
saveLoginData(username, password, rememberMe);
} else {
removeLoginData();
}
this.$store.dispatch("user/login", this.loginForm)
.then(() => {
this.$router.push("/");
})
.catch(() => {
this.loading = false;
this.getCode();
});
}
});
},
getCode() {
getCodeImgAPI().then(res => {
this.codeUrl = "data:image/gif;base64," + res.data.img;
this.loginForm.uuid = res.data.uuid;
});
},
getCookie(){
const username = Cookies.get("username");
const password = Cookies.get("password");
const rememberMe = Cookies.get('rememberMe');
this.loginForm = {
username: username === undefined ? this.loginForm.username : username,
password: password === undefined ? this.loginForm.password : decrypt(password),
rememberMe: rememberMe === undefined ? this.loginForm.rememberMe : Boolean(rememberMe)
};
}
},
created() {
this.getCode();
this.getCookie();
}
};
</script>
<style scoped>
#login-div {
width: 300px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 20px;
background-color: #ffffff;
border-radius: 10px;
}
#login-title {
font-size: 25px;
font-weight: bold;
text-align: center;
height: 50px;
line-height: 50px;
color: #666666;
}
#login-submit-button {
width: 100%;
}
</style>
十、结果图
总结
Base64Utils类是从若依项目拷贝过来的,这个类我也不知道干什么,需要大家伙去了解,其它的配置类就很容易理解了。
大家看到我的配置中并没有设置redis的配置,那是因为SpringBoot已经默认配置好了。
|