1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-13 17:24:44 +08:00

feat: 安全入口功能实现

This commit is contained in:
ssongliu 2022-09-15 17:15:03 +08:00 committed by ssongliu
parent e1bf00cb34
commit 389e268407
27 changed files with 222 additions and 327 deletions

View File

@ -1,11 +1,14 @@
package v1
import (
"errors"
"github.com/1Panel-dev/1Panel/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/app/dto"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/utils/captcha"
"github.com/1Panel-dev/1Panel/utils/encrypt"
"github.com/gin-gonic/gin"
)
@ -46,6 +49,37 @@ func (b *BaseApi) Captcha(c *gin.Context) {
captcha, err := captcha.CreateCaptcha()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, captcha)
}
func (b *BaseApi) GetSafetyStatus(c *gin.Context) {
if err := authService.SafetyStatus(c); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, err)
return
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) SafeEntrance(c *gin.Context) {
code, exist := c.Params.Get("code")
if !exist {
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
return
}
ok, err := authService.VerifyCode(code)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
return
}
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
return
}
codeWithMD5 := encrypt.Md5(code)
cookieValue, _ := encrypt.StringEncrypt(codeWithMD5)
c.SetCookie(codeWithMD5, cookieValue, 86400, "", "", false, false)
helper.SuccessWithData(c, nil)
}

View File

@ -16,6 +16,8 @@ import (
type AuthService struct{}
type IAuthService interface {
SafetyStatus(c *gin.Context) error
VerifyCode(code string) (bool, error)
Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error)
LogOut(c *gin.Context) error
}
@ -89,3 +91,30 @@ func (u *AuthService) LogOut(c *gin.Context) error {
}
return nil
}
func (u *AuthService) VerifyCode(code string) (bool, error) {
setting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
if err != nil {
return false, err
}
return setting.Value == code, nil
}
func (u *AuthService) SafetyStatus(c *gin.Context) error {
setting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
if err != nil {
return err
}
codeWithEcrypt, err := c.Cookie(encrypt.Md5(setting.Value))
if err != nil {
return err
}
code, err := encrypt.StringDecrypt(codeWithEcrypt)
if err != nil {
return err
}
if code != encrypt.Md5(setting.Value) {
return errors.New("code not match")
}
return nil
}

View File

@ -8,6 +8,7 @@ const (
CodeSuccess = 200
CodeErrBadRequest = 400
CodeErrUnauthorized = 401
CodeErrUnSafety = 402
CodeErrForbidden = 403
CodeErrNotFound = 404
CodeErrInternalServer = 500
@ -33,5 +34,6 @@ var (
ErrTypeInvalidParams = "ErrInvalidParams"
ErrTypeToken = "ErrToken"
ErrTypeTokenTimeOut = "ErrTokenTimeOut"
ErrTypeNotLogin = "ErrTypeNotLogin"
ErrTypeNotLogin = "ErrNotLogin"
ErrTypeNotSafety = "ErrNotSafety"
)

View File

@ -8,4 +8,5 @@ ErrInternalServer: "Service internal error: {{ .detail }}"
ErrRecordExist: "Record already exists: {{ .detail }}"
ErrRecordNotFound: "Records not found: {{ .detail }}"
ErrStructTransform: "Type conversion failure: {{ .detail }}"
ErrTypeNotLogin: "User is not Login"
ErrNotLogin: "User is not Login: {{ .detail }}"
ErrNotSafety: "The login status of the current user is unsafe: {{ .detail }}"

View File

@ -8,4 +8,5 @@ ErrInternalServer: "服务内部错误: {{ .detail }}"
ErrRecordExist: "记录已存在: {{ .detail }}"
ErrRecordNotFound: "记录未能找到: {{ .detail }}"
ErrStructTransform: "类型转换失败: {{ .detail }}"
ErrTypeNotLogin: "用户未登录"
ErrNotLogin: "用户未登录: {{ .detail }}"
ErrNotSafety: "当前用户登录状态不安全: {{ .detail }}"

View File

@ -81,7 +81,7 @@ var AddTableSetting = &gormigrate.Migration{
if err := tx.Create(&model.Setting{Key: "ServerPort", Value: "4004"}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "SecurityEntrance", Value: "/89dc6ae8"}).Error; err != nil {
if err := tx.Create(&model.Setting{Key: "SecurityEntrance", Value: "89dc6ae8"}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "PasswordTimeOut", Value: time.Now().AddDate(0, 0, 10).Format("2016.01.02 15:04:05")}).Error; err != nil {

View File

@ -3,6 +3,7 @@ package router
import (
"html/template"
v1 "github.com/1Panel-dev/1Panel/app/api/v1"
"github.com/1Panel-dev/1Panel/docs"
"github.com/1Panel-dev/1Panel/i18n"
"github.com/1Panel-dev/1Panel/middleware"
@ -19,8 +20,11 @@ func Routers() *gin.Engine {
Router.Use(middleware.LoadCsrfToken())
docs.SwaggerInfo.BasePath = "/api/v1"
Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
Router.Use(i18n.GinI18nLocalize())
Router.GET("/api/v1/info", v1.ApiGroupApp.BaseApi.GetSafetyStatus)
Router.GET("/api/v1/:code", v1.ApiGroupApp.BaseApi.SafeEntrance)
Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
Router.SetFuncMap(template.FuncMap{
"Localize": ginI18n.GetMessage,
@ -35,7 +39,9 @@ func Routers() *gin.Engine {
c.JSON(200, "ok")
})
}
PrivateGroup := Router.Group("/api/v1")
PrivateGroup.Use(middleware.SafetyAuth())
{
systemRouter.InitBaseRouter(PrivateGroup)
systemRouter.InitHostRouter(PrivateGroup)

View File

@ -0,0 +1,18 @@
package middleware
import (
"github.com/1Panel-dev/1Panel/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/app/service"
"github.com/1Panel-dev/1Panel/constant"
"github.com/gin-gonic/gin"
)
func SafetyAuth() gin.HandlerFunc {
return func(c *gin.Context) {
if err := service.NewIAuthService().SafetyStatus(c); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, nil)
return
}
c.Next()
}
}

View File

@ -4,8 +4,10 @@ import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
@ -38,6 +40,12 @@ func StringDecrypt(text string) (string, error) {
return "", err
}
func Md5(str string) string {
h := md5.New()
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
func padding(plaintext []byte, blockSize int) []byte {
padding := blockSize - len(plaintext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)

View File

@ -49,6 +49,12 @@ class RequestHttp {
});
return Promise.reject(data);
}
if (data.code == ResultEnum.UNSAFETY) {
router.replace({
path: '/login',
});
return data;
}
if (data.code && data.code !== ResultEnum.SUCCESS) {
ElMessage.error(data.msg);
return Promise.reject(data);

View File

@ -1,18 +0,0 @@
import { CommonModel, ReqPage } from '.';
export namespace User {
export interface User extends CommonModel {
name: string;
email: string;
password: string;
}
export interface UserCreate {
username: string;
email: string;
}
export interface ReqGetUserParams extends ReqPage {
info?: string;
email?: string;
}
}

View File

@ -12,3 +12,11 @@ export const getCaptcha = () => {
export const logOutApi = () => {
return http.post<any>(`/auth/logout`);
};
export const entrance = (code: string) => {
return http.get<any>(`/${code}`);
};
export const loginStatus = () => {
return http.get<any>('/info');
};

View File

@ -1,23 +0,0 @@
import http from '@/api';
import { ResPage } from '../interface';
import { User } from '../interface/user';
export const getUserList = (params: User.ReqGetUserParams) => {
return http.post<ResPage<User.User>>(`/users/search`, params);
};
export const addUser = (params: User.User) => {
return http.post(`/users`, params);
};
export const getUserById = (id: number) => {
return http.get<User.User>(`/users/${id}`);
};
export const editUser = (params: User.User) => {
return http.put(`/users/` + params.id, params);
};
export const deleteUser = (params: { ids: number[] }) => {
return http.post(`/users/del`, params);
};

View File

@ -41,7 +41,7 @@ import { loadingSvg } from '@/utils/svg';
import Logo from './components/logo.vue';
import SubItem from './components/sub-item.vue';
import router, { menuList } from '@/routers/router';
import { logOutApi } from '@/api/modules/login';
import { logOutApi } from '@/api/modules/auth';
import i18n from '@/lang';
import { ElMessageBox, ElMessage } from 'element-plus';
import { GlobalStore } from '@/store';

View File

@ -1,27 +0,0 @@
<template>
<el-switch
v-model="themeConfig.isDark"
@change="onAddDarkChange"
inline-prompt
active-color="#0a0a0a"
inactive-color="#dcdfe6"
:active-icon="Sunny"
:inactive-icon="Moon"
/>
</template>
<script setup lang="ts" name="switchDark">
import { computed } from 'vue';
import { GlobalStore } from '@/store';
import { Sunny, Moon } from '@element-plus/icons-vue';
import { useTheme } from '@/hooks/use-theme';
const globalStore = GlobalStore();
const { switchDark } = useTheme();
const themeConfig = computed(() => globalStore.themeConfig);
const onAddDarkChange = () => {
switchDark();
};
</script>

View File

@ -2,6 +2,7 @@ export enum ResultEnum {
SUCCESS = 200,
ERROR = 500,
OVERDUE = 401,
UNSAFETY = 402,
FORBIDDEN = 403,
TIMEOUT = 100000,
TYPE = 'success',

View File

@ -8,6 +8,8 @@ export default {
sync: 'Sync',
delete: 'Delete',
edit: 'Edit',
enable: 'Enable',
disable: 'Disable',
confirm: 'Confirm',
cancel: 'Cancel',
reset: 'Reset',
@ -49,6 +51,15 @@ export default {
},
login: {
captchaHelper: 'Please enter the verification code',
safeEntrance: 'Please use the correct entry to log in to the panel',
reason: 'Cause of error:',
reasonHelper:
'At present, the newly installed machine has enabled the security entrance login. The newly installed machine will have a random 8-character security entrance name, which can also be modified in the panel Settings. If you do not record or do not remember, you can use the following methods to solve the problem',
solution: 'The solution:',
solutionHelper:
'Run the following command on the SSH terminal to solve the problem: 1. View the /etc/init.d/bt default command on the panel',
warnning:
'Note: [Closing the security entrance] will make your panel login address directly exposed to the Internet, very dangerous, please exercise caution',
},
rule: {
username: 'Please enter a username',
@ -87,7 +98,6 @@ export default {
},
menu: {
home: 'Overview',
demo: 'Example',
terminal: 'Terminal',
apps: 'App Store',
website: 'Website',

View File

@ -61,6 +61,13 @@ export default {
},
login: {
captchaHelper: '请输入验证码',
safeEntrance: '请使用正确的入口登录面板',
reason: '错误原因',
reasonHelper:
'当前新安装的已经开启了安全入口登录新装机器都会随机一个8位字符的安全入口名称亦可以在面板设置处修改如您没记录或不记得了可以使用以下方式解决',
solution: '解决方法',
solutionHelper: '在SSH终端输入以下一种命令来解决 1.查看面板入口/etc/init.d/bt default',
warnning: '注意关闭安全入口将使您的面板登录地址被直接暴露在互联网上非常危险请谨慎操作',
},
rule: {
username: '请输入用户名',
@ -101,7 +108,6 @@ export default {
},
menu: {
home: '概览',
demo: '样例',
monitor: '监控',
terminal: '终端',
operations: '操作日志',
@ -287,7 +293,7 @@ export default {
panelPort: '面板端口',
portHelper: '建议端口范围8888 - 65535注意有安全组的服务器请提前在安全组放行新端口',
safeEntrance: '安全入口',
safeEntranceHelper: '面板管理入口设置后只能通过指定安全入口登录面板,: /89dc6ae8',
safeEntranceHelper: '面板管理入口设置后只能通过指定安全入口登录面板,: 89dc6ae8',
passwordTimeout: '密码过期时间',
timeoutHelper: ' {0} 天后 面板密码即将过期过期后需要重新设置密码',
complexity: '密码复杂度验证',

View File

@ -1,32 +0,0 @@
import { Layout } from '@/routers/constant';
// demo
const demoRouter = {
sort: 1,
path: '/demos',
component: Layout,
redirect: '/demos/table',
meta: {
icon: 'apple',
title: 'menu.demo',
},
children: [
{
path: '/demos/table',
name: 'Table',
component: () => import('@/views/demos/table/index.vue'),
},
{
path: '/demos/table/:op/:id?',
name: 'DemoOperate',
props: true,
hidden: true,
component: () => import('@/views/demos/table/operate/index.vue'),
meta: {
activeMenu: '/demos/table',
},
},
],
};
export default demoRouter;

View File

@ -1,10 +1,10 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import { Layout } from '@/routers/constant';
const modules = import.meta.globEager('./modules/*.ts');
const homeRouter: RouteRecordRaw = {
path: '/',
path: '/home',
component: Layout,
redirect: '/home/index',
meta: {
@ -55,8 +55,9 @@ menuList.unshift(homeRouter);
export const routes: RouteRecordRaw[] = [
homeRouter,
{
path: '/login',
path: '/login/:code?',
name: 'login',
props: true,
component: () => import('@/views/login/index.vue'),
meta: {
requiresAuth: false,
@ -70,7 +71,7 @@ export const routes: RouteRecordRaw[] = [
},
];
const router = createRouter({
history: createWebHashHistory(),
history: createWebHistory(),
routes: routes as RouteRecordRaw[],
strict: false,
scrollBehavior: () => ({ left: 0, top: 0 }),

View File

@ -1,101 +0,0 @@
<template>
<LayoutContent :header="'样例'">
<ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" :data="data" @search="search">
<template #toolbar>
<el-button type="primary" @click="openOperate(null)">{{ $t('commons.button.create') }}</el-button>
<el-button type="primary" plain>{{ '其他操作' }}</el-button>
<el-button type="danger" plain :disabled="selects.length === 0" @click="batchDelete(null)">
{{ $t('commons.button.delete') }}
</el-button>
</template>
<el-table-column type="selection" fix />
<el-table-column label="ID" min-width="100" prop="id" fix />
<el-table-column :label="$t('commons.table.name')" min-width="100" prop="name" fix>
<template #default="{ row }">
<fu-input-rw-switch v-model="row.name" size="mini" />
</template>
</el-table-column>
<el-table-column label="Email" min-width="100" prop="email" />
<el-table-column
prop="createdAt"
:label="$t('commons.table.createdAt')"
:formatter="dateFromat"
show-overflow-tooltip
width="200"
/>
<fu-table-operations :buttons="buttons" :label="$t('commons.table.operate')" fix />
</ComplexTable>
</LayoutContent>
</template>
<script setup lang="ts">
import LayoutContent from '@/layout/layout-content.vue';
import ComplexTable from '@/components/complex-table/index.vue';
import { dateFromat } from '@/utils/util';
import { User } from '@/api/interface/user';
import { deleteUser, getUserList } from '@/api/modules/user';
import { onMounted, reactive, ref } from '@vue/runtime-core';
import { useDeleteData } from '@/hooks/use-delete-data';
import i18n from '@/lang';
import { useRouter } from 'vue-router';
const router = useRouter();
const data = ref();
const selects = ref<any>([]);
const paginationConfig = reactive({
page: 1,
pageSize: 20,
total: 0,
});
const userSearch = reactive({
page: 1,
pageSize: 20,
});
const openOperate = (row: User.User | null) => {
let params: { [key: string]: any } = {
op: 'create',
};
if (row !== null) {
params.op = 'edit';
params.id = row.id;
}
router.push({ name: 'DemoOperate', params });
};
const batchDelete = async (row: User.User | null) => {
let ids: Array<number> = [];
if (row === null) {
selects.value.forEach((item: User.User) => {
ids.push(item.id);
});
} else {
ids.push(row.id);
}
await useDeleteData(deleteUser, { ids: ids }, 'commons.msg.delete', true);
search();
};
const buttons = [
{
label: i18n.global.t('commons.button.edit'),
click: openOperate,
},
{
label: i18n.global.t('commons.button.delete'),
type: 'danger',
click: batchDelete,
},
];
const search = async () => {
userSearch.page = paginationConfig.page;
userSearch.pageSize = paginationConfig.pageSize;
const res = await getUserList(userSearch);
data.value = res.data.items;
paginationConfig.total = res.data.total;
};
onMounted(() => {
search();
});
</script>

View File

@ -1,89 +0,0 @@
<template>
<LayoutContent :header="$t('commons.button.' + op)" :back-name="'Table'">
<template #form>
<el-form ref="ruleFormRef" label-position="left" :model="demoForm" :rules="rules" label-width="140px">
<el-form-item :label="$t('auth.username')" prop="name">
<el-input v-model="demoForm.name" />
</el-form-item>
<el-form-item :label="$t('auth.email')" prop="email">
<el-input v-model="demoForm.email" />
</el-form-item>
<el-form-item :label="$t('auth.password')" prop="password">
<el-input type="password" v-model="demoForm.password" />
</el-form-item>
</el-form>
<div class="form-button">
<el-button @click="router.back()">{{ $t('commons.button.cancel') }}</el-button>
<el-button type="primary" @click="submitForm(ruleFormRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</div>
</template>
</LayoutContent>
</template>
<script setup lang="ts">
import LayoutContent from '@/layout/layout-content.vue';
import { ElMessage, FormInstance, FormRules } from 'element-plus';
import { onMounted, reactive, ref } from 'vue';
import { Rules } from '@/global/form-rues';
import { addUser, editUser, getUserById } from '@/api/modules/user';
import i18n from '@/lang';
import { useRouter } from 'vue-router';
import { User } from '@/api/interface/user';
const router = useRouter();
const ruleFormRef = ref<FormInstance>();
let demoForm = ref<User.User>({
id: 0,
name: '',
email: '',
password: '',
});
interface OperateProps {
op: string;
id: string;
}
const props = withDefaults(defineProps<OperateProps>(), {
op: 'create',
});
const rules = reactive<FormRules>({
name: [Rules.requiredInput, Rules.name],
email: [Rules.requiredInput, Rules.email],
password: [Rules.requiredInput],
});
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (!valid) {
return;
}
if (props.op === 'create') {
addUser(demoForm.value).then(() => {
ElMessage.success(i18n.global.t('commons.msg.createSuccess'));
router.back();
});
} else {
editUser(demoForm.value).then(() => {
ElMessage.success(i18n.global.t('commons.msg.updateSuccess'));
router.back();
});
}
});
};
const getUser = async (id: number) => {
const res = await getUserById(id);
demoForm.value = res.data;
};
onMounted(() => {
if (props.op == 'edit') {
console.log(props);
getUser(Number(props.id)).catch(() => {
router.back();
});
}
});
</script>

View File

@ -47,7 +47,6 @@
@change="quickInput"
style="width: 25%"
:placeholder="$t('terminal.quickCommand')"
>
<el-option
v-for="cmd in commandList"
@ -61,7 +60,6 @@
v-model="batchVal"
@keyup.enter="batchInput"
style="width: 75%"
>
<template #append>
<el-switch v-model="isBatch" class="ml-2" />

View File

@ -46,7 +46,7 @@ import { useRouter } from 'vue-router';
import { Login } from '@/api/interface';
import type { ElForm } from 'element-plus';
import { ElMessage } from 'element-plus';
import { loginApi, getCaptcha } from '@/api/modules/login';
import { loginApi, getCaptcha } from '@/api/modules/auth';
import { GlobalStore } from '@/store';
import { MenuStore } from '@/store/modules/menu';
import i18n from '@/lang';
@ -111,17 +111,15 @@ const resetForm = (formEl: FormInstance | undefined) => {
formEl.resetFields();
};
const loginVerify = () => {
getCaptcha().then(async (ele) => {
captcha.imagePath = ele.data?.imagePath ? ele.data.imagePath : '';
captcha.captchaID = ele.data?.captchaID ? ele.data.captchaID : '';
captcha.captchaLength = ele.data?.captchaLength ? ele.data.captchaLength : 0;
});
const loginVerify = async () => {
const res = await getCaptcha();
captcha.imagePath = res.data.imagePath ? res.data.imagePath : '';
captcha.captchaID = res.data.captchaID ? res.data.captchaID : '';
captcha.captchaLength = res.data.captchaLength ? res.data.captchaLength : 0;
};
loginVerify();
onMounted(() => {
// enter
document.onkeydown = (e: any) => {
e = window.event || e;
if (e.code === 'Enter' || e.code === 'enter' || e.code === 'NumpadEnter') {

View File

@ -1,24 +1,82 @@
<template>
<div class="login-container flx-center">
<SwitchDark class="dark"></SwitchDark>
<div class="login-box">
<div class="login-left">
<img src="@/assets/images/login_left0.png" alt="login" />
</div>
<div class="login-form">
<div class="login-logo">
<img class="login-icon" src="@/assets/images/logo.svg" alt="" />
<h2 class="logo-text">1Panel</h2>
<div>
<div v-if="statusCode == 1" class="login-container flx-center">
<div class="login-box">
<div class="login-left">
<img src="@/assets/images/login_left0.png" alt="login" />
</div>
<LoginForm ref="loginRef"></LoginForm>
<div class="login-form">
<div class="login-logo">
<img class="login-icon" src="@/assets/images/logo.svg" alt="" />
<h2 class="logo-text">1Panel</h2>
</div>
<LoginForm ref="loginRef"></LoginForm>
</div>
</div>
</div>
<div style="margin-left: 50px" v-if="statusCode == -1">
<h1>{{ $t('commons.login.safeEntrance') }}</h1>
<div style="line-height: 30px">
<span style="font-weight: 500">{{ $t('commons.login.reason') }}</span>
<span>
{{ $t('commons.login.reasonHelper') }}
</span>
</div>
<div style="line-height: 30px">
<span style="font-weight: 500">{{ $t('commons.login.solution') }}</span>
<span>{{ $t('commons.login.solutionHelper') }}</span>
</div>
<div style="line-height: 30px">
<span style="color: red">
{{ $t('commons.login.warnning') }}
</span>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="login">
import SwitchDark from '@/components/switch-dark/index.vue';
import LoginForm from './components/login-form.vue';
import { ref, onMounted, onBeforeMount } from 'vue';
import { loginStatus, entrance } from '@/api/modules/auth';
import { useRouter } from 'vue-router';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const router = useRouter();
interface Props {
code: string;
}
const mySafetyCode = withDefaults(defineProps<Props>(), {
code: '',
});
const statusCode = ref<number>(0);
const getStatus = async () => {
const res = await loginStatus();
if (res.code === 402) {
statusCode.value = -1;
} else {
statusCode.value = 1;
return;
}
if (mySafetyCode.code) {
const res = await entrance(mySafetyCode.code);
if (res.code === 200) {
statusCode.value = 1;
}
}
};
onBeforeMount(() => {
if (globalStore.isLogin) {
router.push({ name: 'home' });
}
});
onMounted(() => {
getStatus();
});
</script>
<style scoped lang="scss">

View File

@ -221,7 +221,7 @@ const submitChangePassword = async (formEl: FormInstance | undefined) => {
await updatePassword(passForm);
passwordVisiable.value = false;
ElMessage.success(i18n.t('commons.msg.operationSuccess'));
router.push({ name: 'login' });
router.push({ name: 'login', params: { code: '' } });
globalStore.setLogStatus(false);
});
};

View File

@ -35,7 +35,7 @@
</span>
</div>
</el-form-item>
<el-form-item label="$t('setting.safeEntrance')">
<el-form-item :label="$t('setting.safeEntrance')">
<el-input clearable v-model="form.settingInfo.securityEntrance">
<template #append>
<el-button