fix(mobile): harden stored auth credentials and login response validation

- Treat 'undefined'/'null' localStorage values as absent; clear incomplete
  token/clientId pairs and prompt re-login instead of sending broken headers
- Gate authenticated UI on both token and clientId via isAuthenticated
- Reject login responses missing access_token or client_id
This commit is contained in:
2026-07-08 12:22:33 +08:00
parent eeffbaf752
commit e8d90609c1
+25 -8
View File
@@ -1,6 +1,6 @@
<template>
<main class="mobile-aihr">
<section v-if="!authToken" class="page auth-page">
<section v-if="!isAuthenticated" class="page auth-page">
<header class="top auth-top">
<div class="top-row">
<h1>AI人力助手</h1>
@@ -1604,6 +1604,17 @@ const tokenKey = 'aihr_mobile_access_token';
const clientIdKey = 'aihr_mobile_client_id';
const phoneKey = 'aihr_mobile_phone';
const phonePattern = /^1[3-9]\d{9}$/;
const readStoredCredential = (key: string) => {
const value = localStorage.getItem(key)?.trim() || '';
return value === 'undefined' || value === 'null' ? '' : value;
};
const initialAuthToken = readStoredCredential(tokenKey);
const initialClientId = readStoredCredential(clientIdKey);
const hasIncompleteStoredAuth = Boolean(initialAuthToken || initialClientId) && !(initialAuthToken && initialClientId);
if (hasIncompleteStoredAuth) {
localStorage.removeItem(tokenKey);
localStorage.removeItem(clientIdKey);
}
const practiceScenarios: PracticeScenario[] = [
{
id: 'complaint-water',
@@ -1668,9 +1679,10 @@ const tapped = ref('');
const toastMessage = ref('');
const phone = ref(localStorage.getItem(phoneKey) || '');
const smsCode = ref('');
const authToken = ref(localStorage.getItem(tokenKey) || '');
const clientId = ref(localStorage.getItem(clientIdKey) || '');
const authMessage = ref('');
const authToken = ref(hasIncompleteStoredAuth ? '' : initialAuthToken);
const clientId = ref(hasIncompleteStoredAuth ? '' : initialClientId);
const authMessage = ref(hasIncompleteStoredAuth ? '登录已过期,请重新登录' : '');
const isAuthenticated = computed(() => Boolean(authToken.value && clientId.value));
const sendingCode = ref(false);
const loggingIn = ref(false);
const practiceStatus = ref<PracticeStatus>('idle');
@@ -3364,10 +3376,15 @@ const loginBySms = async () => {
body: JSON.stringify({ phonenumber: phone.value, smsCode: smsCode.value, tenantId: '000000' })
})
);
authToken.value = data.access_token;
clientId.value = data.client_id;
localStorage.setItem(tokenKey, data.access_token);
localStorage.setItem(clientIdKey, data.client_id);
const nextToken = data.access_token?.trim() || '';
const nextClientId = data.client_id?.trim() || '';
if (!nextToken || !nextClientId) {
throw new Error('登录响应缺少认证信息,请重试');
}
authToken.value = nextToken;
clientId.value = nextClientId;
localStorage.setItem(tokenKey, nextToken);
localStorage.setItem(clientIdKey, nextClientId);
localStorage.setItem(phoneKey, phone.value);
smsCode.value = '';
void fetchMobileHome();