feat: Admin 登录密码加密传输
This commit is contained in:
parent
95c194e842
commit
5f018449e6
@ -7,6 +7,11 @@ export namespace AuthApi {
|
|||||||
username?: string;
|
username?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LoginRequest {
|
||||||
|
encryptedPassword?: string;
|
||||||
|
username?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/** 登录接口返回值 */
|
/** 登录接口返回值 */
|
||||||
export interface LoginResult {
|
export interface LoginResult {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
@ -33,15 +38,90 @@ export namespace AuthApi {
|
|||||||
};
|
};
|
||||||
user?: Record<string, any>;
|
user?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PasswordPublicKeyResult {
|
||||||
|
algorithm: 'RSA-OAEP';
|
||||||
|
hash: 'SHA-256';
|
||||||
|
publicKey: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pemToArrayBuffer(pem: string) {
|
||||||
|
const base64 = pem
|
||||||
|
.replaceAll('-----BEGIN PUBLIC KEY-----', '')
|
||||||
|
.replaceAll('-----END PUBLIC KEY-----', '')
|
||||||
|
.replaceAll(/\s/g, '');
|
||||||
|
const binary = window.atob(base64);
|
||||||
|
const bytes = new Uint8Array(binary.length);
|
||||||
|
|
||||||
|
for (let index = 0; index < binary.length; index += 1) {
|
||||||
|
bytes[index] = binary.codePointAt(index) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrayBufferToBase64(buffer: ArrayBuffer) {
|
||||||
|
const bytes = new Uint8Array(buffer);
|
||||||
|
let binary = '';
|
||||||
|
|
||||||
|
bytes.forEach((byte) => {
|
||||||
|
binary += String.fromCodePoint(byte);
|
||||||
|
});
|
||||||
|
|
||||||
|
return window.btoa(binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function encryptPassword(password: string) {
|
||||||
|
const { hash, publicKey } = await getPasswordPublicKeyApi();
|
||||||
|
const cryptoKey = await window.crypto.subtle.importKey(
|
||||||
|
'spki',
|
||||||
|
pemToArrayBuffer(publicKey),
|
||||||
|
{
|
||||||
|
hash,
|
||||||
|
name: 'RSA-OAEP',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
['encrypt'],
|
||||||
|
);
|
||||||
|
const encrypted = await window.crypto.subtle.encrypt(
|
||||||
|
{
|
||||||
|
name: 'RSA-OAEP',
|
||||||
|
},
|
||||||
|
cryptoKey,
|
||||||
|
new TextEncoder().encode(password),
|
||||||
|
);
|
||||||
|
|
||||||
|
return arrayBufferToBase64(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildLoginRequest(data: AuthApi.LoginParams) {
|
||||||
|
return {
|
||||||
|
encryptedPassword: await encryptPassword(data.password || ''),
|
||||||
|
username: data.username,
|
||||||
|
} satisfies AuthApi.LoginRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取登录密码加密公钥
|
||||||
|
*/
|
||||||
|
export async function getPasswordPublicKeyApi() {
|
||||||
|
return requestClient.get<AuthApi.PasswordPublicKeyResult>(
|
||||||
|
'/auth/password-public-key',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录
|
* 登录
|
||||||
*/
|
*/
|
||||||
export async function loginApi(data: AuthApi.LoginParams) {
|
export async function loginApi(data: AuthApi.LoginParams) {
|
||||||
return requestClient.post<AuthApi.LoginResult>('/auth/login', data, {
|
return requestClient.post<AuthApi.LoginResult>(
|
||||||
withCredentials: true,
|
'/auth/login',
|
||||||
});
|
await buildLoginRequest(data),
|
||||||
|
{
|
||||||
|
withCredentials: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { VbenFormSchema } from '@vben/common-ui';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
import type { BasicOption, Recordable } from '@vben/types';
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
import { computed, markRaw, useTemplateRef } from 'vue';
|
import { computed, markRaw, useTemplateRef } from 'vue';
|
||||||
|
|
||||||
@ -13,57 +13,13 @@ defineOptions({ name: 'Login' });
|
|||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
const MOCK_USER_OPTIONS: BasicOption[] = [
|
|
||||||
{
|
|
||||||
label: 'Super',
|
|
||||||
value: 'vben',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Admin',
|
|
||||||
value: 'admin',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'User',
|
|
||||||
value: 'jack',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const formSchema = computed((): VbenFormSchema[] => {
|
const formSchema = computed((): VbenFormSchema[] => {
|
||||||
return [
|
return [
|
||||||
{
|
|
||||||
component: 'VbenSelect',
|
|
||||||
componentProps: {
|
|
||||||
options: MOCK_USER_OPTIONS,
|
|
||||||
placeholder: $t('authentication.selectAccount'),
|
|
||||||
},
|
|
||||||
fieldName: 'selectAccount',
|
|
||||||
label: $t('authentication.selectAccount'),
|
|
||||||
rules: z
|
|
||||||
.string()
|
|
||||||
.min(1, { message: $t('authentication.selectAccount') })
|
|
||||||
.optional()
|
|
||||||
.default('vben'),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
component: 'VbenInput',
|
component: 'VbenInput',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
placeholder: $t('authentication.usernameTip'),
|
placeholder: $t('authentication.usernameTip'),
|
||||||
},
|
},
|
||||||
dependencies: {
|
|
||||||
trigger(values, form) {
|
|
||||||
if (values.selectAccount) {
|
|
||||||
const findUser = MOCK_USER_OPTIONS.find(
|
|
||||||
(item) => item.value === values.selectAccount,
|
|
||||||
);
|
|
||||||
if (findUser) {
|
|
||||||
form.setValues({
|
|
||||||
username: findUser.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
triggerFields: ['selectAccount'],
|
|
||||||
},
|
|
||||||
fieldName: 'username',
|
fieldName: 'username',
|
||||||
label: $t('authentication.username'),
|
label: $t('authentication.username'),
|
||||||
rules: z.string().min(1, { message: $t('authentication.usernameTip') }),
|
rules: z.string().min(1, { message: $t('authentication.usernameTip') }),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user