fix(client): 修复登录会话自动续期

This commit is contained in:
2026-09-03 18:11:07 +08:00
parent 39e1c8e6ee
commit b3649c314a
9 changed files with 128 additions and 28 deletions
+9 -2
View File
@@ -44,10 +44,14 @@ test('matches the frozen username-or-mobile login contract', () => {
assert.match(loginSource, /忘记密码/)
})
test('persists both rotating tokens according to the remember choice', () => {
test('persists rotating tokens independently and only remembers the login id by choice', () => {
assert.match(sessionSource, /ACCESS_TOKEN_KEY/)
assert.match(sessionSource, /REFRESH_TOKEN_KEY/)
assert.match(sessionSource, /remember \? localStorage : sessionStorage/)
assert.match(sessionSource, /localStorage\.setItem\(ACCESS_TOKEN_KEY, tokens\.accessToken\)/)
assert.match(sessionSource, /localStorage\.setItem\(REFRESH_TOKEN_KEY, tokens\.refreshToken\)/)
assert.match(sessionSource, /persistRememberedLoginId/)
assert.match(loginSource, /getRememberedLoginId/)
assert.match(loginSource, /记住账号/)
assert.match(sessionSource, /clearSessionTokens/)
})
@@ -93,6 +97,7 @@ test('uses real auth endpoints and single-flight refresh handling', () => {
assert.match(httpSource, /refreshPromise/)
assert.match(httpSource, /\/auth\/refresh/)
assert.match(httpSource, /persistTokenPair\(tokens\)/)
assert.match(httpSource, /token && !isPublicAuthRequest\(config\.url\)/)
assert.match(httpSource, /request\._retriedAfterRefresh = true/)
assert.match(httpSource, /return await transport\.request\(request\)/)
assert.match(httpSource, /envelope\.code !== '200'/)
@@ -102,6 +107,8 @@ test('uses real auth endpoints and single-flight refresh handling', () => {
assert.match(httpSource, /error instanceof CrossTabRefreshTimeoutError[\s\S]*window\.dispatchEvent\(new CustomEvent\('cc-client-session-expired'\)\)[\s\S]*redirectToLoginAfterSessionExpiry\(\)/)
assert.match(httpSource, /window\.location\.replace\(`\/login\?redirect=\$\{encodeURIComponent\(redirect\)\}`\)/)
assert.match(authStoreSource, /decodeMerchantToken/)
assert.match(authStoreSource, /MerchantTokenExpiredError && getRefreshToken\(\)/)
assert.match(authStoreSource, /applyToken\(await refreshAuthenticatedSession\(\)\)/)
assert.doesNotMatch(authStoreSource, /mock-client:/)
})
+5 -3
View File
@@ -5,6 +5,7 @@ import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { ArrowRight, LockKeyhole, UserRound } from '@lucide/vue'
import { useAuthStore } from '@/modules/auth/store/auth'
import AuthShell from '@/modules/auth/components/AuthShell.vue'
import { getRememberedLoginId } from '@/api/session'
interface LoginForm {
account: string
@@ -19,12 +20,13 @@ const formRef = ref<FormInstance>()
const loading = ref(false)
const impersonationFailed = route.query.impersonation === 'failed'
const loginIdentifiersUpdated = route.query.reason === 'identifiers-updated'
const rememberedLoginId = getRememberedLoginId()
const form = reactive<LoginForm>({
account: typeof route.query.account === 'string'
? route.query.account
: '',
: rememberedLoginId,
password: '',
remember: false,
remember: Boolean(rememberedLoginId),
})
const usernamePattern = /^[A-Za-z][A-Za-z0-9_.@-]{3,63}$/
const mainlandMobilePattern = /^1[3-9]\d{9}$/
@@ -113,7 +115,7 @@ function showResetHint() {
<el-input v-model="form.password" autocomplete="current-password" maxlength="72" type="password" show-password placeholder="请输入 8–72 位密码" :prefix-icon="LockKeyhole" @keyup.enter="submit" />
</el-form-item>
<div class="auth-form-options">
<el-checkbox v-model="form.remember">记住我</el-checkbox>
<el-checkbox v-model="form.remember">记住账号</el-checkbox>
<button type="button" @click="showResetHint">忘记密码?</button>
</div>
<el-button native-type="submit" class="auth-submit" type="primary" :loading="loading">登录 <ArrowRight :size="17" /></el-button>