"use server" import { Base64 } from "js-base64" import { headers } from "next/headers" import redis from "./redis" // 生成加密密钥 export async function generateKey(): Promise { return crypto.subtle.generateKey( { name: "AES-CBC", length: 256, }, true, ["encrypt", "decrypt"] ) } export async function generateKeyFromEnv( envPassword: string ): Promise { const encoder = new TextEncoder() const password = encoder.encode(envPassword) const keyMaterial = await crypto.subtle.importKey( "raw", password, { name: "PBKDF2", }, false, ["deriveKey"] ) return crypto.subtle.deriveKey( { name: "PBKDF2", salt: new Uint8Array(16), // 随机盐值 iterations: 100000, // 迭代次数 hash: "SHA-256", }, keyMaterial, { name: "AES-CBC", length: 256, }, true, ["encrypt", "decrypt"] ) } // 将ArrayBuffer转换为Uint8Array function arrayBufferToUint8Array(buffer: ArrayBuffer) { return new Uint8Array(buffer) } // 加密函数 export async function encrypt(data: any, key: CryptoKey): Promise { const encodedData = new TextEncoder().encode(JSON.stringify(data)) const iv = crypto.getRandomValues(new Uint8Array(16)) // 16 bytes for AES block size const encrypted = await crypto.subtle.encrypt( { name: "AES-CBC", iv: iv, }, key, encodedData ) // 将Uint8Array转换为Base64字符串,以便存储和传输 const encryptedBase64 = Base64.fromUint8Array( arrayBufferToUint8Array(encrypted) ) const ivBase64 = Base64.fromUint8Array(new Uint8Array(iv)) // 使用js-base64进行编码 // 将IV和加密数据拼接在一起,以便解密时使用 return `${ivBase64}::${encryptedBase64}` } // 解密函数 export async function decrypt( encryptedData: string, key: CryptoKey ): Promise { // 将加密数据从Base64字符串转换回Uint8Array const [ivBase64, encryptedBase64] = encryptedData.split("::") const ivArray = Buffer.from(ivBase64, "base64") const encrypted = Buffer.from(encryptedBase64, "base64") // 使用AES-CBC算法进行解密 const decrypted = await crypto.subtle.decrypt( { name: "AES-CBC", iv: ivArray, }, key, encrypted ) // 将ArrayBuffer转换为字符串 return new TextDecoder().decode(decrypted) } // 校验用户信息 export async function genAccessToken(req: any) { try { // 校验用户信息 // const fingerprint = req.headers.get("Fingerprint") const sessionData = req.cookies.get("session")?.value // 无session 直接重定向到登录页面 if (!sessionData) { return "客户端请求中,未包含用户标识" } let redisKey = JSON.parse(sessionData) const key = await generateKeyFromEnv(process.env.ENCRYPTION_KEY || "") const decryptedSessionData = JSON.parse(await decrypt(redisKey, key)) const fingerprint = JSON.parse(decryptedSessionData)?.fingerprint // 获取用户信息 let userData: any = await redis.get(redisKey) userData = JSON.parse(userData) // redis中无用户信息 直接重定向到登录页面 if (!userData) { return "未获取到用户信息" } const header = headers() let clientIP = (await header).get("x-forwarded-for")?.split(",")[0] || req?.socket?.remoteAddress // ip v6地址 if (clientIP.startsWith("::")) { clientIP = clientIP.slice(7) } // 登录ip与用户请求ip不匹配 if ( clientIP !== userData?.clientIP || fingerprint !== userData?.fingerprint ) { return `客户端信息与缓存用户信息不一致 \n 客户端: ${clientIP}, ${fingerprint} | redis存储: ${userData?.clientIP}, ${userData?.fingerprint}` } return { ...userData, redisKey: sessionData, } } catch (err: any) { return `获取用户信息报错, 报错原因:${err?.message}` } }