Commit e22d4e27 authored by 郑磊's avatar 郑磊

API请求支持

parent e4d631e9
node_modules/
{ {
"name": "@3ya/web-lib", "name": "@3ya/web-lib",
"version": "1.0.0", "version": "1.0.0",
"main": "dist/index.js", "main": "index.ts",
"module": "dist/index-esm.js", "private": true,
"private": true "devDependencies": {
"@types/crypto-js": "^4.2.2",
"@types/lodash-es": "^4.17.12",
"axios": "^1.6.5",
"typescript": "^5.3.3"
},
"peerDependencies": {
"axios": "^1.0.0"
},
"dependencies": {
"crypto-js": "^4.2.0",
"lodash-es": "^4.17.21"
}
} }
import {
AxiosRequestHeaders,
InternalAxiosRequestConfig,
type AxiosInstance,
} from 'axios'
import { sample } from 'lodash-es'
import {
aesDecrypt,
aesEncrypt,
base64Decode,
base64Encode,
md5,
} from './security'
/**
* 接口调用配置项
*/
export interface ApiOptions {
/**
* 接口请求的基地址
*/
baseURL?: string
/**
* 包名
*/
source?: string
/**
* 设备类型
*/
device?: string
/**
* 版本号
*/
appversion?: string
/**
* 附带在请求头的token或者token获取函数
*/
token?: string | (() => string | undefined | null)
/**
* 请求体是否加密
*/
encryption?: boolean
}
/**
* 请求加解密密钥
*/
export interface SignKey {
name: string
key: string
iv: string
}
/**
* 默认的请求加解密密钥列表
*/
export const DEFAULT_SIGN_KEYS: SignKey[] = [
{
key: 'k0f3JfxEWd3HC7pXQU8tmSkDheUXibmz',
iv: 'Fu2wU73MBcsEZWJk',
name: 'secret_key_1',
},
{
key: 'NdEHDuAZGCQV6C0oaURvBJJWA4z2QHyG',
iv: 'w0j9K4bswcGJKtj5',
name: 'secret_key_2',
},
{
key: 'lBsn3FrYN9hdoFqMdqqNV1dlpGutkcXk',
iv: 'NRHqJYTNKxH8Z9fU',
name: 'secret_key_3',
},
{
key: 'eVdEc44rke6P6rfn9SgEWGPNqkcipWN7',
iv: 'l5HNU6Q0bdc2piB3',
name: 'secret_key_4',
},
{
key: '53BSHwE29I1u4e5cJDMZGHUdi0A7L4E5',
iv: 'zUsjrk58p2RahP08',
name: 'secret_key_5',
},
{
key: 'vgizOWReMzVJA6LEsb9N36LEzPqcFdeO',
iv: 'e52YLbnvVv4HpGu7',
name: 'secret_key_6',
},
]
export const DEFAULT_API_OPTIONS: ApiOptions = {
source: 'xinxiuweb',
device: 'wap',
appversion: '1.0.0',
}
function checkHeader(
headers: AxiosRequestHeaders,
key: string,
...defaultValues: any[]
): void {
if (headers.has(key)) return
for (const value of defaultValues) {
if (typeof value === 'undefined' || value === null) continue
if (typeof value === 'function') {
const headerValue = value()
if (typeof headerValue === 'undefined' || headerValue === null)
continue
headers.set(key, headerValue)
}
headers.set(key, String(value))
}
}
/**
* 为axios实例注入追加公参的拦截器
* @param axios axios实例
* @param options 默认配置
*/
export function useCommonParamsInterceptor(
axios: AxiosInstance,
options: ApiOptions = {}
) {
options = {
...DEFAULT_API_OPTIONS,
...options,
}
axios.interceptors.request.use(
(request: InternalAxiosRequestConfig<any> & ApiOptions) => {
//包名
checkHeader(
request.headers,
'source',
request.source,
options.source
)
//设备类型
checkHeader(
request.headers,
'device',
request.device,
options.device
)
//版本号
checkHeader(
request.headers,
'appversion',
request.appversion,
options.appversion
)
//时间戳
checkHeader(
request.headers,
'timestamp',
Math.floor(Date.now() / 1000)
)
//请求标识
checkHeader(
request.headers,
'request_number',
md5(String(Date.now() + Math.random()))
)
return request
}
)
}
function getParamValue(value: any): string {
if (value === null || typeof value === 'undefined') {
return ''
}
if (typeof value === 'object') {
return JSON.stringify(value)
}
return String(value)
}
/**
* 为axios实例注入请求签名拦截器
* @param axios axios实例
* @param key 签名密钥,如果不传则使用默认密钥
*/
export function useSignatureInterceptor(axios: AxiosInstance, key?: string) {
axios.interceptors.request.use((request) => {
if (request.headers.has('signature')) return request
const body =
typeof request.data === 'object' &&
request.data !== null &&
!Array.isArray(request.data)
? request.data
: {}
//参与签名的数据
const params: Record<string, any> = {
...body,
source: request.headers.get('source'),
device: request.headers.get('device'),
timestamp: request.headers.get('timestamp'),
request_number: request.headers.get('request_number'),
appversion: request.headers.get('appversion'),
}
const entries = Object.entries(params).sort((k1, k2) =>
k1[0].localeCompare(k2[0])
)
let basestr = entries.map((t) => getParamValue(t[1])).join('')
if (key) {
basestr += key
} else if (request.headers.get('device') === 'wap') {
basestr += 'asdasgfdwqew'
} else {
basestr += 'WQdflHhKjdafsj21321jkl531l45'
}
basestr = md5(md5(basestr))
const randomStr = md5(String(Date.now() + Math.random()))
const signature =
randomStr.substring(0, 5) + basestr + randomStr.substring(6, 12)
request.headers.set('signature', signature)
return request
})
}
/**
* 为axios实例注入请求加解密拦截器
* @param axios axios实例
* @param signKeys 密钥列表,如果不传则使用默认密钥
*/
export function useEncryptInterceptor(
axios: AxiosInstance,
signKeys: SignKey[] = DEFAULT_SIGN_KEYS
) {
axios.interceptors.request.use(
(request: InternalAxiosRequestConfig<any> & ApiOptions) => {
if (request.encryption === false) return request
let data = request.data
const strData =
typeof data === 'string'
? data
: typeof data === 'undefined' || data === null
? '{}'
: JSON.stringify(data)
let sign_name = request.headers.get('sign_name') as string
let signKey: SignKey
if (sign_name) {
signKey = signKeys.find((item) => item.name === sign_name)!
if (!signKey) {
signKey = sample(signKeys)!
}
} else {
signKey = sample(signKeys)!
}
const { key, name, iv } = signKey
request.headers.set('sign_name', name)
const encrypted = aesEncrypt(strData, key, iv)
const base64 = base64Encode(encrypted)
const sign_data = encodeURIComponent(base64)
request.data = { sign_data }
return request
}
)
axios.interceptors.response.use((resp) => {
const sign_name = resp.config.headers.get('sign_name')
if (!sign_name || typeof resp.data !== 'object' || resp.data === null) {
return resp
}
const signKey = signKeys.find((t) => t.name === sign_name)
if (!signKey) return resp
if (typeof resp.data.sign_data !== 'string' || !resp.data.sign_data) {
return resp
}
let signData = resp.data.sign_data
signData = decodeURIComponent(signData)
signData = base64Decode(signData)
signData = aesDecrypt(signData, signKey.key, signKey.iv)
resp.data = JSON.parse(signData)
return resp
})
}
export {
type ApiOptions,
type SignKey,
DEFAULT_SIGN_KEYS,
DEFAULT_API_OPTIONS,
useCommonParamsInterceptor,
useSignatureInterceptor,
useEncryptInterceptor,
} from './api'
import CryptoJS from 'crypto-js'
export function md5(input: string): string {
return CryptoJS.MD5(input).toString(CryptoJS.enc.Hex).toLowerCase()
}
/**
* base64编码
* @param input 待编码的字符串
*/
export function base64Encode(input: string): string {
return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(input))
}
/**
* base64解码
* @param input 待解码的字符串
*/
export function base64Decode(input: string): string {
return CryptoJS.enc.Utf8.stringify(CryptoJS.enc.Base64.parse(input))
}
/**
* AES-CBC-128加密
* @param input 待加密的数据
* @param key 密钥
* @param iv 向量
*/
export function aesEncrypt(input: string, key: string, iv: string): string {
return CryptoJS.AES.encrypt(input, CryptoJS.enc.Utf8.parse(key), {
iv: CryptoJS.enc.Utf8.parse(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
}).toString()
}
/**
* AES-CBC-128解密
* @param input 待加密的数据
* @param key 密钥
* @param iv 向量
*/
export function aesDecrypt(input: string, key: string, iv: string): string {
return CryptoJS.AES.decrypt(input, CryptoJS.enc.Utf8.parse(key), {
iv: CryptoJS.enc.Utf8.parse(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
}).toString(CryptoJS.enc.Utf8)
}
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/crypto-js@^4.2.2":
version "4.2.2"
resolved "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.2.2.tgz#771c4a768d94eb5922cc202a3009558204df0cea"
integrity sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==
"@types/lodash-es@^4.17.12":
version "4.17.12"
resolved "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b"
integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==
dependencies:
"@types/lodash" "*"
"@types/lodash@*":
version "4.14.202"
resolved "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8"
integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
axios@^1.6.5:
version "1.6.5"
resolved "https://registry.npmmirror.com/axios/-/axios-1.6.5.tgz#2c090da14aeeab3770ad30c3a1461bc970fb0cd8"
integrity sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==
dependencies:
follow-redirects "^1.15.4"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
crypto-js@^4.2.0:
version "4.2.0"
resolved "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631"
integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
follow-redirects@^1.15.4:
version "1.15.5"
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020"
integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
lodash-es@^4.17.21:
version "4.17.21"
resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
typescript@^5.3.3:
version "5.3.3"
resolved "https://registry.npmmirror.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment