110 lines
2.7 KiB
Vue
110 lines
2.7 KiB
Vue
<template>
|
||
<div>
|
||
<t-form labelWidth="0">
|
||
<t-form-item>
|
||
<t-input v-model="username" placeholder="请输入用户名"></t-input>
|
||
</t-form-item>
|
||
<t-form-item>
|
||
<t-input v-model="password" placeholder="请输入密码"></t-input>
|
||
</t-form-item>
|
||
<t-space class="login-button-box" direction="vertical" size="small">
|
||
<t-button class="login-button" theme="primary" :loading="loading" @click="handleLogin"
|
||
>登录</t-button
|
||
>
|
||
<t-button
|
||
class="login-button"
|
||
theme="default"
|
||
:disabled="!passkey"
|
||
@click="handleUsePasskey"
|
||
>使用通行密钥</t-button
|
||
>
|
||
<t-link @click="handleRegister">没有账号?去注册</t-link>
|
||
</t-space>
|
||
</t-form>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { reactive, ref, toRefs } from 'vue'
|
||
import { assertion, assertion_options, login } from '@/api/auth.ts'
|
||
|
||
const emits = defineEmits(['complete', 'register'])
|
||
const { username, password } = toRefs(
|
||
reactive({
|
||
username: '',
|
||
password: '',
|
||
}),
|
||
)
|
||
|
||
const loading = ref(false)
|
||
function handleLogin() {
|
||
loading.value = true
|
||
login({ user: username.value, password: password.value })
|
||
.then((x: any) => {
|
||
if (x.code === 200) {
|
||
emits('complete')
|
||
}
|
||
})
|
||
.catch((e: any) => {
|
||
console.error(e)
|
||
})
|
||
.finally(() => {
|
||
loading.value = false
|
||
})
|
||
}
|
||
|
||
const passkey = ref(true)
|
||
|
||
if (!navigator.credentials) {
|
||
passkey.value = false
|
||
}
|
||
function base64ToArrayBuffer(base64: string): ArrayBuffer {
|
||
// 处理base64url格式:替换字符并添加填充
|
||
let normalizedBase64 = base64.replace(/-/g, '+').replace(/_/g, '/')
|
||
// 添加必要的填充
|
||
while (normalizedBase64.length % 4 !== 0) {
|
||
normalizedBase64 += '='
|
||
}
|
||
|
||
try {
|
||
const binaryString = atob(normalizedBase64)
|
||
const bytes = new Uint8Array(binaryString.length)
|
||
for (let i = 0; i < binaryString.length; i++) {
|
||
bytes[i] = binaryString.charCodeAt(i)
|
||
}
|
||
return bytes.buffer
|
||
} catch (e) {
|
||
console.error('Base64 decoding failed:', e, 'Input was:', base64)
|
||
throw e
|
||
}
|
||
}
|
||
function handleUsePasskey() {
|
||
assertion_options().then((x: any) => {
|
||
const parse = JSON.parse(x.data)
|
||
parse.publicKey.challenge = base64ToArrayBuffer(parse.publicKey?.challenge)
|
||
console.log(parse)
|
||
navigator.credentials.get(parse).then((x: any) => {
|
||
assertion(x).then((x: any) => {
|
||
if (x.code === 200) {
|
||
emits('complete')
|
||
}
|
||
})
|
||
})
|
||
})
|
||
// emits('complete')
|
||
}
|
||
function handleRegister() {
|
||
emits('register')
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.login-button-box {
|
||
width: 100%;
|
||
.login-button {
|
||
display: block;
|
||
width: 100%;
|
||
}
|
||
}
|
||
</style>
|