mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-02-12 19:40:06 +08:00
feat(system): Password encryption for login API. (#7825)
This commit is contained in:
parent
a86d56bef5
commit
8b30308a4b
@ -37,16 +37,11 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
|
||||
if err != nil {
|
||||
return nil, "", buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
passwordSetting, err := settingRepo.Get(repo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return nil, "", buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
|
||||
if err != nil {
|
||||
if nameSetting.Value != info.Name {
|
||||
return nil, "ErrAuth", nil
|
||||
}
|
||||
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
|
||||
return nil, "ErrAuth", nil
|
||||
if err = checkPassword(info.Password); err != nil {
|
||||
return nil, "ErrAuth", err
|
||||
}
|
||||
entranceSetting, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
|
||||
if err != nil {
|
||||
@ -77,17 +72,12 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin
|
||||
if err != nil {
|
||||
return nil, "", buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
passwordSetting, err := settingRepo.Get(repo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return nil, "", buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
|
||||
if nameSetting.Value != info.Name {
|
||||
return nil, "ErrAuth", nil
|
||||
}
|
||||
if err = checkPassword(info.Password); err != nil {
|
||||
return nil, "ErrAuth", err
|
||||
}
|
||||
entranceSetting, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
@ -216,3 +206,28 @@ func (u *AuthService) IsLogin(c *gin.Context) bool {
|
||||
_, err := global.SESSION.Get(c)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func checkPassword(password string) error {
|
||||
priKey, _ := settingRepo.Get(repo.WithByKey("PASSWORD_PRIVATE_KEY"))
|
||||
|
||||
privateKey, err := encrypt.ParseRSAPrivateKey(priKey.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loginPassword, err := encrypt.DecryptPassword(password, privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
passwordSetting, err := settingRepo.Get(repo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
existPassword, err := encrypt.StringDecrypt(passwordSetting.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !hmac.Equal([]byte(loginPassword), []byte(existPassword)) {
|
||||
return buserr.New("ErrAuth")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
@ -45,6 +47,8 @@ type ISettingService interface {
|
||||
UpdateTerminal(req dto.TerminalInfo) error
|
||||
|
||||
UpdateSystemSSL() error
|
||||
|
||||
GenerateRSAKey() error
|
||||
}
|
||||
|
||||
func NewISettingService() ISettingService {
|
||||
@ -497,3 +501,29 @@ func checkCertValid() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) GenerateRSAKey() error {
|
||||
priKey, _ := settingRepo.Get(repo.WithByKey("PASSWORD_PRIVATE_KEY"))
|
||||
pubKey, _ := settingRepo.Get(repo.WithByKey("PASSWORD_PUBLIC_KEY"))
|
||||
if priKey.Value != "" && pubKey.Value != "" {
|
||||
return nil
|
||||
}
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
privateKeyPEM := encrypt.ExportPrivateKeyToPEM(privateKey)
|
||||
publicKeyPEM, err := encrypt.ExportPublicKeyToPEM(&privateKey.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = settingRepo.UpdateOrCreate("PASSWORD_PRIVATE_KEY", privateKeyPEM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = settingRepo.UpdateOrCreate("PASSWORD_PUBLIC_KEY", publicKeyPEM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/core/app/service"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
@ -29,6 +30,8 @@ func Init() {
|
||||
}
|
||||
|
||||
handleUserInfo(global.CONF.Base.ChangeUserInfo, settingRepo)
|
||||
|
||||
generateKey()
|
||||
}
|
||||
|
||||
func handleUserInfo(tags string, settingRepo repo.ISettingRepo) {
|
||||
@ -68,3 +71,9 @@ func handleUserInfo(tags string, settingRepo repo.ISettingRepo) {
|
||||
sudo := cmd.SudoHandleCmd()
|
||||
_, _ = cmd.Execf("%s sed -i '/CHANGE_USER_INFO=%v/d' /usr/local/bin/1pctl", sudo, global.CONF.Base.ChangeUserInfo)
|
||||
}
|
||||
|
||||
func generateKey() {
|
||||
if err := service.NewISettingService().GenerateRSAKey(); err != nil {
|
||||
global.LOG.Errorf("generate rsa key error : %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
@ -167,6 +167,7 @@ func Routers() *gin.Engine {
|
||||
PrivateGroup.Use(middleware.WhiteAllow())
|
||||
PrivateGroup.Use(middleware.BindDomain())
|
||||
PrivateGroup.Use(middleware.GlobalLoading())
|
||||
PrivateGroup.Use(middleware.SetPasswordPublicKey())
|
||||
for _, router := range rou.RouterGroupApp {
|
||||
router.InitRouter(PrivateGroup)
|
||||
}
|
||||
|
22
core/middleware/password_rsa.go
Normal file
22
core/middleware/password_rsa.go
Normal file
@ -0,0 +1,22 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SetPasswordPublicKey() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
cookieKey, _ := c.Cookie("panel_public_key")
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
key, _ := settingRepo.Get(repo.WithByKey("PASSWORD_PUBLIC_KEY"))
|
||||
base64Key := base64.StdEncoding.EncodeToString([]byte(key.Value))
|
||||
if base64Key == cookieKey {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
c.SetCookie("panel_public_key", base64Key, 7*24*60*60, "/", "", false, false)
|
||||
c.Next()
|
||||
}
|
||||
}
|
@ -5,26 +5,19 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/model"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
)
|
||||
|
||||
func StringEncryptWithBase64(text string) (string, error) {
|
||||
base64Item, err := base64.StdEncoding.DecodeString(text)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
encryptItem, err := StringEncrypt(string(base64Item))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return encryptItem, nil
|
||||
}
|
||||
|
||||
func StringDecryptWithBase64(text string) (string, error) {
|
||||
decryptItem, err := StringDecrypt(text)
|
||||
if err != nil {
|
||||
@ -136,3 +129,95 @@ func aesDecryptWithSalt(key, ciphertext []byte) ([]byte, error) {
|
||||
ciphertext = unPadding(ciphertext)
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func ParseRSAPrivateKey(privateKeyPEM string) (*rsa.PrivateKey, error) {
|
||||
block, _ := pem.Decode([]byte(privateKeyPEM))
|
||||
if block == nil || block.Type != "RSA PRIVATE KEY" {
|
||||
return nil, errors.New("failed to decode PEM block containing the private key")
|
||||
}
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
func aesDecrypt(ciphertext, key, iv []byte) ([]byte, error) {
|
||||
if len(key) != 16 && len(key) != 24 && len(key) != 32 {
|
||||
return nil, errors.New("invalid AES key length: must be 16, 24, or 32 bytes")
|
||||
}
|
||||
if len(iv) != aes.BlockSize {
|
||||
return nil, errors.New("invalid IV length: must be 16 bytes")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(ciphertext, ciphertext)
|
||||
ciphertext = pkcs7Unpad(ciphertext)
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func pkcs7Unpad(data []byte) []byte {
|
||||
length := len(data)
|
||||
padLength := int(data[length-1])
|
||||
return data[:length-padLength]
|
||||
}
|
||||
|
||||
func DecryptPassword(encryptedData string, privateKey *rsa.PrivateKey) (string, error) {
|
||||
parts := strings.Split(encryptedData, ":")
|
||||
if len(parts) != 3 {
|
||||
return "", errors.New("encrypted data format error")
|
||||
}
|
||||
keyCipher := parts[0]
|
||||
ivBase64 := parts[1]
|
||||
ciphertextBase64 := parts[2]
|
||||
|
||||
encryptedAESKey, err := base64.StdEncoding.DecodeString(keyCipher)
|
||||
if err != nil {
|
||||
return "", errors.New("failed to decode keyCipher")
|
||||
}
|
||||
|
||||
aesKey, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, encryptedAESKey)
|
||||
if err != nil {
|
||||
return "", errors.New("failed to decode AES Key")
|
||||
}
|
||||
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(ciphertextBase64)
|
||||
if err != nil {
|
||||
return "", errors.New("failed to decrypt the encrypted data")
|
||||
}
|
||||
iv, err := base64.StdEncoding.DecodeString(ivBase64)
|
||||
if err != nil {
|
||||
return "", errors.New("failed to decode the IV")
|
||||
}
|
||||
|
||||
password, err := aesDecrypt(ciphertext, aesKey, iv)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(password), nil
|
||||
}
|
||||
|
||||
func ExportPrivateKeyToPEM(privateKey *rsa.PrivateKey) string {
|
||||
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
|
||||
privateKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: privateKeyBytes,
|
||||
})
|
||||
return string(privateKeyPEM)
|
||||
}
|
||||
|
||||
func ExportPublicKeyToPEM(publicKey *rsa.PublicKey) (string, error) {
|
||||
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
publicKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: publicKeyBytes,
|
||||
})
|
||||
return string(publicKeyPEM), nil
|
||||
}
|
||||
|
@ -20,8 +20,8 @@
|
||||
"prettier": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-html": "^6.4.9",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-php": "^6.0.1",
|
||||
"@codemirror/language": "^6.10.2",
|
||||
"@codemirror/legacy-modes": "^6.4.0",
|
||||
@ -35,11 +35,13 @@
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"axios": "^1.7.2",
|
||||
"codemirror": "^6.0.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts": "^5.5.0",
|
||||
"element-plus": "^2.7.5",
|
||||
"fit2cloud-ui-plus": "^1.2.0",
|
||||
"highlight.js": "^11.9.0",
|
||||
"js-base64": "^3.7.7",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"md-editor-v3": "^2.11.3",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"nprogress": "^0.2.0",
|
||||
|
@ -4,6 +4,8 @@ import useClipboard from 'vue-clipboard3';
|
||||
const { toClipboard } = useClipboard();
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import JSEncrypt from 'jsencrypt';
|
||||
import CryptoJS from 'crypto-js';
|
||||
|
||||
export function deepCopy<T>(obj: any): T {
|
||||
let newObj: any;
|
||||
@ -695,3 +697,61 @@ export function getRuntimeLabel(type: string) {
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getCookie(name: string) {
|
||||
const value = `; ${document.cookie}`;
|
||||
const parts = value.split(`; ${name}=`);
|
||||
if (parts.length === 2) return parts.pop().split(';').shift();
|
||||
}
|
||||
|
||||
function rsaEncrypt(data: string, publicKey: string) {
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
const jsEncrypt = new JSEncrypt();
|
||||
jsEncrypt.setPublicKey(publicKey);
|
||||
return jsEncrypt.encrypt(data);
|
||||
}
|
||||
|
||||
function aesEncrypt(data: string, key: string) {
|
||||
const keyBytes = CryptoJS.enc.Utf8.parse(key);
|
||||
const iv = CryptoJS.lib.WordArray.random(16);
|
||||
const encrypted = CryptoJS.AES.encrypt(data, keyBytes, {
|
||||
iv: iv,
|
||||
mode: CryptoJS.mode.CBC,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
return iv.toString(CryptoJS.enc.Base64) + ':' + encrypted.toString();
|
||||
}
|
||||
|
||||
function urlDecode(value: string): string {
|
||||
return decodeURIComponent(value.replace(/\+/g, ' '));
|
||||
}
|
||||
|
||||
function generateAESKey(): string {
|
||||
const keyLength = 16;
|
||||
const randomBytes = new Uint8Array(keyLength);
|
||||
crypto.getRandomValues(randomBytes);
|
||||
return Array.from(randomBytes)
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
}
|
||||
|
||||
export const encryptPassword = (password: string) => {
|
||||
if (!password) {
|
||||
return '';
|
||||
}
|
||||
let rsaPublicKeyText = getCookie('panel_public_key');
|
||||
if (!rsaPublicKeyText) {
|
||||
console.log('RSA public key not found');
|
||||
return password;
|
||||
}
|
||||
rsaPublicKeyText = urlDecode(rsaPublicKeyText);
|
||||
|
||||
const aesKey = generateAESKey();
|
||||
rsaPublicKeyText = rsaPublicKeyText.replaceAll('"', '');
|
||||
const rsaPublicKey = atob(rsaPublicKeyText);
|
||||
const keyCipher = rsaEncrypt(aesKey, rsaPublicKey);
|
||||
const passwordCipher = aesEncrypt(password, aesKey);
|
||||
return `${keyCipher}:${passwordCipher}`;
|
||||
};
|
||||
|
@ -189,6 +189,7 @@ import { GlobalStore, MenuStore, TabsStore } from '@/store';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { getSettingInfo } from '@/api/modules/setting';
|
||||
import { encryptPassword } from '@/utils/util';
|
||||
|
||||
const i18n = useI18n();
|
||||
const themeConfig = computed(() => globalStore.themeConfig);
|
||||
@ -313,7 +314,7 @@ const login = (formEl: FormInstance | undefined) => {
|
||||
}
|
||||
let requestLoginForm = {
|
||||
name: loginForm.name,
|
||||
password: loginForm.password,
|
||||
password: encryptPassword(loginForm.password),
|
||||
ignoreCaptcha: globalStore.ignoreCaptcha,
|
||||
captcha: loginForm.captcha,
|
||||
captchaID: captcha.captchaID,
|
||||
@ -352,7 +353,6 @@ const login = (formEl: FormInstance | undefined) => {
|
||||
globalStore.ignoreCaptcha = false;
|
||||
errCaptcha.value = false;
|
||||
errAuthInfo.value = true;
|
||||
console.log('11111');
|
||||
}
|
||||
}
|
||||
loginVerify();
|
||||
@ -368,7 +368,7 @@ const mfaLogin = async (auto: boolean) => {
|
||||
if ((!auto && mfaLoginForm.code) || (auto && mfaLoginForm.code.length === 6)) {
|
||||
isLoggingIn = true;
|
||||
mfaLoginForm.name = loginForm.name;
|
||||
mfaLoginForm.password = loginForm.password;
|
||||
mfaLoginForm.password = encryptPassword(loginForm.password);
|
||||
try {
|
||||
await mfaLoginApi(mfaLoginForm);
|
||||
globalStore.setLogStatus(true);
|
||||
@ -464,173 +464,3 @@ onMounted(() => {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
<!-- <style scoped lang='scss' > .login-form {
|
||||
padding: 0 40px;
|
||||
.hide {
|
||||
width: 0;
|
||||
border: 0;
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.login-title {
|
||||
font-size: 30px;
|
||||
letter-spacing: 0;
|
||||
text-align: center;
|
||||
color: #646a73;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.no-border {
|
||||
:deep(.el-input__wrapper) {
|
||||
background: none !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: 0 !important;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
}
|
||||
}
|
||||
|
||||
.el-input {
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.login-captcha {
|
||||
margin-top: 10px;
|
||||
|
||||
:deep(.el-input__wrapper) {
|
||||
background: none !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: 0 !important;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
}
|
||||
.el-input {
|
||||
width: 50%;
|
||||
height: 44px;
|
||||
}
|
||||
img {
|
||||
width: 45%;
|
||||
height: 44px;
|
||||
margin-left: 5%;
|
||||
}
|
||||
}
|
||||
|
||||
.login-msg {
|
||||
margin-top: 10px;
|
||||
padding: 0 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-image {
|
||||
width: 480px;
|
||||
height: 480px;
|
||||
@media only screen and (max-width: 1280px) {
|
||||
height: 380px;
|
||||
}
|
||||
}
|
||||
|
||||
.submit {
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.forget-password {
|
||||
margin-top: 40px;
|
||||
padding: 0 40px;
|
||||
float: right;
|
||||
@media only screen and (max-width: 1280px) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-button {
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
margin-top: 10px;
|
||||
background-color: #005eeb;
|
||||
border-color: #005eeb;
|
||||
color: #ffffff;
|
||||
&:hover {
|
||||
--el-button-hover-border-color: #005eeb;
|
||||
}
|
||||
}
|
||||
|
||||
.demo {
|
||||
text-align: center;
|
||||
span {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
.login-form-header {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.title {
|
||||
color: #646a73;
|
||||
font-size: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.agree {
|
||||
white-space: pre-wrap;
|
||||
line-height: 14px;
|
||||
color: #005eeb;
|
||||
}
|
||||
|
||||
:deep(a) {
|
||||
color: #005eeb;
|
||||
&:hover {
|
||||
color: #005eeb95;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-checkbox__input .el-checkbox__inner) {
|
||||
background-color: #fff !important;
|
||||
border-color: #fff !important;
|
||||
}
|
||||
|
||||
:deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
|
||||
background-color: #005eeb !important;
|
||||
border-color: #005eeb !important;
|
||||
}
|
||||
|
||||
:deep(.el-checkbox__input.is-checked .el-checkbox__inner::after) {
|
||||
border-color: #fff !important;
|
||||
}
|
||||
|
||||
.agree-helper {
|
||||
min-height: 20px;
|
||||
margin-top: -20px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
:deep(.el-input__inner) {
|
||||
color: #000 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
outline: none;
|
||||
}
|
||||
.el-dropdown:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
.el-tooltip__trigger:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
:deep(.el-dropdown-menu__item:not(.is-disabled):hover) {
|
||||
color: #005eeb !important;
|
||||
background-color: #e5eefd !important;
|
||||
}
|
||||
:deep(.el-dropdown-menu__item:not(.is-disabled):focus) {
|
||||
color: #005eeb !important;
|
||||
background-color: #e5eefd !important;
|
||||
}
|
||||
|
||||
.login-footer-btn {
|
||||
.el-button--primary {
|
||||
border-color: #005eeb !important;
|
||||
background-color: #005eeb !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
-->
|
||||
|
@ -19,23 +19,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div>
|
||||
<div class="login-background" v-loading="loading">
|
||||
<div class="login-wrapper">
|
||||
<div :class="screenWidth > 1110 ? 'left inline-block' : ''">
|
||||
<div class="login-title">
|
||||
<span>{{ gStore.themeConfig.title || $t('setting.description') }}</span>
|
||||
</div>
|
||||
<img src="@/assets/images/1panel-login.png" alt="" v-if="screenWidth > 1110" />
|
||||
</div>
|
||||
<div :class="screenWidth > 1110 ? 'right inline-block' : ''">
|
||||
<div class="login-container">
|
||||
<LoginForm ref="loginRef"></LoginForm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="login">
|
||||
@ -48,7 +31,7 @@ import { getXpackSettingForTheme } from '@/utils/xpack';
|
||||
|
||||
const gStore = GlobalStore();
|
||||
const loading = ref();
|
||||
const backgroundOpacity = ref(1);
|
||||
const backgroundOpacity = ref(0.8);
|
||||
const backgroundImage = ref(new URL('', import.meta.url).href);
|
||||
const logoImage = ref(new URL('@/assets/images/1panel-login.png', import.meta.url).href);
|
||||
|
||||
@ -91,89 +74,3 @@ onMounted(() => {
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- <style scoped lang="scss">
|
||||
@mixin login-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.login-background {
|
||||
height: 100vh;
|
||||
background: url(@/assets/images/1panel-login-bg.png) no-repeat,
|
||||
radial-gradient(153.25% 257.2% at 118.99% 181.67%, rgba(50, 132, 255, 0.2) 0%, rgba(82, 120, 255, 0) 100%)
|
||||
/* warning: gradient uses a rotation that is not supported by CSS and may not behave as expected */,
|
||||
radial-gradient(123.54% 204.83% at 25.87% 195.17%, rgba(111, 76, 253, 0.15) 0%, rgba(122, 76, 253, 0) 78.85%)
|
||||
/* warning: gradient uses a rotation that is not supported by CSS and may not behave as expected */,
|
||||
linear-gradient(0deg, rgba(0, 94, 235, 0.03), rgba(0, 94, 235, 0.03)),
|
||||
radial-gradient(109.58% 109.58% at 31.53% -36.58%, rgba(0, 94, 235, 0.3) 0%, rgba(0, 94, 235, 0) 100%)
|
||||
/* warning: gradient uses a rotation that is not supported by CSS and may not behave as expected */,
|
||||
rgba(0, 57, 142, 0.05);
|
||||
|
||||
.login-wrapper {
|
||||
padding-top: 8%;
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
// @media only screen and (max-width: 1440px) {
|
||||
// width: 100%;
|
||||
// padding-top: 6%;
|
||||
// }
|
||||
.left {
|
||||
vertical-align: middle;
|
||||
text-align: right;
|
||||
width: 60%;
|
||||
img {
|
||||
object-fit: contain;
|
||||
width: 100%;
|
||||
@media only screen and (min-width: 1440px) {
|
||||
width: 85%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
vertical-align: middle;
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
|
||||
.login-title {
|
||||
text-align: right;
|
||||
margin-right: 10%;
|
||||
span:first-child {
|
||||
color: $primary-color;
|
||||
font-size: 40px;
|
||||
font-family: pingFangSC-Regular;
|
||||
font-weight: 600;
|
||||
@media only screen and (max-width: 768px) {
|
||||
font-size: 35px;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 1110px) {
|
||||
margin-bottom: 20px;
|
||||
font-size: 35px;
|
||||
text-align: center;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.login-container {
|
||||
margin-top: 40px;
|
||||
padding: 40px 0;
|
||||
width: 390px;
|
||||
box-sizing: border-box;
|
||||
background-color: rgba(255, 255, 255, 0.55);
|
||||
border-radius: 4px;
|
||||
box-shadow: 2px 4px 22px rgba(0, 94, 235, 0.2);
|
||||
@media only screen and (max-width: 1440px) {
|
||||
margin-top: 60px;
|
||||
}
|
||||
@media only screen and (max-width: 1110px) {
|
||||
margin: 60px auto 0;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style> -->
|
||||
|
Loading…
x
Reference in New Issue
Block a user