温馨提示,前端代码是基于
vue
的,因此你可能需要了解vue
才能看懂前端代码。因为代码写的比较仓促,风格可能不太好,如因代码引起不适,请关闭浏览器。:dog:(狗头保命)
图片验证码
图片验证码我是用 EasyCaptcha
生成的,如需更深入了解,点击 EasyCaptcha 跳转。
maven 项目导入
<dependencies>
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
</dependencies>
使用 SpecCaptcha 生成 PNG 格式验证码
如需生成
gif
类型、中文
类型等,请查看文档。
@GetMapping("getCode")
public void getCode(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 设置宽、高、位数
SpecCaptcha specCaptcha = new SpecCaptcha(200, 40, 5);
String text = specCaptcha.text();
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.opsForValue().set("VERIFY_CODE", text, 5 * 60, TimeUnit.SECONDS);
specCaptcha.out(response.getOutputStream());
}
这里使用 redis
缓存的目的主要是因为我这个项目是前后端分离的,将验证码存储在 redis
中便于验证码的校,前后端分离项目建议不要存储在 session
中,当然也可以往 session
中存。
前端请求
getCode() {
let url = BACKEND_IP_AND_PORT + "/captcha/getCode";
let xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.onload = function () {
if (this.status === 200) {
let res = this.response;
$("#code").attr("src", window.URL.createObjectURL(res));
}
};
xhr.send();
}
因为后台传回的是流的形式,因此需要将其转为 blob
格式再赋予 img
标签。
效果展示
完成代码及登录页面展示
下面轮到代码出场了。
后端代码
import com.wf.captcha.SpecCaptcha;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("captcha")
@CrossOrigin
public class CaptchaController {
private final static Logger logger = LoggerFactory.getLogger(CaptchaController.class);
private final RedisTemplate redisTemplate;
@Autowired
public CaptchaController(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@GetMapping("getCode")
public void getCode(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 设置宽、高、位数
SpecCaptcha specCaptcha = new SpecCaptcha(200, 40, 5);
String text = specCaptcha.text();
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.opsForValue().set("VERIFY_CODE", text, 5 * 60, TimeUnit.SECONDS);
specCaptcha.out(response.getOutputStream());
}
}
前端代码
<template>
<div class="main">
<div class="header box">
<div class="header-container">
<div class="header-title">XXX系统</div>
<div class="header-img"></div>
</div>
</div>
<div class="center-container box">
<div class="center-left"></div>
<div class="center-right">
<div class="login-form"></div>
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on"
label-position="left">
<el-form-item prop="username">
<el-input ref="username" v-model="loginForm.username" placeholder="请输入用户名" name="username" type="text"
tabindex="1" auto-complete="on" prefix-icon="el-icon-user-solid"/>
</el-form-item>
<el-form-item prop="password">
<el-input :key="passwordType" ref="password" v-model="loginForm.password" :type="passwordType"
placeholder="请输入密码" name="password" tabindex="2" auto-complete="on"
@keyup.enter.native="handleLogin" prefix-icon="el-icon-monitor"/>
</el-form-item>
<el-checkbox v-model="checked" style="color: white;" @change="showPwd">显示密码</el-checkbox>
<div class="box">
<div class="verify-code">
<el-input v-model="verifyCode" placeholder="不区分大小写"></el-input>
</div>
<div class="verify-img">
<div class="img-code">
<!--<img src="@/assets/images/code.png" alt="二维码">-->
<img :src="getCode" alt="二维码" id="code" @click="getCode">
</div>
</div>
</div>
<el-button :loading="loading" type="primary" style="width: 100%; margin-top: 10px;"
@click.native.prevent="handleLogin">登录
</el-button>
</el-form>
</div>
</div>
</div>
</template>
<script>
// ...
// 省略部分代码
export default {
name: 'Login',
mounted() {
this.getCode()
},
methods: {
getCode() {
let url = BACKEND_IP_AND_PORT + "/captcha/getCode";
let xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.onload = function () {
if (this.status === 200) {
let res = this.response;
$("#code").attr("src", window.URL.createObjectURL(res));
}
};
xhr.send();
},
showPwd() {
if (this.passwordType === 'password') {
this.passwordType = ''
} else {
this.passwordType = 'password'
}
this.$nextTick(() => {
this.$refs.password.focus()
})
},
// 登录之类的代码...
},
}
</script>
<style scoped>
.img-code {
margin: 5px 0 0 5px;
width: 200px;
height: 40px;
}
.verify-code {
width: 40%;
height: 50px;
margin-top: 5px;
}
.verify-img {
width: 60%;
height: 50px;
}
.main {
width: 100%;
height: 100%;
background: url("../../../public/images/bj.jpg");
}
.header {
width: 1920px;
height: 180px;
}
.box {
display: -webkit-flex;
display: flex;
justify-content: center;
}
.header-container {
/*border: 1px solid pink;*/
}
.header-title {
text-align: center;
color: #ffffff;
font-size: 32px;
line-height: 84px;
}
.header-img {
background: url("../../../public/images/head.gif") no-repeat center center;
background-size: 100%;
height: 96px;
width: 960px;
}
.center-container {
width: 1920px;
height: 464px;
}
.center-left {
width: 491px;
height: 464px;
background: url("../../../public/images/d.png") no-repeat;
background-size: 100% 100%;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
.center-container .center-left {
-webkit-transform: rotate(360deg);
animation: rotation 15s linear infinite;
-moz-animation: rotation 15s linear infinite;
-webkit-animation: rotation 15s linear infinite;
-o-animation: rotation 15s linear infinite;
}
.center-right {
width: 432px;
height: 464px;
background: url("../../../public/images/e.png") no-repeat;
background-size: 100% 100%;
margin-left: 200px;
}
.login-form {
width: 80%;
margin: 150px 0 0 10%;
}
</style>
---------------------------------分割线------------------------------------
应简友要求,把背景图片上传分享一下:
链接:https://pan.baidu.com/s/1sEJpDXKJyNggjm_5H8EYRA
提取码:mugu