一、基础验证码
gVerify.js:
!(function (window, document) {
function GVerify(options) { //创建一个图形验证码对象,接收options对象为参数
this.options = { //默认options参数值
id: "", //容器Id
canvasId: "verifyCanvas", //canvas的ID
width: "100", //默认canvas宽度
height: "30", //默认canvas高度
type: "blend", //图形验证码默认类型blend:数字字母混合类型、number:纯数字、letter:纯字母
code: ""
}
if (Object.prototype.toString.call(options) == "[object Object]") {//判断传入参数类型
for (var i in options) { //根据传入的参数,修改默认参数值
this.options[i] = options[i];
}
} else {
this.options.id = options;
}
this.options.numArr = "0,1,2,3,4,5,6,7,8,9".split(",");
this.options.letterArr = getAllLetter();
this._init();
this.refresh();
}
GVerify.prototype = {
/**版本号**/
version: '1.0.0',
/**初始化方法**/
_init: function () {
var con = document.getElementById(this.options.id);
var canvas = document.createElement("canvas");
this.options.width = con.offsetWidth > 0 ? con.offsetWidth : "100";
this.options.height = con.offsetHeight > 0 ? con.offsetHeight : "30";
canvas.id = this.options.canvasId;
canvas.innerHTML = "您的浏览器版本不支持canvas";
canvas.width = this.options.width;
canvas.height = this.options.height;
canvas.style.cursor = "pointer";
canvas.style.marginTop = '2px';
canvas.style.width = "100px";
canvas.style.height = "38px";
con.appendChild(canvas);
var parent = this;
canvas.onclick = function () {
parent.refresh();
}
},
/**生成验证码**/
refresh: function () {
this.options.code = "";
var canvas = document.getElementById(this.options.canvasId);
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
} else {
alert('您的浏览器版本不支持canvas,请使用IE9以上版本的浏览器!');
return;
}
ctx.textBaseline = "middle";
ctx.fillStyle = randomColor(180, 240);
ctx.fillRect(0, 0, this.options.width, this.options.height);
if (this.options.type == "blend") { //判断验证码类型
var txtArr = this.options.numArr.concat(this.options.letterArr);
} else if (this.options.type == "number") {
var txtArr = this.options.numArr;
} else {
var txtArr = this.options.letterArr;
}
/**生成四个字符**/
for (var i = 1; i <= 4; i++) {
var txt = txtArr[randomNum(0, txtArr.length)];
this.options.code += txt;
ctx.font = randomNum(this.options.height / 2, this.options.height) + 'px SimHei'; //随机生成字体大小
ctx.fillStyle = randomColor(50, 160); //随机生成字体颜色
ctx.shadowOffsetX = randomNum(-3, 3);
ctx.shadowOffsetY = randomNum(-3, 3);
ctx.shadowBlur = randomNum(-3, 3);
ctx.shadowColor = "rgba(0, 0, 0, 0.3)";
var x = this.options.width / 5 * i - 5; //根据需要向左偏移5px
var y = this.options.height / 2;
var deg = randomNum(-30, 30);
/**设置旋转角度和坐标原点**/
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 < 4; i++) {
ctx.strokeStyle = randomColor(40, 180);
ctx.beginPath();
ctx.moveTo(randomNum(0, this.options.width), randomNum(0, this.options.height));
ctx.lineTo(randomNum(0, this.options.width), randomNum(0, this.options.height));
ctx.stroke();
}
/**绘制干扰点**/
for (var i = 0; i < this.options.width / 4; i++) {
ctx.fillStyle = randomColor(0, 255);
ctx.beginPath();
ctx.arc(randomNum(0, this.options.width), randomNum(0, this.options.height), 1, 0, 2 * Math.PI);
ctx.fill();
}
},
/**验证验证码**/
validate: function (code) {
var code = code.toLowerCase();
var v_code = this.options.code.toLowerCase();
if (code == v_code) {
return true;
} else {
this.refresh();
return false;
}
}
}
/**生成字母数组**/
// function getAllLetter() {
// var letterStr = "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,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";
// return letterStr.split(",");
// }
/**生成字母和汉字数组**/
function getAllLetter() {
var letterStr = "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,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";
var hanziStr = chineseUnicode; // chineseUnicode为unicode.js里面的变量
var str = letterStr + "," + hanziStr;
return str.split(",");
}
/**生成一个随机数**/
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 + ")";
}
window.GVerify = GVerify;
})(window, document);
验证码添加汉字:
/*常用汉字的 unicode 编码*/
/*如果要使验证码包含汉字,请配置*/
/*
河东太上原党三川郡南颍九阳江泗巨水鹿齐琅会邪稽汉蜀中巴陇北西地云雁沙门谷代渔右辽平海桂象邯林郸砀薛长
\u6cb3,\u4e1c,\u592a,\u4e0a,\u539f,\u515a,\u4e09,\u5ddd,\u90e1,\u5357,\u988d,\u4e5d,\u9633,\u6c5f,\u6cd7,\u5de8,\u6c34,\u9e7f,\u9f50,\u7405,\u4f1a,\u90aa,\u7a3d,\u6c49,\u8700,\u4e2d,\u5df4,\u9647,\u5317,\u897f,\u5730,\u4e91,\u96c1,\u6c99,\u95e8,\u8c37,\u4ee3,\u6e14,\u53f3,\u8fbd,\u5e73,\u6d77,\u6842,\u8c61,\u90af,\u6797,\u90f8,\u7800,\u859b,\u957f
*/
/*unicode 编码*/
//let chineseUnicode = "\u6cb3,\u4e1c,\u592a,\u4e0a,\u539f,\u515a,\u4e09,\u5ddd,\u90e1,\u5357,\u988d,\u4e5d,\u9633,\u6c5f,\u6cd7,\u5de8,\u6c34,\u9e7f,\u9f50,\u7405,\u4f1a,\u90aa,\u7a3d,\u6c49,\u8700,\u4e2d,\u5df4,\u9647,\u5317,\u897f,\u5730,\u4e91,\u96c1,\u6c99,\u95e8,\u8c37,\u4ee3,\u6e14,\u53f3,\u8fbd,\u5e73,\u6d77,\u6842,\u8c61,\u90af,\u6797,\u90f8,\u7800,\u859b,\u957f";
let chineseUnicode = "";
登录页面使用gVerify.js:
<!DOCTYPE html >
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<!--<title th:text="${#themes.code('cas.page.title')}"></title> 存在中文乱码问题,-->
<!--所以改为在messages_zh_CN.properties文件取-->
<title th:text="#{screen.login.header}">CAS Login</title>
<link rel="stylesheet" th:href="@{/themes/portal/css/element-ui.css}"/>
<link rel="stylesheet" th:href="@{/themes/portal/css/index.css}"/>
<link rel="icon" th:href="@{/themes/portal/favicon.ico}" id="icon">
<script th:src="@{/themes/portal/js/vue.js}"></script>
<script th:src="@{/themes/portal/js/vue-resource.js}"></script>
<script th:src="@{/themes/portal/js/element-ui.js}"></script>
<script th:src="@{/themes/portal/js/jsencrypt.min.js}"></script>
<script th:src="@{/themes/portal/js/gVerify.js}"></script>
<script th:src="@{/themes/portal/js/unicode.js}"></script>
<style type="text/css">
body {
padding: 0;
margin: 0;
}
</style>
</head>
<body>
<div id="app">
<div v-show="!loading" class="left-image">
<img class="login-img" th:src="@{themes/portal/images/loginimg.jpg}"/>
<img class="login-logo" th:src="@{themes/portal/images/loginlogo.png}"/>
</div>
<div class="right-form">
<el-card v-show="!loading" class="card">
<div>
<h3 class="login-title">数字化转型平台</h3>
</div>
<div>
<el-form :label-position="labelPosition" :model="ruleForm" method="post" th:object="${credential}" status-icon
:rules="rules" @submit.native.prevent
ref="ruleForm" class="demo-ruleForm">
<div th:if="${#fields.hasErrors('*')}" class="fieldHasErr">
<span th:each="err : ${#fields.errors('*')}" th:utext="${err}"
class="fieldErr" id="fieldErr"/>
</div>
<div th:if="!${#fields.hasErrors('*')}" class="fieldHasErr">
<span class="fieldErr"> </span>
</div>
<label class="control-label text-right">用户名</label>
<el-form-item prop="userDTO">
<el-input v-model.trim="ruleForm.userDTO" name="username"
prefix-icon="el-icon-user"
placeholder="用户名" ref="userNameInput"
v-no-paste autocomplete="username"
></el-input>
</el-form-item>
<label class="control-label text-right">密码</label>
<el-form-item prop="inputPass">
<el-input type="password" v-model.trim="ruleForm.inputPass" name="inputPass"
prefix-icon="el-icon-key" placeholder="密码"
v-no-paste autocomplete="current-password"
></el-input>
</el-form-item>
<el-form-item prop="pass" v-show="false">
<el-input type="password" v-model.trim="ruleForm.pass" name="password"
></el-input>
</el-form-item>
<div v-show="gVerify">
<label class="control-label text-right">验证码</label>
<el-form-item prop="captcha" id="code_input">
<el-input v-model.trim="ruleForm.captcha" name="captcha"
prefix-icon="el-icon-tickets"
placeholder="请输入验证码"
value=""
id="captcha_input"
></el-input>
</el-form-item>
<div id="v_container">
<div id="changeBtn">
<!--换一张-->
</div>
</div>
</div>
<section class="form-check" th:if="${rememberMeAuthenticationEnabled}">
<div>
<input type="checkbox"
name="rememberMe"
id="rememberMe" value="true" tabindex="5"/>
<label for="rememberMe" th:text="#{screen.rememberme.checkbox.title}">Remember Me</label>
</div>
</section>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')" class="login-submit">登 录
</el-button>
</el-form-item>
<input type="hidden" name="casServerLogin" v-model.trim="isCASServerLogin"/>
<input type="hidden" name="execution" th:value="${flowExecutionKey}"/>
<input type="hidden" name="_eventId" value="submit"/>
</el-form>
</div>
<div class="login-footer" align="center">
<span class="login-copyright">©</span> <span id="copyright">Copyright © 2024 Gener Soft. All Rights Reserved. 沪ICP备15036345号</span>
</div>
</el-card>
</div>
</div>
<div class="pre-loader" id="pre-loader">
<div class="inner one"></div>
<div class="inner two"></div>
<div class="inner three"></div>
</div>
</body>
<script th:inline="text" type="application/javascript">
function getRequestParameter() {
let url = location.search;
let requestParameter = {};
if (url.indexOf("?") !== -1) {
let strs = url.substr(1).split('&');
for (let i = 0; i < strs.length; i++) {
requestParameter[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);
}
}
return requestParameter;
}
function getRequestParameterBackTo() {
let url = location.search;
let requestParameter = {};
if (url.indexOf("?") !== -1) {
let strs = url.substr(1).split('back_to%3D');
if (strs.length > 1) {
requestParameter = unescape(strs[strs.length - 1]);
} else {
let strs = url.substr(1).split('service=');
if (strs.length > 1) {
requestParameter = unescape(strs[strs.length - 1]);
}
}
}
return requestParameter;
}
// 自定义指令 v-no-paste 密码框禁止粘贴
Vue.directive('no-paste', {
// 当绑定元素插入到DOM中
inserted: function (el) {
el.addEventListener('paste', function (event) {
event.preventDefault();
});
}
});
new Vue({
el: '#app',
data() {
let checkUser = (rule, value, callback) => {
if (!value) {
return callback(new Error('请输入用户名'))
} else if (value.length > 15) {
return callback(new Error('用户名长度错误'))
} else {
callback()
}
}
let checkPass = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入密码'))
} else if (value.length > 15) {
callback(new Error('密码长度错误'))
} else {
callback()
}
};
let checkCaptcha = (rule, value, callback) => {
if (this.gVerify) {
if (!value) {
callback(new Error('请输入验证码'))
} else {
callback()
}
} else {
callback()
}
};
return {
labelPosition: 'right',
loading: true,
loginConfig: null,
ruleForm: {
inputPass: '', // 录入的密码
pass: '', // 提交的密码
userDTO: '[[${credential.username}]]',
captcha: ''
},
rules: {
userDTO: [
{validator: checkUser, trigger: 'blur'}
],
inputPass: [
{validator: checkPass, trigger: 'blur'}
],
captcha: [
{validator: checkCaptcha, trigger: 'blur'}
]
},
isCASServerLogin: false,
publicKey: '',
gVerify: false
}
},
computed: {},
watch: {
loginConfig(nv) {
this.initLoginPage()
},
gVerify: {
immediate: true,
handler(newValue) {
if (newValue) {
document.querySelector(".login-footer").style.marginTop = 22 + 'vh';
} else {
document.querySelector(".login-footer").style.marginTop = 30 + 'vh';
}
}
}
},
created() {
let {service} = getRequestParameter();
if (service) {
this.request4LoginConfig(service, this.isCASServerLogin)
} else {
this.isCASServerLogin = true
this.request4LoginConfig(service, this.isCASServerLogin)
}
let node = document.querySelector("#fieldErr")
if (node) {
let fieldErr = node.innerText;
if (fieldErr && (fieldErr.startsWith("refreshing page..."))) {
node.className = "fieldErr1";
}
}
},
mounted() {
// 如果错误提示信息是“用户名和密码不匹配,还可以重试n次”/“您已连续输错密码n次,账号已锁定请稍后再试”/“用户名不存在”,启用图片验证码
let node = document.querySelector("#fieldErr")
if (node) {
let fieldErr = node.innerText;
if (fieldErr && (fieldErr.startsWith("用户名和密码不匹配,还可以重试") || fieldErr.startsWith("您已连续输错密码") || fieldErr.startsWith("用户名不存在"))) {
this.gVerify = true;
} else if (fieldErr && (fieldErr.startsWith("登录["))) {
// 后登录账号和已登录账号用户名不同,弹窗提示先登出已登录账号再操作登录
this.openMsgBox(fieldErr);
} else if (fieldErr && (fieldErr.startsWith("refreshing page..."))) {
// 后登录账号和已登录账号用户名相同,刷新页面即可
let search = getRequestParameterBackTo();
location.replace(search);
}
}
this.$nextTick(function () {
this.$refs.userNameInput.focus();
})
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
if (this.gVerify) {
//验证码是否正确
let oInput = document.querySelector("#captcha_input");
let res = verifyCode.validate(oInput.value);
if (!res) {
document.querySelector("#fieldErr").innerText = "验证码不正确";
this.ruleForm.captcha = '';
return false;
}
}
//Encrypt the input password with a public key
let encrypt = new JSEncrypt({default_key_size: 1024});
encrypt.setPublicKey(this.publicKey);
let encrypted = encrypt.encrypt(this.ruleForm.inputPass);
this.ruleForm.pass = encrypted;
this.ruleForm.inputPass = this.ruleForm.inputPass.replace(/./g, '*');
this.$nextTick(function () {
this.loginSystem();
})
} else {
this.$message.error('Error submit!');
return false;
}
})
},
loginSystem() {
this.$refs.ruleForm.$el.submit()
},
async logout() {
this.$http.get('[[@{/logout}]]', {}).then(resp => {
}).catch(e => {
})
},
async request4LoginConfig(service, isCASServerLogin) {
this.$http.post('[[@{/getLoginConfig}]]', {
service, isCASServerLogin
}).then(
resp => {
if (resp.body.publicKey) {
this.publicKey = resp.body.publicKey;
}
/*if (resp.body.htmlTitle) {
document.title = resp.body.htmlTitle;
}
if (resp.body.favicon) {
document.querySelector('#icon').href = resp.body.favicon;
}
if (resp.body.backgroundImg) {
let prefix = 'url('
let suffix = ') no-repeat center center'
let background = prefix + resp.body.backgroundImg + suffix
document.querySelector('.login-container').style.background = background;
document.querySelector('.login-container').style.backgroundSize = 'cover';
}
if (resp.body.copyright) {
document.querySelector('#copyright').innerText = resp.body.copyright;
}*/
// this.loginConfig值变化可以结束预加载的动画
if (resp.body.title) {
this.loginConfig = {
banner: resp.body.title
}
} else {
this.loginConfig = {
banner: 'Welcome!'
}
}
this.$nextTick(function () {
this.$refs.userNameInput.focus()
})
}).catch(e => {
setTimeout(() => {
this.loginConfig = {
banner: 'Welcome!'
}
this.$refs.userNameInput.focus()
})
})
},
initLoginPage() {
this.loading = false;
this.stopLoading()
},
stopLoading() {
const preLoader = document.querySelector('#pre-loader')
preLoader.style.display = 'none'
},
openMsgBox(msg) {
this.$confirm(msg, '确认信息', {
distinguishCancelAndClose: true,
confirmButtonText: '登出',
cancelButtonText: '取消',
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
this.logout();
instance.confirmButtonLoading = true;
instance.confirmButtonText = '登出中...';
setTimeout(() => {
done();
setTimeout(() => {
instance.confirmButtonLoading = false;
}, 100);
}, 1000);
} else {
done();
}
}
}).then(() => {
this.$message({
type: 'info',
message: '登出成功'
});
setTimeout(() => {
let search = getRequestParameterBackTo();
location.replace(search);
}, 1000)
})
}
}
})
</script>
<script type="text/javascript">
let verifyCode = new GVerify("v_container");
let oDiv = document.querySelector("#changeBtn");
//看不清,换一张
oDiv.onclick = function () {
verifyCode.refresh();
}
</script>
</html>
二、XX验证码
三、短信验证码
https://download.csdn.net/download/qq_43542296/89064721