1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

feat: Added password encryption for login functionality (#7764)

This commit is contained in:
zhengkunwang 2025-01-23 18:09:12 +08:00 committed by GitHub
parent 4f57dfc76e
commit aaaa5980b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 277 additions and 35 deletions

View File

@ -15,6 +15,7 @@ type DBOption func(*gorm.DB) *gorm.DB
type ICommonRepo interface { type ICommonRepo interface {
WithByID(id uint) DBOption WithByID(id uint) DBOption
WithByName(name string) DBOption WithByName(name string) DBOption
WithByLowerName(name string) DBOption
WithByType(tp string) DBOption WithByType(tp string) DBOption
WithOrderBy(orderStr string) DBOption WithOrderBy(orderStr string) DBOption
WithOrderRuleBy(orderBy, order string) DBOption WithOrderRuleBy(orderBy, order string) DBOption
@ -45,6 +46,12 @@ func (c *CommonRepo) WithByName(name string) DBOption {
} }
} }
func (c *CommonRepo) WithByLowerName(name string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("LOWER(name) = LOWER(?)", name)
}
}
func (c *CommonRepo) WithByDate(startTime, endTime time.Time) DBOption { func (c *CommonRepo) WithByDate(startTime, endTime time.Time) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
return g.Where("start_time > ? AND start_time < ?", startTime, endTime) return g.Where("start_time > ? AND start_time < ?", startTime, endTime)

View File

@ -16,6 +16,7 @@ type ISettingRepo interface {
Create(key, value string) error Create(key, value string) error
Update(key, value string) error Update(key, value string) error
WithByKey(key string) DBOption WithByKey(key string) DBOption
UpdateOrCreate(key, value string) error
CreateMonitorBase(model model.MonitorBase) error CreateMonitorBase(model model.MonitorBase) error
BatchCreateMonitorIO(ioList []model.MonitorIO) error BatchCreateMonitorIO(ioList []model.MonitorIO) error
@ -85,3 +86,7 @@ func (u *SettingRepo) DelMonitorIO(timeForDelete time.Time) error {
func (u *SettingRepo) DelMonitorNet(timeForDelete time.Time) error { func (u *SettingRepo) DelMonitorNet(timeForDelete time.Time) error {
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
} }
func (u *SettingRepo) UpdateOrCreate(key, value string) error {
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Assign(model.Setting{Key: key, Value: value}).FirstOrCreate(&model.Setting{}).Error
}

View File

@ -326,7 +326,8 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
err = buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil) err = buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)
return return
} }
if list, _ := appInstallRepo.ListBy(commonRepo.WithByName(req.Name)); len(list) > 0 {
if list, _ := appInstallRepo.ListBy(commonRepo.WithByLowerName(req.Name)); len(list) > 0 {
err = buserr.New(constant.ErrAppNameExist) err = buserr.New(constant.ErrAppNameExist)
return return
} }

View File

@ -3,8 +3,6 @@ package service
import ( import (
"crypto/hmac" "crypto/hmac"
"encoding/base64" "encoding/base64"
"strconv"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
@ -15,6 +13,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/pkg/errors" "github.com/pkg/errors"
"strconv"
) )
type AuthService struct{} type AuthService struct{}
@ -38,16 +37,11 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
if err != nil { if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
} }
passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password")) if nameSetting.Value != info.Name {
if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
}
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
if err != nil {
return nil, constant.ErrAuth return nil, constant.ErrAuth
} }
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name { if err = checkPassword(info.Password); err != nil {
return nil, constant.ErrAuth return nil, err
} }
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance")) entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
if err != nil { if err != nil {
@ -83,17 +77,12 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin
if err != nil { if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
} }
passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password")) if nameSetting.Value != info.Name {
if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
}
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
if err != nil {
return nil, err
}
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
return nil, constant.ErrAuth return nil, constant.ErrAuth
} }
if err = checkPassword(info.Password); err != nil {
return nil, err
}
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance")) entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
if err != nil { if err != nil {
return nil, err return nil, err
@ -219,3 +208,28 @@ func (u *AuthService) IsLogin(c *gin.Context) bool {
} }
return true return true
} }
func checkPassword(password string) error {
priKey, _ := settingRepo.Get(settingRepo.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(settingRepo.WithByKey("Password"))
if err != nil {
return errors.WithMessage(constant.ErrRecordNotFound, err.Error())
}
existPassword, err := encrypt.StringDecrypt(passwordSetting.Value)
if err != nil {
return err
}
if !hmac.Equal([]byte(loginPassword), []byte(existPassword)) {
return constant.ErrAuth
}
return nil
}

View File

@ -1,6 +1,8 @@
package service package service
import ( import (
"crypto/rand"
"crypto/rsa"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
@ -42,6 +44,7 @@ type ISettingService interface {
HandlePasswordExpired(c *gin.Context, old, new string) error HandlePasswordExpired(c *gin.Context, old, new string) error
GenerateApiKey() (string, error) GenerateApiKey() (string, error)
UpdateApiConfig(req dto.ApiInterfaceConfig) error UpdateApiConfig(req dto.ApiInterfaceConfig) error
GenerateRSAKey() error
} }
func NewISettingService() ISettingService { func NewISettingService() ISettingService {
@ -516,3 +519,47 @@ func (u *SettingService) UpdateApiConfig(req dto.ApiInterfaceConfig) error {
global.CONF.System.ApiKeyValidityTime = req.ApiKeyValidityTime global.CONF.System.ApiKeyValidityTime = req.ApiKeyValidityTime
return nil return 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
}
func (u *SettingService) GenerateRSAKey() error {
priKey, _ := settingRepo.Get(settingRepo.WithByKey("PASSWORD_PRIVATE_KEY"))
pubKey, _ := settingRepo.Get(settingRepo.WithByKey("PASSWORD_PUBLIC_KEY"))
if priKey.Value != "" && pubKey.Value != "" {
return nil
}
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return err
}
privateKeyPEM := exportPrivateKeyToPEM(privateKey)
publicKeyPEM, err := exportPublicKeyToPEM(&privateKey.PublicKey)
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
}

View File

@ -11,6 +11,7 @@ func Init() {
go syncInstalledApp() go syncInstalledApp()
go syncRuntime() go syncRuntime()
go syncSSL() go syncSSL()
generateKey()
} }
func syncApp() { func syncApp() {
@ -38,3 +39,9 @@ func syncSSL() {
global.LOG.Errorf("sync ssl status error : %s", err.Error()) global.LOG.Errorf("sync ssl status error : %s", err.Error())
} }
} }
func generateKey() {
if err := service.NewISettingService().GenerateRSAKey(); err != nil {
global.LOG.Errorf("generate rsa key error : %s", err.Error())
}
}

View File

@ -113,9 +113,7 @@ func setWebStatic(rootRouter *gin.RouterGroup) {
rootRouter.StaticFS("/public", http.FS(web.Favicon)) rootRouter.StaticFS("/public", http.FS(web.Favicon))
rootRouter.StaticFS("/favicon.ico", http.FS(web.Favicon)) rootRouter.StaticFS("/favicon.ico", http.FS(web.Favicon))
rootRouter.Static("/api/v1/images", "./uploads") rootRouter.Static("/api/v1/images", "./uploads")
rootRouter.Use(func(c *gin.Context) {
c.Next()
})
rootRouter.GET("/assets/*filepath", func(c *gin.Context) { rootRouter.GET("/assets/*filepath", func(c *gin.Context) {
c.Writer.Header().Set("Cache-Control", fmt.Sprintf("private, max-age=%d", 3600)) c.Writer.Header().Set("Cache-Control", fmt.Sprintf("private, max-age=%d", 3600))
staticServer := http.FileServer(http.FS(web.Assets)) staticServer := http.FileServer(http.FS(web.Assets))
@ -158,6 +156,7 @@ func Routers() *gin.Engine {
Router.Use(middleware.WhiteAllow()) Router.Use(middleware.WhiteAllow())
Router.Use(middleware.BindDomain()) Router.Use(middleware.BindDomain())
Router.Use(middleware.SetPasswordPublicKey())
Router.NoRoute(func(c *gin.Context) { Router.NoRoute(func(c *gin.Context) {
if checkFrontendPath(c) { if checkFrontendPath(c) {

View File

@ -0,0 +1,22 @@
package middleware
import (
"encoding/base64"
"github.com/1Panel-dev/1Panel/backend/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(settingRepo.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()
}
}

View File

@ -5,9 +5,14 @@ import (
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/rand" "crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/pem"
"errors"
"fmt" "fmt"
"io" "io"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
@ -102,3 +107,75 @@ func aesDecryptWithSalt(key, ciphertext []byte) ([]byte, error) {
ciphertext = unPadding(ciphertext) ciphertext = unPadding(ciphertext)
return ciphertext, nil 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
}

View File

@ -21,9 +21,9 @@
}, },
"dependencies": { "dependencies": {
"@codemirror/lang-html": "^6.4.9", "@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-yaml": "^6.1.2",
"@codemirror/lang-javascript": "^6.2.2", "@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-php": "^6.0.1", "@codemirror/lang-php": "^6.0.1",
"@codemirror/lang-yaml": "^6.1.2",
"@codemirror/language": "^6.10.2", "@codemirror/language": "^6.10.2",
"@codemirror/legacy-modes": "^6.4.0", "@codemirror/legacy-modes": "^6.4.0",
"@codemirror/state": "^6.4.1", "@codemirror/state": "^6.4.1",
@ -37,11 +37,13 @@
"@xterm/xterm": "^5.5.0", "@xterm/xterm": "^5.5.0",
"axios": "^1.7.2", "axios": "^1.7.2",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"crypto-js": "^4.2.0",
"echarts": "^5.5.0", "echarts": "^5.5.0",
"element-plus": "^2.7.5", "element-plus": "^2.7.5",
"fit2cloud-ui-plus": "^1.2.0", "fit2cloud-ui-plus": "^1.2.0",
"highlight.js": "^11.9.0", "highlight.js": "^11.9.0",
"js-base64": "^3.7.7", "js-base64": "^3.7.7",
"jsencrypt": "^3.3.2",
"md-editor-v3": "^2.11.3", "md-editor-v3": "^2.11.3",
"monaco-editor": "^0.50.0", "monaco-editor": "^0.50.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@ -54,11 +56,12 @@
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",
"vue-codemirror": "^6.1.1", "vue-codemirror": "^6.1.1",
"vue-demi": "^0.14.6", "vue-demi": "^0.14.6",
"vue-i18n": "^10.0.5", "vue-i18n": "^9.13.1",
"vue-router": "^4.3.3" "vue-router": "^4.3.3",
"vue-virtual-scroller": "^2.0.0-beta.8"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.14.2", "@types/node": "^20.15.0",
"@typescript-eslint/eslint-plugin": "^5.22.0", "@typescript-eslint/eslint-plugin": "^5.22.0",
"@typescript-eslint/parser": "^5.22.0", "@typescript-eslint/parser": "^5.22.0",
"@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue": "^5.0.5",
@ -73,7 +76,7 @@
"postcss": "^8.4.31", "postcss": "^8.4.31",
"postcss-html": "^1.4.1", "postcss-html": "^1.4.1",
"prettier": "^2.6.2", "prettier": "^2.6.2",
"rollup-plugin-visualizer": "^5.5.4", "rollup-plugin-visualizer": "^5.14.0",
"sass": "^1.77.8", "sass": "^1.77.8",
"standard-version": "^9.5.0", "standard-version": "^9.5.0",
"stylelint": "^15.10.1", "stylelint": "^15.10.1",
@ -81,7 +84,7 @@
"typescript": "^4.5.4", "typescript": "^4.5.4",
"unplugin-auto-import": "^0.16.4", "unplugin-auto-import": "^0.16.4",
"unplugin-vue-components": "^0.25.0", "unplugin-vue-components": "^0.25.0",
"vite": "^5.2.13", "vite": "^6.0.7",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-eslint": "^1.8.1", "vite-plugin-eslint": "^1.8.1",
"vite-plugin-html": "^3.2.2", "vite-plugin-html": "^3.2.2",
@ -90,7 +93,7 @@
"vue-tsc": "^0.29.8" "vue-tsc": "^0.29.8"
}, },
"overrides": { "overrides": {
"esbuild": "npm:esbuild-wasm@latest" "esbuild": "npm:esbuild-wasm@0.24.2"
}, },
"config": { "config": {
"commitizen": { "commitizen": {

View File

@ -289,7 +289,7 @@ const checkAppName = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) { if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.appName'))); callback(new Error(i18n.global.t('commons.rule.appName')));
} else { } else {
const reg = /^(?![_-])[a-z0-9_-]{1,29}[a-zA-Z0-9]$/; const reg = /^(?![_-])[a-zA-Z0-9_-]{1,29}[a-zA-Z0-9]$/;
if (!reg.test(value) && value !== '') { if (!reg.test(value) && value !== '') {
callback(new Error(i18n.global.t('commons.rule.appName'))); callback(new Error(i18n.global.t('commons.rule.appName')));
} else { } else {

View File

@ -1451,8 +1451,7 @@ const message = {
confDockerProxy: 'Configurar proxy do Docker', confDockerProxy: 'Configurar proxy do Docker',
restartNowHelper: 'Configurar o proxy do Docker exige reiniciar o serviço Docker.', restartNowHelper: 'Configurar o proxy do Docker exige reiniciar o serviço Docker.',
restartNow: 'Reiniciar imediatamente', restartNow: 'Reiniciar imediatamente',
systemIPWarning: systemIPWarning: 'O endereço do sistema não está definido no momento. Defina-o primeiro no painel de controle.',
'O endereço do sistema não está definido no momento. Defina-o primeiro no painel de controle.',
systemIPWarning1: systemIPWarning1:
'O endereço do sistema atual está definido como {0}, e o redirecionamento rápido não é possível!', 'O endereço do sistema atual está definido como {0}, e o redirecionamento rápido não é possível!',
defaultNetwork: 'Placa de rede', defaultNetwork: 'Placa de rede',

View File

@ -3,6 +3,8 @@ import i18n from '@/lang';
import useClipboard from 'vue-clipboard3'; import useClipboard from 'vue-clipboard3';
const { toClipboard } = useClipboard(); const { toClipboard } = useClipboard();
import { MsgError, MsgSuccess } from '@/utils/message'; import { MsgError, MsgSuccess } from '@/utils/message';
import JSEncrypt from 'jsencrypt';
import CryptoJS from 'crypto-js';
export function deepCopy<T>(obj: any): T { export function deepCopy<T>(obj: any): T {
let newObj: any; let newObj: any;
@ -615,3 +617,61 @@ export const escapeProxyURL = (url: string): string => {
return url.replace(/[\/:?#[\]@!$&'()*+,;=%~]/g, (match) => encodeMap[match] || match); return url.replace(/[\/:?#[\]@!$&'()*+,;=%~]/g, (match) => encodeMap[match] || match);
}; };
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}`;
};

View File

@ -180,6 +180,7 @@ import { MsgSuccess } from '@/utils/message';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { getSettingInfo } from '@/api/modules/setting'; import { getSettingInfo } from '@/api/modules/setting';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import { encryptPassword } from '@/utils/util';
const i18n = useI18n(); const i18n = useI18n();
const themeConfig = computed(() => globalStore.themeConfig); const themeConfig = computed(() => globalStore.themeConfig);
@ -314,7 +315,7 @@ const login = (formEl: FormInstance | undefined) => {
} }
let requestLoginForm = { let requestLoginForm = {
name: loginForm.name, name: loginForm.name,
password: loginForm.password, password: encryptPassword(loginForm.password),
ignoreCaptcha: globalStore.ignoreCaptcha, ignoreCaptcha: globalStore.ignoreCaptcha,
captcha: loginForm.captcha, captcha: loginForm.captcha,
captchaID: captcha.captchaID, captchaID: captcha.captchaID,
@ -370,7 +371,7 @@ const mfaLogin = async (auto: boolean) => {
if ((!auto && mfaLoginForm.code) || (auto && mfaLoginForm.code.length === 6)) { if ((!auto && mfaLoginForm.code) || (auto && mfaLoginForm.code.length === 6)) {
isLoggingIn = true; isLoggingIn = true;
mfaLoginForm.name = loginForm.name; mfaLoginForm.name = loginForm.name;
mfaLoginForm.password = loginForm.password; mfaLoginForm.password = encryptPassword(loginForm.password);
const res = await mfaLoginApi(mfaLoginForm); const res = await mfaLoginApi(mfaLoginForm);
if (res.code === 406) { if (res.code === 406) {
errMfaInfo.value = true; errMfaInfo.value = true;