You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
434 lines
15 KiB
Vue
434 lines
15 KiB
Vue
<template>
|
|
<div>
|
|
<el-container class="common-layout">
|
|
|
|
<el-row style="width: 100%; display: flex; justify-content: center;">
|
|
<el-col :span="9" class="left">
|
|
|
|
</el-col>
|
|
<el-col :span="15" class="right">
|
|
<div class="right_top">
|
|
<div>
|
|
<el-switch v-model="isDark" inline-prompt active-color="var(--el-fill-color-dark)"
|
|
inactive-color="var(--el-color-primary)" active-action-icon="Moon"
|
|
inactive-action-icon="Sunny" @change="toggleDark" />
|
|
</div>
|
|
</div>
|
|
<el-form v-if="!UseErweima && !UseUkey" style="width: 400px;" ref="ruleFormRef" status-icon class="demo-ruleForm">
|
|
<el-form-item>
|
|
<span style="font-size: 22px;">登录您的账户</span>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-input v-model.number="username" :prefix-icon="User" placeholder="用户名" size="large" />
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-input v-model="pwd" type="password" autocomplete="off" placeholder="密码" size="large"
|
|
:prefix-icon="Lock" />
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button style="width: 100%;" type="primary" @click="login(ruleFormRef)"
|
|
size="large">登录</el-button>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button style="width: 48%;" @click="usePhone()" size="large"
|
|
:icon="Iphone">手机协同签名登录</el-button>
|
|
<el-button style="width: 48%;" @click="useUkey()" size="large"
|
|
:icon="Key" type="danger">证书登录</el-button>
|
|
</el-form-item>
|
|
<div style="height: 160px;"></div>
|
|
</el-form>
|
|
|
|
<el-form v-if="UseErweima" style="width: 400px;" status-icon class="demo-ruleForm erweima">
|
|
<el-form-item>
|
|
<span style="font-size: 22px;">请扫描二维码登录</span>
|
|
</el-form-item>
|
|
<div class="erweima_tu" v-loading="erweima_loading">
|
|
<canvas v-if="seconds>0" id="canvas" width="300" height="300" style="min-height: 300px;min-width: 300px;"></canvas>
|
|
<div v-else class="timeout" @click="refresh()">{{erweimaErrMsg}}
|
|
<div><RefreshRight style="height: 60px;width: 60px;"></RefreshRight></div>
|
|
</div>
|
|
|
|
</div>
|
|
<el-form-item>
|
|
<el-button style="width: 100%;" @click="UseErweima=false;seconds=0" size="large">返回账号密码</el-button>
|
|
</el-form-item>
|
|
<div style="height: 160px;"></div>
|
|
</el-form>
|
|
<el-form v-if="UseUkey" id="form_xtx" name="form_xtx" style="width: 400px;" status-icon class="demo-ruleForm erweima">
|
|
<el-form-item>
|
|
<span style="font-size: 22px;">证书登录</span>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-select v-model="selectedZhengShuId" placeholder="请选择证书" size="large" style="width: 100%;">
|
|
<el-option v-for="(item,index) in ZhengShuList" :key="index" :label="item.name"
|
|
:value="item.value" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-input v-model="uke_pwd" type="password" autocomplete="off" placeholder="密码" size="large"
|
|
:prefix-icon="Lock" />
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button style="width: 100%;" @click="ukeyLogin()" type="danger" size="large">登录</el-button>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button style="width: 100%;" @click="UseUkey=false;" size="large">返回账号密码</el-button>
|
|
</el-form-item>
|
|
<div style="height: 160px;"></div>
|
|
</el-form>
|
|
|
|
<div class="right_bottom"></div>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
</el-container>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import {
|
|
Login,addSignJob,XTSignCheckNotify,UkeyGetServeInfo,UkeyUserInfoLogin
|
|
} from "@/api/api.js";
|
|
import QRCode from 'qrcode'
|
|
import {
|
|
ElMessage, ElMessageBox
|
|
} from 'element-plus'
|
|
|
|
import {
|
|
ref
|
|
} from 'vue'
|
|
import {
|
|
Lock,
|
|
User,
|
|
Iphone,RefreshRight,Key
|
|
} from '@element-plus/icons-vue'
|
|
import {
|
|
useToggle
|
|
} from '@vueuse/shared'
|
|
import {
|
|
useDark
|
|
} from "@vueuse/core";
|
|
import CryptoJS from 'crypto-js';
|
|
|
|
const erweima_loading=ref(false)
|
|
const UseErweima=ref(false) //是否使用二维码
|
|
const UseUkey=ref(false) //是否使用Ukey
|
|
const isDark = useDark()
|
|
const toggleDark = () => useToggle(isDark)
|
|
|
|
let username = ref('')
|
|
let pwd = ref('')
|
|
const hunxiao=(str)=>{
|
|
let originalString =str
|
|
var charMap = {
|
|
'a': 'z', 'b': 'y', 'c': 'x', 'd': 'w', 'e': 'v',
|
|
'f': 'u', 'g': 't', 'h': 's', 'i': 'r', 'j': 'q',
|
|
'k': 'p', 'l': 'o', 'm': 'n', 'n': 'm', 'o': 'l',
|
|
'p': 'k', 'q': 'j', 'r': 'i', 's': 'h', 't': 'g',
|
|
'u': 'f', 'v': 'e', 'w': 'd', 'x': 'c', 'y': 'b',
|
|
'z': 'a',
|
|
'0': '@', '1': '!', '2': '#', '3': '$', '4': '%',
|
|
'5': '^', '6': '&', '7': '*', '8': '(', '9': ')'
|
|
};
|
|
var mappedString = originalString.split('').map(char => charMap[char] || char).join('');
|
|
|
|
// 第二步:反转字符串
|
|
var reversedString = mappedString.split('').reverse().join('');
|
|
|
|
// 第三步:添加固定前缀和后缀
|
|
var prefixedSuffixString = 'a5331_' + reversedString + '_a454d';
|
|
|
|
// 第四步:使用 btoa 函数执行 Base64 编码
|
|
var encodedString = btoa(prefixedSuffixString);
|
|
return encodedString;
|
|
}
|
|
let login = () => { //登录
|
|
if (username.value == '' || pwd.value == '') return ElMessage.error('用户名和密码不能为空')
|
|
|
|
let data = { //传参
|
|
username: username.value,
|
|
password:pwd.value ,
|
|
}
|
|
//调用登录接口
|
|
Login(data).then(res => {
|
|
|
|
if (res.data.status == 'ok') {
|
|
|
|
sessionStorage.setItem('token', res.data.token);
|
|
sessionStorage.setItem('refreshToken', res.data.refresh_token);
|
|
// sessionStorage.setItem('tk', JSON.stringify(res.data.tk));
|
|
var token = sessionStorage.getItem('token');
|
|
if (token == res.data.token) {
|
|
if(res.data.pwd_default != undefined && res.data.pwd_default==true){
|
|
ElMessageBox.alert('请到"个人中心"修改您的密码', '提示', {
|
|
confirmButtonText: '知道了',
|
|
callback:action=> {
|
|
window.location.href = "./#/dashboard"
|
|
}
|
|
})
|
|
}else{
|
|
window.location.href = "./#/dashboard"
|
|
}
|
|
|
|
}
|
|
} else {
|
|
ElMessage.error(res.data.msg)
|
|
|
|
}
|
|
})
|
|
|
|
}
|
|
const refresh=()=>{
|
|
usePhone()
|
|
}
|
|
let erweimaErrMsg=ref('扫描超时,点击刷新');
|
|
let signJobId=ref('');////签名任务id
|
|
let qrcode=ref('');
|
|
//使用二维码登录
|
|
const usePhone=()=>{
|
|
clearInterval(timer);
|
|
signJobId.value=''
|
|
qrcode.value=''
|
|
seconds.value=timelength
|
|
|
|
erweima_loading.value=true
|
|
UseErweima.value=true
|
|
addSignJob({}).then(res => {
|
|
|
|
if(res.status){
|
|
qrcode.value = JSON.stringify(JSON.parse(res.data.qrCode));
|
|
signJobId.value=res.data.signDataId;
|
|
console.log(qrcode.value )
|
|
var canvas = document.getElementById("canvas");
|
|
// 调用函数去生成二维码,参数依次为:二维码的容器、要生成的内容、回调函数
|
|
QRCode.toCanvas(canvas,qrcode.value, function(error) {
|
|
if (error) {
|
|
console.error(error);
|
|
} else {
|
|
erweima_loading.value=false
|
|
start_time()
|
|
console.log("success!");
|
|
}
|
|
})
|
|
|
|
}else{
|
|
ElMessage.error(res.msg)
|
|
|
|
}
|
|
})
|
|
}
|
|
//定时60秒倒计时
|
|
let timelength=60
|
|
let seconds = ref(0);
|
|
seconds.value=timelength
|
|
let pinlv=5 //频率/秒
|
|
let timer
|
|
const start_time=()=>{
|
|
timer = setInterval(CheckNotify, pinlv*1000);
|
|
}
|
|
|
|
//检查扫码回调结果
|
|
const CheckNotify=()=>{
|
|
if (seconds.value <= 0) {
|
|
console.log("倒计时结束!");
|
|
clearInterval(timer);
|
|
} else {
|
|
console.log(seconds.value + "秒");
|
|
XTSignCheckNotify({signJobId:signJobId.value}).then(res=>{
|
|
if(res.msg !='暂未回调'){
|
|
//如果查到了,则结束定时器
|
|
clearInterval(timer);
|
|
if(res.status){
|
|
sessionStorage.setItem('token', res.data.token);
|
|
sessionStorage.setItem('refreshToken', res.data.refresh_token);
|
|
// sessionStorage.setItem('tk', JSON.stringify(res.data.tk));
|
|
var token = sessionStorage.getItem('token');
|
|
if (token == res.data.token) {
|
|
window.location.href = "./#/dashboard"
|
|
}
|
|
}else{
|
|
ElMessage.error(res.msg)
|
|
erweimaErrMsg.value=res.msg
|
|
seconds.value=0
|
|
}
|
|
}
|
|
|
|
})
|
|
seconds.value=seconds.value-pinlv;
|
|
}
|
|
|
|
}
|
|
|
|
//证书--------------------------------------------------------------
|
|
|
|
let ZhengShuList = ref('');
|
|
let selectedZhengShuId = ref(''); //选中的id
|
|
let uke_pwd=ref('');
|
|
const useUkey= ()=>{
|
|
UseUkey.value=true
|
|
let list = xtxsync.SOF_GetUserList()
|
|
console.log('证书列表 SOF_GetVersion', list) //获取证书列表
|
|
const arr = list.split('&&&').slice(0, -1);
|
|
ZhengShuList.value = arr.map(item => {
|
|
const [name, value] = item.split('||');
|
|
return {
|
|
name,
|
|
value
|
|
};
|
|
});
|
|
UkeyGetServeInfoAction()
|
|
|
|
}
|
|
let serverInfo=ref('');
|
|
const UkeyGetServeInfoAction=()=>{
|
|
UkeyGetServeInfo().then(res=>{
|
|
if(res.status){
|
|
serverInfo.value=res.data.serve_info
|
|
if(serverInfo.value.random && serverInfo.value.signData && serverInfo.value.serverCert){
|
|
return true
|
|
}else{
|
|
return ElMessage.error('获取服务器信息失败')
|
|
}
|
|
}
|
|
|
|
})
|
|
}
|
|
const ukeyLogin=()=>{
|
|
if(selectedZhengShuId.value==''){
|
|
return ElMessage.error('请选择证书')
|
|
}
|
|
if(uke_pwd.value==''){
|
|
return ElMessage.error('请输入证书密码')
|
|
}
|
|
console.log(selectedZhengShuId.value);
|
|
//获取服务器随机数
|
|
let randomStr=xtxsync.GenerateRandom(16);
|
|
console.log(randomStr);
|
|
//服务器对随机数签名
|
|
let randomStrSign=xtxsync.SignedData(selectedZhengShuId.value,randomStr);
|
|
console.log(randomStrSign);
|
|
|
|
//----自己获取的
|
|
//var strServerSignedData="MEUCIQCOxI79/PFbZiA+tz+PhejhBX8dQn+IvUhA3J/gxD3TygIgT8sPpdr2OqQPUkBUJ3TzF2UiJMu2/WQbAjbi3FBPMN4="
|
|
//var strServerRan = "4sXyN2iip6PeTj3n16iRsA=="
|
|
//var strServerCert ="MIIEazCCBBCgAwIBAgIKGhAAAAAAIA/MZDAKBggqgRzPVQGDdTBEMQswCQYDVQQGEwJDTjENMAsGA1UECgwEQkpDQTENMAsGA1UECwwEQkpDQTEXMBUGA1UEAwwOQmVpamluZyBTTTIgQ0EwHhcNMjMwNzAyMTYwMDAwWhcNMjYwNzAzMTU1OTU5WjCBgDELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAiIiMQswCQYDVQQHDAIiIjEPMA0GA1UECgwG5YWo5oC7MRIwEAYDVQQLDAnmtYvor5Xpg6gxGzAZBgNVBAMMEuWFqOaAu+a1i+ivleivgeS5pjEVMBMGA1UEKQwMMTExMTExMjIzMTIzMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEiSiGTJ/XbzT7W+SKm7cOp0KAK6S9ZXKHJqVGDefI4UK0+CaUJGJbjYN1HoSu82/M/VTHQg6VAi37iqXyrwLMqaOCAqswggKnMB8GA1UdIwQYMBaAFB/mz9SPxSIql0opihXnFsmSNMS2MB0GA1UdDgQWBBRYIKlLw6Mb3ggWoOsF8YLiF7q6WzAOBgNVHQ8BAf8EBAMCBsAwgaMGA1UdHwSBmzCBmDBgoF6gXKRaMFgxCzAJBgNVBAYTAkNOMQ0wCwYDVQQKDARCSkNBMQ0wCwYDVQQLDARCSkNBMRcwFQYDVQQDDA5CZWlqaW5nIFNNMiBDQTESMBAGA1UEAxMJY2EyMWNybDMwMDSgMqAwhi5odHRwOi8vdGVzdC5iamNhLm9yZy5jbjo4MDAzL2NybC9jYTIxY3JsMzAuY3JsMB4GCiqBHIbvMgIBAQEEEAwOSkoxMTExMTEyMjMxMjMwYAYIKwYBBQUHAQEEVDBSMCMGCCsGAQUFBzABhhdPQ1NQOi8vb2NzcC5iamNhLm9yZy5jbjArBggrBgEFBQcwAoYfaHR0cDovL2NybC5iamNhLm9yZy5jbi9jYWlzc3VlcjBABgNVHSAEOTA3MDUGCSqBHIbvMgICATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vd3d3LmJqY2Eub3JnLmNuL2NwczARBglghkgBhvhCAQEEBAMCAP8wHAYKKoEchu8yAgEBCAQODAwxMTExMTEyMjMxMjMwHgYKKoEchu8yAgECAgQQDA5KSjExMTExMTIyMzEyMzAfBgoqgRyG7zICAQEOBBEMDzk5ODAwMDEwMDkyNDQ0OTAeBgoqgRyG7zICAQEEBBAMDkpKMTExMTExMjIzMTIzMCcGCiqBHIbvMgIBARcEGQwXMUAyMTUwMDlKSjAxMTExMTEyMjMxMjMwGgYIKoEc0BQEAQQEDgwMMTExMTExMjIzMTIzMBQGCiqBHIbvMgIBAR4EBgwEMTA1MDAKBggqgRzPVQGDdQNJADBGAiEAieGsHBwvSeYSrGlK8OCO73lk2B8xP1Hu2OpuHp8AswICIQC7T5jjMXpNMdYA6zZRO1mU61to9uW4UmRjnIgg6GX6GA=="
|
|
|
|
|
|
//服务器随机数 4sXyN2iip6PeTj3n16iRsA==
|
|
//签名后的 值 MEUCIQCOxI79/PFbZiA+tz+PhejhBX8dQn+IvUhA3J/gxD3TygIgT8sPpdr2OqQPUkBUJ3TzF2UiJMu2/WQbAjbi3FBPMN4=
|
|
//服务器证书 MIIEazCCBBCgAwIBAgIKGhAAAAAAIA/MZDAKBggqgRzPVQGDdTBEMQswCQYDVQQGEwJDTjENMAsGA1UECgwEQkpDQTENMAsGA1UECwwEQkpDQTEXMBUGA1UEAwwOQmVpamluZyBTTTIgQ0EwHhcNMjMwNzAyMTYwMDAwWhcNMjYwNzAzMTU1OTU5WjCBgDELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAiIiMQswCQYDVQQHDAIiIjEPMA0GA1UECgwG5YWo5oC7MRIwEAYDVQQLDAnmtYvor5Xpg6gxGzAZBgNVBAMMEuWFqOaAu+a1i+ivleivgeS5pjEVMBMGA1UEKQwMMTExMTExMjIzMTIzMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEiSiGTJ/XbzT7W+SKm7cOp0KAK6S9ZXKHJqVGDefI4UK0+CaUJGJbjYN1HoSu82/M/VTHQg6VAi37iqXyrwLMqaOCAqswggKnMB8GA1UdIwQYMBaAFB/mz9SPxSIql0opihXnFsmSNMS2MB0GA1UdDgQWBBRYIKlLw6Mb3ggWoOsF8YLiF7q6WzAOBgNVHQ8BAf8EBAMCBsAwgaMGA1UdHwSBmzCBmDBgoF6gXKRaMFgxCzAJBgNVBAYTAkNOMQ0wCwYDVQQKDARCSkNBMQ0wCwYDVQQLDARCSkNBMRcwFQYDVQQDDA5CZWlqaW5nIFNNMiBDQTESMBAGA1UEAxMJY2EyMWNybDMwMDSgMqAwhi5odHRwOi8vdGVzdC5iamNhLm9yZy5jbjo4MDAzL2NybC9jYTIxY3JsMzAuY3JsMB4GCiqBHIbvMgIBAQEEEAwOSkoxMTExMTEyMjMxMjMwYAYIKwYBBQUHAQEEVDBSMCMGCCsGAQUFBzABhhdPQ1NQOi8vb2NzcC5iamNhLm9yZy5jbjArBggrBgEFBQcwAoYfaHR0cDovL2NybC5iamNhLm9yZy5jbi9jYWlzc3VlcjBABgNVHSAEOTA3MDUGCSqBHIbvMgICATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vd3d3LmJqY2Eub3JnLmNuL2NwczARBglghkgBhvhCAQEEBAMCAP8wHAYKKoEchu8yAgEBCAQODAwxMTExMTEyMjMxMjMwHgYKKoEchu8yAgECAgQQDA5KSjExMTExMTIyMzEyMzAfBgoqgRyG7zICAQEOBBEMDzk5ODAwMDEwMDkyNDQ0OTAeBgoqgRyG7zICAQEEBBAMDkpKMTExMTExMjIzMTIzMCcGCiqBHIbvMgIBARcEGQwXMUAyMTUwMDlKSjAxMTExMTEyMjMxMjMwGgYIKoEc0BQEAQQEDgwMMTExMTExMjIzMTIzMBQGCiqBHIbvMgIBAR4EBgwEMTA1MDAKBggqgRzPVQGDdQNJADBGAiEAieGsHBwvSeYSrGlK8OCO73lk2B8xP1Hu2OpuHp8AswICIQC7T5jjMXpNMdYA6zZRO1mU61to9uW4UmRjnIgg6GX6GA==
|
|
|
|
var strServerSignedData=serverInfo.value.signData
|
|
var strServerRan =serverInfo.value.random
|
|
var strServerCert =serverInfo.value.serverCert
|
|
|
|
|
|
var bRet = xtxLogin("form_xtx", selectedZhengShuId.value, uke_pwd.value,strServerCert,strServerRan,strServerSignedData);
|
|
console.log(bRet)
|
|
if (bRet) {
|
|
//如果前端教研成功
|
|
|
|
console.log('前端校验成功')
|
|
UkeyUserInfoLoginAction()
|
|
|
|
} else {
|
|
console.log('ukey登录失败')
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
const UkeyUserInfoLoginAction=()=>{
|
|
|
|
var UserCert= document.getElementsByName('UserCert')
|
|
var UserSignedData= document.getElementsByName('UserSignedData')
|
|
var ukey_userid=xtxsync.GetCertEntity(UserCert[0].value)//获取证书唯一标识
|
|
var data={
|
|
ukey_userid:ukey_userid,
|
|
cliCert:UserCert[0].value ,
|
|
sign:UserSignedData[0].value,
|
|
oridata:serverInfo.value.random,
|
|
}
|
|
UkeyUserInfoLogin(data).then(res=>{
|
|
if(res.status){
|
|
sessionStorage.setItem('token', res.data.token);
|
|
sessionStorage.setItem('refreshToken', res.data.refresh_token);
|
|
// sessionStorage.setItem('tk', JSON.stringify(res.data.tk));
|
|
var token = sessionStorage.getItem('token');
|
|
if (token == res.data.token) {
|
|
window.location.href = "./#/dashboard"
|
|
}
|
|
}else{
|
|
return ElMessage.error(res.msg)
|
|
}
|
|
|
|
})
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
<style scoped>
|
|
.common-layout {
|
|
/* border: 10px solid red; */
|
|
width: 100%;
|
|
height: 100vh;
|
|
|
|
}
|
|
|
|
.common-layout .el-main {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.left {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100vh;
|
|
background-color: var(--color-table-th-background);
|
|
background-image: url('../assets/loginBackg.png');
|
|
background-size: 100%;
|
|
background-repeat: no-repeat;
|
|
|
|
background-position: center center;
|
|
|
|
|
|
}
|
|
|
|
.right {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.right_top {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
width: 100%;
|
|
padding-right: 20px;
|
|
padding-top: 10px;
|
|
padding-bottom: 10px;
|
|
}
|
|
.erweima{
|
|
|
|
}
|
|
.erweima_tu {
|
|
width: 300px;
|
|
height: 300px;
|
|
border: 1px solid #ccc;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
margin-bottom: 20px;
|
|
text-align: center;
|
|
color: coral;
|
|
}
|
|
.timeout{
|
|
margin-top: 40%;
|
|
text-align: center;
|
|
color: coral;
|
|
cursor:pointer;
|
|
}
|
|
</style> |