feat(controlled): 实现设备激活与安全认证流程

- 添加API客户端、加密存储和provision/token激活逻辑
- WebSocket改用Bearer令牌认证,移除REGISTER请求
- 设备ID改为服务端下发,支持令牌刷新和强制下线处理
- 新增deviceSecret加密存储和accessToken自动刷新
- 更新设备ID获取方式为出厂SN,添加安全存储依赖
This commit is contained in:
2026-08-01 15:13:12 +08:00
parent 376a2c1217
commit 6eb2c7321a
124 changed files with 10535 additions and 862 deletions

View 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>