-
Notifications
You must be signed in to change notification settings - Fork 1
Token
Ray Hao edited this page Jun 6, 2024
·
4 revisions
- 支持单一令牌和多令牌缓存
- 支持 JWT(JSON Web Token) 格式配置
interface TokenOptions {
/**
* Window 对象
* @default window
*/
window?: Window
/**
* 是否持久化数据到 `storage` 中
* @default true
*/
persistent?: boolean
/**
* 存储中心,支持实现 `Storage` 接口的对象
* @default window.localStorage
*/
storage?: StorageLike
/**
* 持久化到存储中心的键
* @default '__VUE_APP_SDK__TOKEN__'
*/
storageKey?: string
/**
* 访问令牌格式
* - `normal`: 普通格式,`resolve()` 存储即所得
* - `jwt`: JWT(JSON Web Token) 格式,`resolve()` 返回 `toJWT()` 格式
*
* @default 'normal'
*/
format?: MaybeRefOrGetter<'normal' | 'jwt'>
/**
* 默认的 JWT(JSON Web Token) 前缀
* @default 'Bearer'
*/
jwtPrefix?: string
}
interface Token {
/**
* 插件 ID
*/
id: PluginID<Token>
/**
* 配置项
*/
options: TokenOptions
/**
* 指定令牌是否存在
* @param type 令牌类型
* @param key 令牌键
* @example
* ```ts
* // 应用仅存在单令牌
* token.has('accessToken')
*
* // 应用存在多令牌
* token.has('accessToken', 'second')
* ```
*/
has: (type: 'accessToken' | 'refreshToken', key?: string) => boolean
/**
* 获取指定令牌
* @param type 令牌类型
* @param key 令牌键
* @example
* ```ts
* // 应用仅存在单令牌
* token.get('accessToken')
*
* // 应用存在多令牌
* token.get('accessToken', 'second')
* ```
*/
get: (type: 'accessToken' | 'refreshToken', key?: string) => string | undefined
/**
* 设置令牌状态
* @param type 令牌类型
* @param value 令牌
* @param key 令牌键
* @param profile 令牌配置
*
* @example
* ```ts
* // 应用仅存在单令牌
* token.set('accessToken', 'token value', { format: 'jwt' })
* token.set({ accessToken: 'token value' }, { format: 'jwt' })
*
* // 应用存在多令牌
* token.set('accessToken', 'token value', 'second', { format: 'jwt', jwtPrefix: 'Token' })
* token.set({ accessToken: 'token value' }, 'second', { format: 'jwt', jwtPrefix: 'Token' })
* ```
*/
set: {
(type: 'accessToken' | 'refreshToken', value?: string, key?: string, profile?: TokenProfile): void
(type: 'accessToken' | 'refreshToken', value?: string, profile?: TokenProfile): void
(state: Partial<TokenState<false>>, key?: string, profile?: TokenProfile): void
(state: Partial<TokenState<false>>, profile?: TokenProfile): void
}
/**
* 清理令牌状态
* @param type 令牌类型,设为空时清理全部令牌
* @param key 令牌键,设为空时清理指定类型全部令牌
* @example
* ```ts
* // 应用仅存在单令牌
* token.clear('accessToken')
*
* // 应用存在多令牌
* token.clear('accessToken', 'second')
*
* // 删除指定类型所有令牌
* token.clear('accessToken', '')
*
* // 删除全部令牌
* token.clear()
* ```
*/
clear: (type?: 'accessToken' | 'refreshToken', key?: string) => void
/**
* 转为 JWT(JSON Web Token) 格式令牌
* @param prefix JWT(JSON Web Token) 前缀
* @param key 令牌键
* @returns JWT(JSON Web Token) 格式令牌
* @example
* ```ts
* // 使用默认 `jwtPrefix`
* token.toJWT() // 'Bearer abcdefg'
*
* // 使用自定义 `jwtPrefix`
* token.toJWT('Token') // 'Token abcdefg''
* ```
*/
toJWT: (prefix?: string, key?: string) => string
/**
* 根据设定令牌格式获取访问令牌
* @param key 令牌键
* @example
* ```ts
* // 普通格式
* const token = createToken()
* token.set('accessToken', 'abcdefg')
* token.resolve() // 'abcdefg'
*
* // JWT 格式
* const token = createToken({ format: 'jwt' })
* token.set('accessToken', 'abcdefg')
* token.resolve() // 'Bearer abcdefg'
*
* // 单独设置
* const token = createToken()
* token.set('accessToken', 'abcdefg', { format: 'jwt', jwtPrefix: 'Token' })
* token.resolve() // 'Token abcdefg'
* ```
*/
resolve: (key?: string) => string | undefined
/**
* 获取令牌配置
* @param key 令牌键
*/
getTokenProfile: (key?: string) => TokenProfile
/**
* 插件安装
*/
install: (sdk: AppSDKInternalInstance) => void
}
// sdk.ts
import { createAppSDK, createToken } from 'vue-app-sdk'
export const sdk = createAppSDK()
sdk.use(
createToken({
// `accessToken` 格式
// - `normal`: 普通格式,`resolve()` 存储即所得
// - `jwt`: JWT(JSON Web Token) 格式,`resolve()` 返回 `toJWT()` 格式
format: 'jwt',
// JWT(JSON Web Token) 前缀
jwtPrefix: 'Bearer'
}),
)
在调用
sdk.cleanup()
时自动清理令牌缓存。
import { TOKEN_ID, useAppSDK } from 'vue-app-sdk'
import Axios from 'axios'
const token = useAppSDK().getPlugin(TOKEN_ID)!
const axios = Axios.create()
axios.interceptors.request.use((config) => {
// 如果有访问令牌则携带
if (token.has('accessToken')) {
// 设置最终的访问令牌
config.headers.Authorization = token.resolve()
}
return config
})
login()
async function login() {
// ...
const response = await axios<{ accessToken: string, refreshToken?: string }>('/login')
if (response.status === 200) {
// 设置令牌信息,刷新令牌非必须
token.set({
accessToken: response.data.accessToken,
refreshToken: response.data.refreshToken
})
}
else {
// 清除所有令牌信息,可传入指定令牌类型
token.clear()
}
}
- 定义令牌键,调用方法时可获得类型提示
// types/vue-app-sdk.d.ts
declare module 'vue-app-sdk' {
interface TokenRecord {
// 三方开放接口访问令牌,值类型随意设置
open: void
}
}
export {}
- 调用方法时传入指定令牌键
// ...
fetchUserInfo().then((data) => {
// 设置三方开放接口访问令牌和令牌配置
token.set('accessToken', data.openToken, 'open', { format: 'normal' })
// 控制台输出三方开放接口访问令牌
console.log(token.get('accessToken', 'open'))
})
function fetchUserInfo() {
return Promise.resolve({ id: 1, name: 'admin', openToken: 'abcd' })
}
本示例以
axios
为例,其他请求工具可自行尝试。
- 定义
axios
全局配置项,获取类型提示
// types/axios.d.ts
import type { TokenKey } from 'vue-app-sdk'
declare module 'axios' {
interface AxiosRequestConfig {
/**
* 令牌键,多令牌时可设置指定访问令牌键
*/
tokenKey?: TokenKey
}
}
export {}
- 拦截器携带指定令牌键的访问令牌
// ...
axios.interceptors.request.use((config) => {
// 如果有指定访问令牌则携带
if (token.has('accessToken', config.tokenKey)) {
// 设置最终的访问令牌
config.headers.Authorization = token.resolve(config.tokenKey)
}
return config
})
// 请求三方开放接口数据
function fetchData() {
// 设置令牌键,在接口请求时携带指定令牌键的访问令牌
return axios.get('/open/list', { tokenKey: 'open' })
}