Files
user-system/frontend/admin/src/lib/http/csrf.ts

146 lines
3.3 KiB
TypeScript
Raw Normal View History

/**
* CSRF Token
*
* CSRF
* 1. GET CSRF Token cookie API
* 2. POST/PUT/DELETE Token X-CSRF-Token
*
* 使 Bearer Token
* CSRF
*/
// 注意:避免从 './client' 导入,防止循环依赖
// 使用原生 fetch 获取 CSRF Token
import { config } from '@/lib/config'
// CSRF Token 存储
let csrfToken: string | null = null
/**
* CSRF Token
*/
export function getCSRFToken(): string | null {
return csrfToken
}
/**
* CSRF Token
*/
export function setCSRFToken(token: string): void {
csrfToken = token
}
/**
* cookie CSRF Token
* Django/Laravel cookie csrftoken
*/
export function getCSRFTokenFromCookie(): string | null {
if (typeof document === 'undefined') {
return null
}
const match = document.cookie.match(/csrftoken=([^;]+)/)
return match ? match[1] : null
}
/**
* API URL
* client.ts
*/
function resolveApiBaseUrl(): URL {
const origin = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'
const rawBaseUrl = /^https?:\/\//i.test(config.apiBaseUrl)
? config.apiBaseUrl
: config.apiBaseUrl.startsWith('/')
? config.apiBaseUrl
: `/${config.apiBaseUrl}`
const baseUrl = new URL(rawBaseUrl, origin)
if (!baseUrl.pathname.endsWith('/')) {
baseUrl.pathname = `${baseUrl.pathname}/`
}
return baseUrl
}
/**
* URL
*/
function buildUrl(path: string): string {
const normalizedPath = path.replace(/^\/+/, '')
const url = new URL(normalizedPath, resolveApiBaseUrl())
return url.toString()
}
/**
* CSRF Token
* cookie API Token
*/
export async function initCSRFToken(): Promise<string | null> {
// 优先从 cookie 获取
let token = getCSRFTokenFromCookie()
if (!token) {
try {
// 使用原生 fetch 避免循环依赖
const response = await fetch(buildUrl('/auth/csrf-token'), {
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
})
if (response.ok) {
const result = await response.json()
// 后端返回字段名为 csrf_token
if (result.code === 0 && result.data?.csrf_token) {
token = result.data.csrf_token
}
}
} catch {
// API 不支持,使用 cookie 中的 token如果有
token = getCSRFTokenFromCookie()
}
}
if (token) {
setCSRFToken(token)
}
return token
}
/**
* CSRF Token
*/
export function clearCSRFToken(): void {
csrfToken = null
}
/**
* CSRF Token
*/
export const CSRF_HEADER_NAME = 'X-CSRF-Token'
/**
* CSRF Token
* POST/PUT/DELETE
*/
export function getCSRFHeaders(): Record<string, string> {
const token = csrfToken || getCSRFTokenFromCookie()
if (!token) {
return {}
}
return {
[CSRF_HEADER_NAME]: token
}
}
/**
* CSRF
*/
export const CSRF_PROTECTED_METHODS = ['POST', 'PUT', 'DELETE', 'PATCH']