feat(controlled): 实现设备激活与安全认证流程
- 添加API客户端、加密存储和provision/token激活逻辑 - WebSocket改用Bearer令牌认证,移除REGISTER请求 - 设备ID改为服务端下发,支持令牌刷新和强制下线处理 - 新增deviceSecret加密存储和accessToken自动刷新 - 更新设备ID获取方式为出厂SN,添加安全存储依赖
This commit is contained in:
103
WebRTCControllerWeb/src/components/LoginPanel.vue
Normal file
103
WebRTCControllerWeb/src/components/LoginPanel.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { store, login, register, connectSignaling } from '../store/controllerStore';
|
||||
|
||||
const username = ref('');
|
||||
const password = ref('');
|
||||
const showRegister = ref(false);
|
||||
const regUsername = ref('');
|
||||
const regPassword = ref('');
|
||||
const regBusy = ref(false);
|
||||
const regError = ref('');
|
||||
|
||||
async function onLogin() {
|
||||
const ok = await login(username.value.trim(), password.value);
|
||||
if (ok) {
|
||||
await connectSignaling();
|
||||
}
|
||||
}
|
||||
|
||||
async function onRegister() {
|
||||
regError.value = '';
|
||||
regBusy.value = true;
|
||||
try {
|
||||
await register({ username: regUsername.value.trim(), password: regPassword.value });
|
||||
// 注册成功后直接登录并连接。
|
||||
username.value = regUsername.value.trim();
|
||||
password.value = regPassword.value;
|
||||
showRegister.value = false;
|
||||
await onLogin();
|
||||
} catch (e) {
|
||||
regError.value = e.message || '注册失败';
|
||||
} finally {
|
||||
regBusy.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="login-mask">
|
||||
<div class="login-card">
|
||||
<div class="login-brand">WebRTC 网页远程控制</div>
|
||||
<div class="login-sub">登录后才能连接信令服务器并发起远程控制</div>
|
||||
|
||||
<form v-if="!showRegister" class="login-form" @submit.prevent="onLogin">
|
||||
<label>用户名
|
||||
<input v-model="username" type="text" autocomplete="username" placeholder="请输入用户名" />
|
||||
</label>
|
||||
<label>密码
|
||||
<input v-model="password" type="password" autocomplete="current-password" placeholder="请输入密码" />
|
||||
</label>
|
||||
<p v-if="store.loginError" class="login-err">{{ store.loginError }}</p>
|
||||
<button class="btn primary" type="submit" :disabled="store.loginBusy">
|
||||
{{ store.loginBusy ? '登录中…' : '登录' }}
|
||||
</button>
|
||||
<button class="btn link" type="button" @click="showRegister = true">没有账号?注册</button>
|
||||
</form>
|
||||
|
||||
<form v-else class="login-form" @submit.prevent="onRegister">
|
||||
<label>用户名
|
||||
<input v-model="regUsername" type="text" autocomplete="username" placeholder="设置用户名" />
|
||||
</label>
|
||||
<label>密码
|
||||
<input v-model="regPassword" type="password" autocomplete="new-password" placeholder="设置密码(至少12位,含大小写/数字/特殊字符)" />
|
||||
</label>
|
||||
<p v-if="regError" class="login-err">{{ regError }}</p>
|
||||
<button class="btn primary" type="submit" :disabled="regBusy">
|
||||
{{ regBusy ? '注册中…' : '注册并登录' }}
|
||||
</button>
|
||||
<button class="btn link" type="button" @click="showRegister = false">返回登录</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.login-mask {
|
||||
position: fixed; inset: 0;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: radial-gradient(circle at 30% 20%, #1e293b, #0f172a 70%);
|
||||
}
|
||||
.login-card {
|
||||
width: 360px; padding: 32px;
|
||||
background: #1e293b; border: 1px solid #334155; border-radius: 16px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,.45); color: #e2e8f0;
|
||||
}
|
||||
.login-brand { font-size: 20px; font-weight: 700; color: #38bdf8; }
|
||||
.login-sub { margin: 8px 0 20px; font-size: 13px; color: #94a3b8; }
|
||||
.login-form { display: flex; flex-direction: column; gap: 14px; }
|
||||
.login-form label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: #cbd5e1; }
|
||||
.login-form input {
|
||||
padding: 10px 12px; border-radius: 8px; border: 1px solid #334155;
|
||||
background: #0f172a; color: #e2e8f0; font-size: 14px;
|
||||
}
|
||||
.login-form input:focus { outline: none; border-color: #38bdf8; }
|
||||
.login-err { color: #f87171; font-size: 13px; margin: 0; }
|
||||
.btn.primary {
|
||||
padding: 11px; border: none; border-radius: 8px; cursor: pointer;
|
||||
background: #38bdf8; color: #0f172a; font-weight: 600; font-size: 14px;
|
||||
}
|
||||
.btn.primary:disabled { opacity: .6; cursor: default; }
|
||||
.btn.link { background: none; border: none; color: #94a3b8; cursor: pointer; font-size: 13px; }
|
||||
.btn.link:hover { color: #38bdf8; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user