PersonalWebApplication/Client/src/hooks/useAuth.ts

78 lines
2.6 KiB
TypeScript
Raw Normal View History

import { useCallback, useState } from 'react'
import { login as loginRequest } from '../api/auth'
// storage 键:登录态写在浏览器。
// - 勾“记住我” → localStorage关浏览器再开仍登录
// - 不勾 → sessionStorage刷新仍在关标签页/浏览器即清)
const STORAGE_KEY = 'ip.auth'
interface Session {
username: string
token: string
}
function parseSession(raw: string | null): Session | null {
if (!raw) return null
try {
const parsed = JSON.parse(raw) as Partial<Session>
if (typeof parsed.username === 'string' && typeof parsed.token === 'string') {
return { username: parsed.username, token: parsed.token }
}
} catch {
// 解析失败(被手动改坏等)当作未登录
}
return null
}
// 先读 localStorage记住我再读 sessionStorage仅本次会话
function loadSession(): Session | null {
return (
parseSession(localStorage.getItem(STORAGE_KEY)) ??
parseSession(sessionStorage.getItem(STORAGE_KEY))
)
}
export interface Auth {
/** 已登录则为当前用户名,否则为 null */
username: string | null
/** 校验凭据remember 为 true 时持久化到 localStorage否则只存 sessionStorage */
signIn: (username: string, password: string, remember: boolean) => Promise<void>
/** 退出登录并清除浏览器里的会话(两种 storage 都清) */
signOut: () => void
}
/**
* localStorage / sessionStorage
*
*/
export function useAuth(): Auth {
const [session, setSession] = useState<Session | null>(loadSession)
const signIn = useCallback(
async (username: string, password: string, remember: boolean) => {
const result = await loginRequest(username, password)
const next: Session = { username: result.username, token: result.token }
setSession(next)
const raw = JSON.stringify(next)
if (remember) {
// 记住我:持久化,关浏览器再开仍登录。
localStorage.setItem(STORAGE_KEY, raw)
sessionStorage.removeItem(STORAGE_KEY)
} else {
// 不记住:仅本次会话,刷新仍在、关标签页/浏览器即清。
sessionStorage.setItem(STORAGE_KEY, raw)
localStorage.removeItem(STORAGE_KEY)
}
},
[],
)
const signOut = useCallback(() => {
setSession(null)
localStorage.removeItem(STORAGE_KEY)
sessionStorage.removeItem(STORAGE_KEY)
}, [])
return { username: session?.username ?? null, signIn, signOut }
}