添加二次认证

This commit is contained in:
lijun 2025-10-12 15:22:57 +08:00
parent 160fcb7c9c
commit 10d6cce1bf
6 changed files with 456 additions and 6 deletions

151
package-lock.json generated
View File

@ -50,6 +50,7 @@
"vue-cropper": "1.1.1", "vue-cropper": "1.1.1",
"vue-cropperjs": "^5.0.0", "vue-cropperjs": "^5.0.0",
"vue-echarts": "^6.6.9", "vue-echarts": "^6.6.9",
"vue-qr": "^4.0.9",
"vue-router": "^4.2.5", "vue-router": "^4.2.5",
"vue-schart": "^2.0.0", "vue-schart": "^2.0.0",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
@ -2564,6 +2565,20 @@
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
}, },
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dependencies": {
"mimic-response": "^3.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/deepmerge": { "node_modules/deepmerge": {
"version": "1.5.2", "version": "1.5.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz",
@ -3355,6 +3370,11 @@
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@ -3528,6 +3548,21 @@
"url": "https://opencollective.com/immer" "url": "https://opencollective.com/immer"
} }
}, },
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ini": { "node_modules/ini": {
"version": "1.3.8", "version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
@ -3665,6 +3700,11 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/js-binary-schema-parser": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz",
"integrity": "sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg=="
},
"node_modules/js-cookie": { "node_modules/js-cookie": {
"version": "3.0.5", "version": "3.0.5",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
@ -4598,11 +4638,21 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "5.1.6", "version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"dependencies": { "dependencies": {
"brace-expansion": "^2.0.1" "brace-expansion": "^2.0.1"
}, },
@ -4740,6 +4790,14 @@
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
"integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA=="
}, },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/package-json-from-dist": { "node_modules/package-json-from-dist": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
@ -4761,6 +4819,11 @@
"path-to-regexp": "~1.2.1" "path-to-regexp": "~1.2.1"
} }
}, },
"node_modules/parenthesis": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz",
"integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw=="
},
"node_modules/path-data-parser": { "node_modules/path-data-parser": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz",
@ -5207,6 +5270,49 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/simple-get": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/slate": { "node_modules/slate": {
"version": "0.72.8", "version": "0.72.8",
"resolved": "https://registry.npmjs.org/slate/-/slate-0.72.8.tgz", "resolved": "https://registry.npmjs.org/slate/-/slate-0.72.8.tgz",
@ -5284,6 +5390,14 @@
"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz", "resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz",
"integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==" "integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA=="
}, },
"node_modules/string-split-by": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/string-split-by/-/string-split-by-1.0.0.tgz",
"integrity": "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==",
"dependencies": {
"parenthesis": "^3.1.5"
}
},
"node_modules/string-width": { "node_modules/string-width": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@ -5912,6 +6026,36 @@
} }
} }
}, },
"node_modules/vue-qr": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/vue-qr/-/vue-qr-4.0.9.tgz",
"integrity": "sha512-pAISV94T0MNEYA3NGjykUpsXRE2QfaNxlu9ZhEL6CERgqNc21hJYuP3hRVzAWfBQlgO18DPmZTbrFerJC3+Ikw==",
"dependencies": {
"glob": "^8.0.1",
"js-binary-schema-parser": "^2.0.2",
"simple-get": "^4.0.1",
"string-split-by": "^1.0.0"
}
},
"node_modules/vue-qr/node_modules/glob": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^5.0.1",
"once": "^1.3.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/vue-route": { "node_modules/vue-route": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/vue-route/-/vue-route-1.5.1.tgz", "resolved": "https://registry.npmjs.org/vue-route/-/vue-route-1.5.1.tgz",
@ -6101,6 +6245,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/xlsx": { "node_modules/xlsx": {
"version": "0.18.5", "version": "0.18.5",
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",

View File

@ -50,6 +50,7 @@
"vue-cropper": "1.1.1", "vue-cropper": "1.1.1",
"vue-cropperjs": "^5.0.0", "vue-cropperjs": "^5.0.0",
"vue-echarts": "^6.6.9", "vue-echarts": "^6.6.9",
"vue-qr": "^4.0.9",
"vue-router": "^4.2.5", "vue-router": "^4.2.5",
"vue-schart": "^2.0.0", "vue-schart": "^2.0.0",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"

View File

@ -245,7 +245,7 @@ export const getGithubLoginUrl = (Data) => {
//获取第三方登录状态 //获取第三方登录状态
export const getThirdPartyLoginStatus = (Data) => { export const getThirdPartyLoginStatus = (Data) => {
let url ='/user/oAuth' + "?uuid=" + Data.uuid let url ='/user/oAuth' + "?uuid=" + Data.uuid + "&host_id=" + Data.host_id
return request.get(url) return request.get(url)
} }
@ -285,7 +285,41 @@ export const getTokenByCode = (Data) => {
}) })
} }
//生成totp密钥
export const genTOTPSecret = (Data) => {
return request.get('/tool/gen_totp_secret', {
headers: {
'token': Data.token,
}
})
}
export const getTOTPSecretInfo = (Data) => {
return request.get('/tool/get_totp_secret_info', {
headers: {
'token': Data.token,
}
})
}
export const unBindTOTPSecretService = (Data) => {
return request.delete('/tool/del_totp_secret', {
headers: {
'token': Data.token,
}
})
}
export const secondAuthLoginService = (Data) => {
let token = Data.token
delete Data.token
return request.post('/user/second_auth' , Data, {
headers: {
'token': token,
'Content-Type': 'application/json' // 设置请求头为 JSON 格式
}
})
}
export const fetchUserData = () => { export const fetchUserData = () => {
return { return {
"list": [ "list": [

View File

@ -109,6 +109,35 @@
</div> </div>
</div> </div>
</div> </div>
<el-dialog
v-model="second_auth_visible"
title="二次认证"
:close-on-click-modal="false"
width="30%"
>
<!-- 输入验证码 -->
<span>请选择二次认证方式</span>
<el-select v-model="second_auth_method" placeholder="请选择认证方式" style="width: 100%; margin-top: 20px;">
<el-option v-for="item in second_auth_type_list" :key="item" :label="item.toUpperCase()" :value="item"></el-option>
</el-select>
<el-row>
<span>输入验证码</span>
<el-input placeholder="请输入验证码" style="width: 100%; margin-top: 20px;" v-model="second_auth_code" >
<template #prepend>
<el-icon>
<Lock />
</el-icon>
</template>
</el-input>
</el-row>
<el-row>
<el-button class="login-btn"
type="primary"
size="large" @click="secondAuthLogin">确认</el-button>
</el-row>
</el-dialog>
</div> </div>
</template> </template>
@ -122,7 +151,7 @@ import { UserToken } from "@/types/user";
import { getBrowserFingerprint,getStoredFingerprint } from "@/utils/fingerprint"; import { getBrowserFingerprint,getStoredFingerprint } from "@/utils/fingerprint";
import { usePermissStore } from "@/store/permiss"; import { usePermissStore } from "@/store/permiss";
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import {getThirdPartyUUID,getThirdPartyLoginStatus,getThirdPartyLoginUrl,sendLoginCode, loginByCode} from "@/api/user"; import {getThirdPartyUUID,getThirdPartyLoginStatus,getThirdPartyLoginUrl,sendLoginCode, loginByCode, secondAuthLoginService} from "@/api/user";
import { log, time } from "console"; import { log, time } from "console";
import { pa } from "element-plus/es/locale"; import { pa } from "element-plus/es/locale";
// //
@ -137,6 +166,7 @@ const countdown = ref(0); //倒计时
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const second_auth_visible = ref(false); //
// //
const param = reactive({ const param = reactive({
username: defParam ? defParam.username : "", username: defParam ? defParam.username : "",
@ -175,6 +205,38 @@ const maxLoginRepeatRequest = 60; //最大请求次数
const currentLoginRequest = ref(0); // const currentLoginRequest = ref(0); //
const timer = ref(null); // const timer = ref(null); //
const secondAuthLogin = async () => {
if(!second_auth_method.value){
ElMessage.error("请选择认证方式");
return;
}
if(!second_auth_code.value){
ElMessage.error("请输入验证码");
return;
}
let req = {
state: second_auth_state_key.value,
type: second_auth_method.value,
code: second_auth_code.value
};
let result = await secondAuthLoginService(req);
if (result["code"] === 0) {
ElMessage.success("登录成功");
let userTokenInfo: UserToken = result["data"];
globalData["token"] = result.data;
localStorage.setItem("token", userTokenInfo.access_token);
localStorage.setItem("refresh_token", userTokenInfo.refresh_token);
localStorage.setItem("userId", userTokenInfo.user_id.toString());
localStorage.setItem("username", userTokenInfo.username);
let now = new Date();
localStorage.setItem("end_time", (now.setDate(now.getHours())).toString()); //
await getMyUserInfo(userTokenInfo.user_id);
}else{
ElMessage.error(result["message"] || "登录失败!请稍后再试");
return;
}
};
const thirdPartyLogoInfoList = ref([ const thirdPartyLogoInfoList = ref([
{ {
name: "github", name: "github",
@ -234,6 +296,10 @@ var loginData = ref({
fingerprint: '' // fingerprint: '' //
}); });
const querySite =ref(""); const querySite =ref("");
const second_auth_state_key = ref(""); //key
const second_auth_method = ref("");
const second_auth_type_list = ref([]);
const second_auth_code = ref(""); //
onMounted(async () => { onMounted(async () => {
let fp = await getStoredFingerprint(); let fp = await getStoredFingerprint();
@ -291,7 +357,7 @@ const thirdLogin = async (type) => {
ElMessage.error("登录超时,请重新登录!"); ElMessage.error("登录超时,请重新登录!");
return; return;
} }
let statusResp = await getThirdPartyLoginStatus({uuid: uuid}); let statusResp = await getThirdPartyLoginStatus({uuid: uuid,host_id:loginData.value.fingerprint});
if (statusResp["code"] !== 0) { if (statusResp["code"] !== 0) {
ElMessage.error("获取登录状态失败!请稍后再试"); ElMessage.error("获取登录状态失败!请稍后再试");
clearInterval(timer.value); clearInterval(timer.value);
@ -408,6 +474,18 @@ const onLogin = async () => {
console.log("login result:", result); console.log("login result:", result);
if (result["code"] === 0) { if (result["code"] === 0) {
ElMessage.success("登录成功"); ElMessage.success("登录成功");
}else if (result["code"] == 1102) {
//
second_auth_visible.value = true;
second_auth_state_key.value = result["data"]["state"];
let types = result["data"]["type"];
second_auth_type_list.value = types.split(",");
//''
second_auth_type_list.value = second_auth_type_list.value.filter(item => item);
second_auth_method.value = second_auth_type_list.value[0];
console.log("second_auth_type_list:", second_auth_type_list.value);
return;
}else if(result["code"] === 1101){ }else if(result["code"] === 1101){
ElMessage.error(result["message"] || "该账号已被禁用,请联系管理员!"); ElMessage.error(result["message"] || "该账号已被禁用,请联系管理员!");
return; return;

View File

@ -109,6 +109,28 @@
</template> </template>
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane name="label6" label="二次认证" class="user-tabpane">
<div>
<p>TOTP密钥</p>
<el-button type="primary" @click="GenTOTPSecret" :disabled="gen_totp_secret">生成TOTP密钥</el-button>
<div v-if="totp_secret || totp_url" style="margin-top: 20px;">
<!-- 提示 -->
<p>使用支持TOTP的应用如Google AuthenticatorAuthy等扫描二维码或手动输入密钥进行绑定</p>
<p>请妥善保存您的TOTP密钥只能查看一次若丢失请解绑后重新生成</p>
<el-button type="primary" @click="onCopyTOTPSecret" v-if="totp_secret">点击复制密钥</el-button>
<vueQr v-if="totp_url" :text="totp_url" :size="200" :margin="20" :background="'#ffffff'" :foreground="'#000000'" />
</div>
<el-row :gutter="20" class="user-tabpane">
<el-col :span="12" v-if="totp_secret_created_at">
<el-tag>密钥创建时间{{ totp_secret_created_at }}</el-tag>
</el-col>
<el-col :span="12" v-if="totp_secret_created_at">
<el-button type="danger" @click="unBindTOTPSecret">解绑</el-button>
</el-col>
</el-row>
</div>
</el-tab-pane>
</el-tabs> </el-tabs>
</el-card> </el-card>
</div> </div>
@ -121,7 +143,7 @@ import { VueCropper } from 'vue-cropper';
import 'vue-cropper/dist/index.css'; import 'vue-cropper/dist/index.css';
import avatar from '@/assets/img/img.jpg'; import avatar from '@/assets/img/img.jpg';
import TabsComp from '../element/tabs.vue'; import TabsComp from '../element/tabs.vue';
import {GetUserInfoService} from "@/api/user"; import {GetUserInfoService, genTOTPSecret, unBindTOTPSecretService, getTOTPSecretInfo} from "@/api/user";
import { GetUserStatisticService } from "@/api/user"; import { GetUserStatisticService } from "@/api/user";
import { UploadFileService } from "@/api/tool"; import { UploadFileService } from "@/api/tool";
import { UserInfo } from '@/types/user'; import { UserInfo } from '@/types/user';
@ -133,6 +155,7 @@ import {genResetPassword} from "@/api/user";
import {updateUserInfoService} from "@/api/user"; import {updateUserInfoService} from "@/api/user";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import {getThirdPartyUUID,getThirdPartyLoginStatus,getThirdPartyLoginUrl, getThirdPartyInfo, deleteThirdPartyInfo} from "@/api/user"; import {getThirdPartyUUID,getThirdPartyLoginStatus,getThirdPartyLoginUrl, getThirdPartyInfo, deleteThirdPartyInfo} from "@/api/user";
import vueQr from 'vue-qr/src/packages/vue-qr.vue'
const name = localStorage.getItem('ms_username'); const name = localStorage.getItem('ms_username');
const qqButtonBgImage = ref('https://wiki.connect.qq.com/wp-content/uploads/2016/12/Connect_logo_4.png'); const qqButtonBgImage = ref('https://wiki.connect.qq.com/wp-content/uploads/2016/12/Connect_logo_4.png');
@ -147,6 +170,7 @@ const userStatistic = reactive({
total: 0, total: 0,
file_count: 0, file_count: 0,
}); });
const gen_totp_secret = ref(false);
const userInfo = ref<UserInfo>(); const userInfo = ref<UserInfo>();
const isUserInfoLoaded = ref(false); const isUserInfoLoaded = ref(false);
const globalData = inject("globalData"); const globalData = inject("globalData");
@ -160,6 +184,9 @@ const cropImg = ref('');
const cropper: any = ref(); const cropper: any = ref();
const maxLoginRepeatRequest = 60; // const maxLoginRepeatRequest = 60; //
const currentLoginRequest = ref(0); // const currentLoginRequest = ref(0); //
const totp_secret = ref('');
const totp_url = ref('');
const totp_secret_created_at = ref('');
const thirdPartyUserInfo = ref<ThirdPartyUserInfo[]>([]); const thirdPartyUserInfo = ref<ThirdPartyUserInfo[]>([]);
@ -174,7 +201,74 @@ const thirdPartyPlatform = ref([
{ label: "Gitea自建", value: "my_gitea"}, { label: "Gitea自建", value: "my_gitea"},
{ label: "Microsoft", value: "microsoft"} { label: "Microsoft", value: "microsoft"}
]); ]);
const onCopyTOTPSecret = () => {
navigator.clipboard.writeText(totp_secret.value).then(() => {
ElMessage.success('复制成功!');
}).catch(() => {
ElMessage.error('复制失败,请手动复制');
});
};
const unBindTOTPSecret =async () => {
let req = {
token: localStorage.getItem('token'),
};
try{
let result = await unBindTOTPSecretService(req);
if (result["code"] == 0) {
ElMessage.success("解绑成功");
totp_secret.value = '';
totp_url.value = '';
totp_secret_created_at.value = '';
gen_totp_secret.value = false;
}else{
ElMessage.error(result["msg"]);
}
}catch(e){
console.log(e);
}
}
const GenTOTPSecret = async () => {
let req = {
token: localStorage.getItem('token'),
};
try{
let result = await genTOTPSecret(req);
if (result["code"] == 0) {
totp_secret.value = result["data"]["secret"];
totp_url.value = result["data"]["url"];
gen_totp_secret.value = true;
ElMessage.success("TOTP密钥生成成功请妥善保存,建议使用Google Authenticator等支持TOTP的应用扫码保存只能查询一次");
//
}else{
ElMessage.error(result["msg"]);
}
}catch(e){
console.log(e);
}
};
const GetTOTPSecret = async () => {
let req = {
token: localStorage.getItem('token'),
};
try{
let result = await getTOTPSecretInfo(req);
if (result["code"] == 0) {
if (result["data"]["ID"] != 0) {
totp_secret_created_at.value = result["data"]["CreatedAt"];
gen_totp_secret.value = true;
}
}else{
ElMessage.error(result["msg"]);
}
}catch(e){
console.log(e);
}
};
GetTOTPSecret();
const reset_password = () => { const reset_password = () => {
localStorage.removeItem("ms_username"); localStorage.removeItem("ms_username");

View File

@ -1821,6 +1821,13 @@ decode-named-character-reference@^1.0.0:
dependencies: dependencies:
character-entities "^2.0.0" character-entities "^2.0.0"
decompress-response@^6.0.0:
version "6.0.0"
resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz"
integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
dependencies:
mimic-response "^3.1.0"
deepmerge@^1.2.0: deepmerge@^1.2.0:
version "1.5.2" version "1.5.2"
resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz" resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz"
@ -2143,6 +2150,11 @@ frac@~1.1.2:
resolved "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz" resolved "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz"
integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA== integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
function-bind@^1.1.2: function-bind@^1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
@ -2167,6 +2179,17 @@ glob@^10.4.2:
package-json-from-dist "^1.0.0" package-json-from-dist "^1.0.0"
path-scurry "^1.11.1" path-scurry "^1.11.1"
glob@^8.0.1:
version "8.1.0"
resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz"
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^5.0.1"
once "^1.3.0"
globals@^15.14.0: globals@^15.14.0:
version "15.15.0" version "15.15.0"
resolved "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz" resolved "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz"
@ -2239,6 +2262,19 @@ immer@^9.0.6:
resolved "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz" resolved "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz"
integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz"
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2:
version "2.0.4"
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ini@^1.3.4: ini@^1.3.4:
version "1.3.8" version "1.3.8"
resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz"
@ -2335,6 +2371,11 @@ js-beautify@^1.14.9:
js-cookie "^3.0.5" js-cookie "^3.0.5"
nopt "^7.2.1" nopt "^7.2.1"
js-binary-schema-parser@^2.0.2:
version "2.0.3"
resolved "https://registry.npmjs.org/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz"
integrity sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==
js-cookie@^3.0.5: js-cookie@^3.0.5:
version "3.0.5" version "3.0.5"
resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz" resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz"
@ -2960,7 +3001,12 @@ mime-types@^2.1.12:
dependencies: dependencies:
mime-db "1.52.0" mime-db "1.52.0"
minimatch@^5.1.1: mimic-response@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz"
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
minimatch@^5.0.1, minimatch@^5.1.1:
version "5.1.6" version "5.1.6"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz"
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
@ -3058,6 +3104,13 @@ nprogress@^0.2.0:
resolved "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz" resolved "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz"
integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==
once@^1.3.0, once@^1.3.1:
version "1.4.0"
resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies:
wrappy "1"
package-json-from-dist@^1.0.0: package-json-from-dist@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz" resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz"
@ -3077,6 +3130,11 @@ page@^1.5.0:
dependencies: dependencies:
path-to-regexp "~1.2.1" path-to-regexp "~1.2.1"
parenthesis@^3.1.5:
version "3.1.8"
resolved "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz"
integrity sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==
path-data-parser@^0.1.0, path-data-parser@0.1.0: path-data-parser@^0.1.0, path-data-parser@0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz" resolved "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz"
@ -3360,6 +3418,20 @@ signal-exit@^4.0.1:
resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz"
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
simple-concat@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz"
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
simple-get@^4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz"
integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==
dependencies:
decompress-response "^6.0.0"
once "^1.3.1"
simple-concat "^1.0.0"
slate-history@^0.66.0: slate-history@^0.66.0:
version "0.66.0" version "0.66.0"
resolved "https://registry.npmjs.org/slate-history/-/slate-history-0.66.0.tgz" resolved "https://registry.npmjs.org/slate-history/-/slate-history-0.66.0.tgz"
@ -3416,6 +3488,13 @@ ssr-window@^3.0.0-alpha.1:
resolved "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz" resolved "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz"
integrity sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA== integrity sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==
string-split-by@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/string-split-by/-/string-split-by-1.0.0.tgz"
integrity sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==
dependencies:
parenthesis "^3.1.5"
"string-width-cjs@npm:string-width@^4.2.0": "string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3" version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
@ -3762,6 +3841,16 @@ vue-echarts@^6.6.9:
resize-detector "^0.3.0" resize-detector "^0.3.0"
vue-demi "^0.13.11" vue-demi "^0.13.11"
vue-qr@^4.0.9:
version "4.0.9"
resolved "https://registry.npmjs.org/vue-qr/-/vue-qr-4.0.9.tgz"
integrity sha512-pAISV94T0MNEYA3NGjykUpsXRE2QfaNxlu9ZhEL6CERgqNc21hJYuP3hRVzAWfBQlgO18DPmZTbrFerJC3+Ikw==
dependencies:
glob "^8.0.1"
js-binary-schema-parser "^2.0.2"
simple-get "^4.0.1"
string-split-by "^1.0.0"
vue-route@^1.5.1: vue-route@^1.5.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.npmjs.org/vue-route/-/vue-route-1.5.1.tgz" resolved "https://registry.npmjs.org/vue-route/-/vue-route-1.5.1.tgz"
@ -3874,6 +3963,11 @@ wrap-ansi@^8.1.0:
string-width "^5.0.1" string-width "^5.0.1"
strip-ansi "^7.0.1" strip-ansi "^7.0.1"
wrappy@1:
version "1.0.2"
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
xlsx@^0.18.5: xlsx@^0.18.5:
version "0.18.5" version "0.18.5"
resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz" resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz"