实现数据加密传输,成功调试登录接口
This commit is contained in:
+5
-4
@@ -18,6 +18,7 @@ export class LoginApi {
|
||||
clientId: Config.VITE_APP_CLIENT_ID,
|
||||
grantType: 'password'
|
||||
}
|
||||
// 请求参数处理
|
||||
const Requestconfig = new RequestConfig()
|
||||
Requestconfig.url = "/auth/login"
|
||||
Requestconfig.headers!.isToken = false
|
||||
@@ -25,19 +26,19 @@ export class LoginApi {
|
||||
Requestconfig.headers!.repeatSubmit = false
|
||||
Requestconfig.method = HttpMethod.POST
|
||||
Requestconfig.data = configData
|
||||
|
||||
//发起请求
|
||||
const res = await this.http.request<UTSJSONObject>(Requestconfig)
|
||||
const data = res.data
|
||||
console.log(data);
|
||||
if (data == null) {
|
||||
throw new Error("登陆失败")
|
||||
}
|
||||
// 手动转换 UTSJSONObject 到 VerifyCodeResult
|
||||
const result: LoginResult = {
|
||||
// 格式化返回数据 手动转换 UTSJSONObject 到 VerifyCodeResult
|
||||
const result : LoginResult = {
|
||||
access_token: data["access_token"] as string
|
||||
}
|
||||
console.log(result.access_token);
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -9,10 +9,10 @@ export class Config {
|
||||
|
||||
|
||||
// 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
||||
static readonly VITE_APP_ENCRYPT = false
|
||||
// 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
|
||||
static readonly VITE_APP_ENCRYPT = true
|
||||
// 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换 公钥
|
||||
static readonly VITE_APP_RSA_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvEDuRIOM3oZPWj9Ukoc5pQklR4PFH6/clnjeFqjDLIgDyQvjxhgqAZQA+E9eD6qu6FsXPmK8djcL+nh3cFHz4pX473jDvO3Sve+8yL3VRQ0n2pRgQ2a01MJsy+WwTZCBYWf0VnLRIvANUoWQgy9vz94q7Va44dg7A1/3ICf+xAwIDAQAB'
|
||||
// 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
|
||||
// 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换 私钥
|
||||
static readonly VITE_APP_RSA_PRIVATE_KEY = 'MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAObq7yrxfvyieZtTjAYyrdvi59tYTXxjO5ajmPCRSXBY9M9wQ1tli297JN6mnY53UJMNyOFNSZVi8WSFoIXjpR87FmvChJlzeN/dZdd3SEs48Ee66XKeSePYqxa8oO5GKDsnajgpsOHKXSeeVSIysiIPS2/WsEqk0In9P4w3RsRFAgMBAAECgYBiMEWwce24SPICnRzuScBpvmsudrbEDIH7BOd0a6LZlcnLJwZNJ7mJlshPsHNQb+WgEf135+BBGEhioPtn0yuTdEuKP4kB9UdYUKiayWCoWhJpesv7sAD4RDClV7dhuV+gcd1AXD+YzyRIPbGm0VC2U+4q8/+UPRpVjqskbLVTgQJBAPRpou7g3S8n4XB527kq0D8I3+ZYwMxZhszwhrCDpJU319+ucmpLVwYIzDmZVeID2QQdUaDfIEViFHu95xDrGiUCQQDx3YOKn3yaEctk/ERVn7hDAyAXUbd8/pv2b24/M/l1ZevlsFem8U4Jk5Mu64t3z3YGJoymEjQmbucwT01iKhehAkEAxlnccsRmfFh/KkqauKE4M4++NTAd9zlInpUsmZ+cN8UEGnF2RTEzRKBrLOt1uWCqBB7PGiE6DVTVjr7FAQPrSQJAT5yeY87DcONSk9cFlzmPqV8p/QME5rvYEnHzVBKDlkUKNPyqnWToTvaoh9U4fyNmsfeWbEOprszqhFhWHG3GgQJAK8+ynmyFhaw63+Hx2KU5zR4hVuQso2IzrEurGxCxybV6mR7VBerb4502+EPx3PmOgxQL+niUFhcMWxcvBFP9+A=='
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/test/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "加解密测试"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('登录失败:', e)
|
||||
console.error('登录失败:'+ e.message)
|
||||
loadCode()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<image class="logo" src="/static/logo.png"></image>
|
||||
<text class="title">{{title}}</text>
|
||||
<button @click="goLogin()">去登陆</button>
|
||||
<button @click="goLogin1()">加解密测试</button>
|
||||
需要一个导航栏
|
||||
</view>
|
||||
</template>
|
||||
@@ -18,6 +19,14 @@
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function goLogin1() {
|
||||
|
||||
uni.navigateTo({
|
||||
url: '/pages/test/index'
|
||||
})
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -0,0 +1,522 @@
|
||||
<template>
|
||||
<!-- #ifdef APP -->
|
||||
<scroll-view class="crypto-scroll">
|
||||
<!-- #endif -->
|
||||
<view class="crypto-page">
|
||||
<view class="hero-panel">
|
||||
<text class="hero-kicker">LAOQIANJUNZI-CRYPTO</text>
|
||||
<text class="hero-title">统一加密能力演示</text>
|
||||
<text class="hero-desc">这一页直接调用插件导出的新接口,集中演示编码、摘要、HMAC、AES、3DES、RC4、RSA 与字节数组加解密。</text>
|
||||
</view>
|
||||
|
||||
<view class="card-block runtime-card">
|
||||
<text class="card-title">运行时概览</text>
|
||||
<view class="status-row">
|
||||
<text class="status-label">平台</text>
|
||||
<text class="status-value">{{ runtimeInfo.platformName }}</text>
|
||||
</view>
|
||||
<view class="status-row">
|
||||
<text class="status-label">引擎</text>
|
||||
<text class="status-value">{{ runtimeInfo.engineName }}</text>
|
||||
</view>
|
||||
<view class="status-row">
|
||||
<text class="status-label">高级模式</text>
|
||||
<text class="status-value">{{ runtimeInfo.supportsAdvancedModes ? '已启用' : '受限' }}</text>
|
||||
</view>
|
||||
<view class="status-row">
|
||||
<text class="status-label">原生桥接</text>
|
||||
<text class="status-value">{{ runtimeInfo.usesNativeBridge ? '是' : '否' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-block form-card">
|
||||
<text class="card-title">输入参数</text>
|
||||
<view class="field-group">
|
||||
<text class="field-label">明文</text>
|
||||
<input class="field-input" v-model="plainText" />
|
||||
</view>
|
||||
<view class="field-group">
|
||||
<text class="field-label">主密钥</text>
|
||||
<input class="field-input" v-model="secretText" />
|
||||
</view>
|
||||
<view class="field-group">
|
||||
<text class="field-label">向量</text>
|
||||
<input class="field-input" v-model="ivText" />
|
||||
</view>
|
||||
<view class="field-group">
|
||||
<text class="field-label">HMAC 密钥</text>
|
||||
<input class="field-input" v-model="macSecretText" />
|
||||
</view>
|
||||
<view class="field-group">
|
||||
<text class="field-label">3DES 密钥</text>
|
||||
<input class="field-input" v-model="legacySecretText" />
|
||||
</view>
|
||||
<view class="field-group">
|
||||
<text class="field-label">RSA 位数</text>
|
||||
<input class="field-input" type="number" v-model="rsaBitsText" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="action-grid">
|
||||
<button class="action-button action-strong" @click="runAllDemos">运行全部示例</button>
|
||||
<button class="action-button" @click="runCodecDemo">编码</button>
|
||||
<button class="action-button" @click="runDigestDemo">摘要/HMAC</button>
|
||||
<button class="action-button" @click="runAesDemo">AES 文本</button>
|
||||
<button class="action-button" @click="runByteDemo">AES 字节</button>
|
||||
<button class="action-button" @click="runLegacyDemo">3DES/RC4</button>
|
||||
<button class="action-button" @click="runRsaDemo">RSA</button>
|
||||
<button class="action-button" @click="runCompatDemo">兼容 API</button>
|
||||
<button class="action-button action-warn" @click="clearResults">清空结果</button>
|
||||
</view>
|
||||
|
||||
<view class="result-grid">
|
||||
<view class="card-block result-card">
|
||||
<text class="card-title">编码</text>
|
||||
<text class="result-line">Base64: {{ codecBase64 }}</text>
|
||||
<text class="result-line">Hex: {{ codecHex }}</text>
|
||||
<text class="result-line">Base64url: {{ codecBase64url }}</text>
|
||||
<text class="result-line">反解: {{ codecBackText }}</text>
|
||||
</view>
|
||||
|
||||
<view class="card-block result-card">
|
||||
<text class="card-title">摘要与签名</text>
|
||||
<text class="result-line">MD5: {{ md5Text }}</text>
|
||||
<text class="result-line">SHA256: {{ sha256Text }}</text>
|
||||
<text class="result-line">SHA512: {{ sha512Text }}</text>
|
||||
<text class="result-line">HMAC-SHA256: {{ hmacText }}</text>
|
||||
</view>
|
||||
|
||||
<view class="card-block result-card">
|
||||
<text class="card-title">AES 文本</text>
|
||||
<text class="result-line">密文: {{ aesCipherText }}</text>
|
||||
<text class="result-line">解密: {{ aesPlainText }}</text>
|
||||
</view>
|
||||
|
||||
<view class="card-block result-card">
|
||||
<text class="card-title">AES 字节</text>
|
||||
<text class="result-line">字节密文: {{ aesByteCipherHex }}</text>
|
||||
<text class="result-line">字节解密: {{ aesBytePlainText }}</text>
|
||||
</view>
|
||||
|
||||
<view class="card-block result-card">
|
||||
<text class="card-title">3DES 与 RC4</text>
|
||||
<text class="result-line">3DES 密文: {{ tripleCipherText }}</text>
|
||||
<text class="result-line">3DES 解密: {{ triplePlainText }}</text>
|
||||
<text class="result-line">RC4 密文: {{ rc4CipherHex }}</text>
|
||||
<text class="result-line">RC4 解密: {{ rc4PlainText }}</text>
|
||||
</view>
|
||||
|
||||
<view class="card-block result-card">
|
||||
<text class="card-title">RSA</text>
|
||||
<text class="result-line">公钥长度: {{ rsaPublicBrief }}</text>
|
||||
<text class="result-line">私钥长度: {{ rsaPrivateBrief }}</text>
|
||||
<text class="result-line">RSA 密文: {{ rsaCipherText }}</text>
|
||||
<text class="result-line">RSA 解密: {{ rsaPlainText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-block log-card">
|
||||
<text class="card-title">执行日志</text>
|
||||
<view class="log-list">
|
||||
<text class="log-line" v-for="(item,index) in logLines" :key="index">{{ item }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #ifdef APP -->
|
||||
</scroll-view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { aesDecrypt, aesDecryptBytes, aesDecryptText, aesEncrypt, aesEncryptBytes, aesEncryptText, base64Decode, base64Encode, createRsaPair, defaultByteCipherOptions, defaultTextCipherOptions, defaultTripleDesOptions, decodeText, describeCryptoRuntime, digestText, encodeText, generateRSAKeyPair, md5, rc4DecryptText, rc4EncryptText, rsaDecrypt, rsaDecryptText, rsaEncrypt, rsaEncryptText, signText, tripleDesDecryptText, tripleDesEncryptText, useCrypto } from '@/uni_modules/laoqianjunzi-crypto'
|
||||
import type { CryptoRuntimeSnapshot } from '@/uni_modules/laoqianjunzi-crypto'
|
||||
|
||||
const runtimeInfo = ref(describeCryptoRuntime() as CryptoRuntimeSnapshot)
|
||||
const plainText = ref('Hello laoqianjunzi-crypto')
|
||||
const secretText = ref('1234567890abcdef')
|
||||
const ivText = ref('fedcba0987654321')
|
||||
const macSecretText = ref('signing-key')
|
||||
const legacySecretText = ref('1234567890abcdef12345678')
|
||||
const rsaBitsText = ref('2048')
|
||||
|
||||
const codecBase64 = ref('')
|
||||
const codecHex = ref('')
|
||||
const codecBase64url = ref('')
|
||||
const codecBackText = ref('')
|
||||
|
||||
const md5Text = ref('')
|
||||
const sha256Text = ref('')
|
||||
const sha512Text = ref('')
|
||||
const hmacText = ref('')
|
||||
|
||||
const aesCipherText = ref('')
|
||||
const aesPlainText = ref('')
|
||||
const aesByteCipherHex = ref('')
|
||||
const aesBytePlainText = ref('')
|
||||
|
||||
const tripleCipherText = ref('')
|
||||
const triplePlainText = ref('')
|
||||
const rc4CipherHex = ref('')
|
||||
const rc4PlainText = ref('')
|
||||
|
||||
const rsaCipherText = ref('')
|
||||
const rsaPlainText = ref('')
|
||||
const rsaPublicBrief = ref('')
|
||||
const rsaPrivateBrief = ref('')
|
||||
|
||||
const logLines = ref(['等待执行示例'] as string[])
|
||||
|
||||
function appendLog(message : string) : void {
|
||||
const stamp = new Date().toISOString()
|
||||
logLines.value.unshift(stamp + ' ' + message)
|
||||
if (logLines.value.length > 20) {
|
||||
logLines.value.splice(20)
|
||||
}
|
||||
}
|
||||
|
||||
function bytesToHex(input : Uint8Array) : string {
|
||||
let output = ''
|
||||
let index = 0
|
||||
while (index < input.length) {
|
||||
let part = input[index].toString(16)
|
||||
if (part.length < 2) {
|
||||
part = '0' + part
|
||||
}
|
||||
output += part
|
||||
index += 1
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
function textToBytes(input : string) : Uint8Array {
|
||||
const output = new Uint8Array(input.length)
|
||||
let index = 0
|
||||
while (index < input.length) {
|
||||
const code = input.charCodeAt(index)
|
||||
output[index] = code == null ? 0 : code & 0xff
|
||||
index += 1
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
function bytesToText(input : Uint8Array) : string {
|
||||
let output = ''
|
||||
let index = 0
|
||||
while (index < input.length) {
|
||||
output += String.fromCharCode(input[index])
|
||||
index += 1
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
function currentRsaBits() : number {
|
||||
const parsed = parseInt(rsaBitsText.value)
|
||||
if (parsed == 1024 || parsed == 2048 || parsed == 3072 || parsed == 4096) {
|
||||
return parsed
|
||||
}
|
||||
return 2048
|
||||
}
|
||||
|
||||
function runCodecDemo() : void {
|
||||
codecBase64.value = encodeText('base64', plainText.value)
|
||||
codecHex.value = encodeText('hex', plainText.value)
|
||||
codecBase64url.value = encodeText('base64url', plainText.value)
|
||||
codecBackText.value = decodeText('base64', codecBase64.value)
|
||||
appendLog('编码能力已完成')
|
||||
}
|
||||
|
||||
function runDigestDemo() : void {
|
||||
md5Text.value = digestText('md5', plainText.value)
|
||||
sha256Text.value = digestText('sha256', plainText.value)
|
||||
sha512Text.value = digestText('sha512', plainText.value)
|
||||
hmacText.value = signText('sha256', macSecretText.value, plainText.value)
|
||||
appendLog('摘要与 HMAC 已完成')
|
||||
}
|
||||
|
||||
function runAesDemo() : void {
|
||||
const options = defaultTextCipherOptions()
|
||||
options.mode = 'CBC'
|
||||
options.padding = 'PKCS7'
|
||||
options.ivText = ivText.value
|
||||
options.keyLength = 128
|
||||
aesCipherText.value = aesEncryptText(secretText.value, plainText.value, options)
|
||||
aesPlainText.value = aesDecryptText(secretText.value, aesCipherText.value, options)
|
||||
appendLog('AES 文本加解密已完成')
|
||||
}
|
||||
|
||||
function runByteDemo() : void {
|
||||
const options = defaultByteCipherOptions()
|
||||
options.mode = 'CTR'
|
||||
options.padding = 'PKCS7'
|
||||
options.keyLength = 128
|
||||
const secretBytes = textToBytes(secretText.value)
|
||||
const plainBytes = textToBytes(plainText.value)
|
||||
const cipherBytes = aesEncryptBytes(secretBytes, plainBytes, options)
|
||||
const restoreBytes = aesDecryptBytes(secretBytes, cipherBytes, options)
|
||||
aesByteCipherHex.value = bytesToHex(cipherBytes)
|
||||
aesBytePlainText.value = bytesToText(restoreBytes)
|
||||
appendLog('AES 字节加解密已完成')
|
||||
}
|
||||
|
||||
function runLegacyDemo() : void {
|
||||
const options = defaultTripleDesOptions()
|
||||
options.mode = 'CBC'
|
||||
options.ivText = '12345678'
|
||||
tripleCipherText.value = tripleDesEncryptText(legacySecretText.value, plainText.value, options)
|
||||
triplePlainText.value = tripleDesDecryptText(legacySecretText.value, tripleCipherText.value, options)
|
||||
rc4CipherHex.value = rc4EncryptText(macSecretText.value, plainText.value)
|
||||
rc4PlainText.value = rc4DecryptText(macSecretText.value, rc4CipherHex.value)
|
||||
appendLog('3DES 与 RC4 已完成')
|
||||
}
|
||||
|
||||
function runRsaDemo() : void {
|
||||
const pair = createRsaPair(currentRsaBits())
|
||||
rsaPublicBrief.value = pair.publicKey.length + ' chars'
|
||||
rsaPrivateBrief.value = pair.privateKey.length + ' chars'
|
||||
rsaCipherText.value = rsaEncryptText(pair.publicKey, plainText.value, 'base64')
|
||||
rsaPlainText.value = rsaDecryptText(pair.privateKey, rsaCipherText.value, 'base64')
|
||||
|
||||
console.log("ggggggggggggggggg"+pair.publicKey);
|
||||
console.log("sssssssssssssssss"+pair.publicKey);
|
||||
|
||||
appendLog('RSA 生成与加解密已完成')
|
||||
}
|
||||
|
||||
function runCompatDemo() : void {
|
||||
const xBase64 = base64Encode(plainText.value)
|
||||
const xPlain = base64Decode(xBase64)
|
||||
const xMd5 = md5(plainText.value)
|
||||
const xCipher = aesEncrypt(secretText.value, plainText.value, 'CBC', ivText.value, 16)
|
||||
const xRestore = aesDecrypt(secretText.value, xCipher, 'CBC', ivText.value, 16)
|
||||
const xPair = generateRSAKeyPair(currentRsaBits())
|
||||
const xRsaText = rsaDecrypt(xPair.privateKey, rsaEncrypt(xPair.publicKey, plainText.value))
|
||||
const crypto = useCrypto()
|
||||
const limeIv = crypto.enc.Utf8.parse(ivText.value)
|
||||
const limeCipher = crypto.AES.encrypt(plainText.value, secretText.value, {
|
||||
iv: limeIv,
|
||||
mode: crypto.mode.CBC,
|
||||
padding: crypto.pad.Pkcs7
|
||||
} as UTSJSONObject)
|
||||
const limePlain = crypto.AES.decrypt(limeCipher.toString(), secretText.value, {
|
||||
iv: limeIv,
|
||||
mode: crypto.mode.CBC,
|
||||
padding: crypto.pad.Pkcs7
|
||||
} as UTSJSONObject).toString(crypto.enc.Utf8)
|
||||
const limeRsa = crypto.RSA
|
||||
const limePair = limeRsa.generateKeyPair()
|
||||
limeRsa.setPublicKey(limePair.publicKey)
|
||||
const limeCipherText = limeRsa.encrypt(plainText.value)
|
||||
limeRsa.setPrivateKey(limePair.privateKey)
|
||||
const limeRsaText = limeRsa.decrypt(limeCipherText)
|
||||
appendLog('兼容 API 已完成:x-base64=' + xPlain.length + ' x-md5=' + xMd5.substring(0, 8) + ' x-aes=' + xRestore.length + ' x-rsa=' + xRsaText.length + ' lime-aes=' + limePlain.length + ' lime-rsa=' + limeRsaText.length)
|
||||
}
|
||||
|
||||
function runAllDemos() : void {
|
||||
try {
|
||||
runCodecDemo()
|
||||
runDigestDemo()
|
||||
runAesDemo()
|
||||
runByteDemo()
|
||||
runLegacyDemo()
|
||||
runRsaDemo()
|
||||
runCompatDemo()
|
||||
appendLog('全部能力演示执行结束')
|
||||
} catch (error) {
|
||||
appendLog('执行失败:' + (error as Error).message)
|
||||
}
|
||||
}
|
||||
|
||||
function clearResults() : void {
|
||||
codecBase64.value = ''
|
||||
codecHex.value = ''
|
||||
codecBase64url.value = ''
|
||||
codecBackText.value = ''
|
||||
md5Text.value = ''
|
||||
sha256Text.value = ''
|
||||
sha512Text.value = ''
|
||||
hmacText.value = ''
|
||||
aesCipherText.value = ''
|
||||
aesPlainText.value = ''
|
||||
aesByteCipherHex.value = ''
|
||||
aesBytePlainText.value = ''
|
||||
tripleCipherText.value = ''
|
||||
triplePlainText.value = ''
|
||||
rc4CipherHex.value = ''
|
||||
rc4PlainText.value = ''
|
||||
rsaCipherText.value = ''
|
||||
rsaPlainText.value = ''
|
||||
rsaPublicBrief.value = ''
|
||||
rsaPrivateBrief.value = ''
|
||||
logLines.value = ['结果已清空'] as string[]
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
appendLog('示例页已就绪,点击按钮即可执行')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.crypto-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.crypto-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 48rpx;
|
||||
background-color: #efe8dc;
|
||||
}
|
||||
|
||||
.hero-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 40rpx;
|
||||
border-radius: 48rpx;
|
||||
background-color: #11231c;
|
||||
margin-bottom: 36rpx;
|
||||
}
|
||||
|
||||
.hero-kicker {
|
||||
color: #f2c169;
|
||||
font-size: 26rpx;
|
||||
letter-spacing: 4rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
color: #fff8ea;
|
||||
font-size: 56rpx;
|
||||
font-weight: 700;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.hero-desc {
|
||||
color: #c9d6cc;
|
||||
font-size: 28rpx;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
.card-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 36rpx;
|
||||
border-radius: 40rpx;
|
||||
background-color: #fffaf1;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
color: #24352e;
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
margin-bottom: 18rpx;
|
||||
}
|
||||
|
||||
.runtime-card {
|
||||
background-color: #f7f1e3;
|
||||
}
|
||||
|
||||
.status-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom-width: 2rpx;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #e7dbc8;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
color: #6b6d66;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.status-value {
|
||||
color: #1c2b25;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
background-color: #fff7ee;
|
||||
}
|
||||
|
||||
.field-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
color: #6f6258;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
height: 84rpx;
|
||||
padding: 0 24rpx;
|
||||
border-radius: 24rpx;
|
||||
background-color: #f1e7da;
|
||||
color: #1d2823;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.action-grid {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
min-width: 208rpx;
|
||||
border-radius: 999rpx;
|
||||
background-color: #d8e5db;
|
||||
color: #20312a;
|
||||
font-size: 26rpx;
|
||||
margin-right: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.action-strong {
|
||||
background-color: #17392d;
|
||||
color: #fff8ef;
|
||||
}
|
||||
|
||||
.action-warn {
|
||||
background-color: #ead4b9;
|
||||
color: #5d3722;
|
||||
}
|
||||
|
||||
.result-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
background-color: #fffdf8;
|
||||
}
|
||||
|
||||
.result-line {
|
||||
color: #25322c;
|
||||
font-size: 26rpx;
|
||||
line-height: 38rpx;
|
||||
}
|
||||
|
||||
.log-card {
|
||||
background-color: #15261f;
|
||||
}
|
||||
|
||||
.log-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.log-line {
|
||||
color: #d6e7dc;
|
||||
font-size: 24rpx;
|
||||
line-height: 36rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
</style>
|
||||
+27
-11
@@ -3,6 +3,9 @@ import { Config } from "@/config.uts"
|
||||
import { HttpMethod } from "@/enum/HttpMethod"
|
||||
import { Auth } from "@/utils/Auth.uts"
|
||||
import { XiaoZhi } from "@/utils/XiaoZhi"
|
||||
import { Crypto } from "@/utils/crypto.uts"
|
||||
import { base64Encode, createRsaPair, rsaEncryptText } from "@/uni_modules/laoqianjunzi-crypto"
|
||||
|
||||
|
||||
/**
|
||||
* HTTP请求客户端
|
||||
@@ -19,6 +22,11 @@ export class HttpClient {
|
||||
|
||||
private static instance : HttpClient | null = null
|
||||
|
||||
private static encryptHeader = 'encrypt-key';
|
||||
private static publicKey = '-----BEGIN PUBLIC KEY-----\n' +
|
||||
Config.VITE_APP_RSA_PUBLIC_KEY +
|
||||
'\n-----END PUBLIC KEY-----';
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
@@ -38,7 +46,7 @@ export class HttpClient {
|
||||
* @returns 标准响应对象
|
||||
*/
|
||||
async request<T>(config : RequestConfig) : Promise<ApiResponse<T>> {
|
||||
console.log("请求拦截")
|
||||
console.log("请求拦截,请求地址:" + config.url)
|
||||
|
||||
let url : string = Config.BASE_URL + config.url
|
||||
let data = config.data ?? null
|
||||
@@ -96,14 +104,28 @@ export class HttpClient {
|
||||
*/
|
||||
if (Config.VITE_APP_ENCRYPT) {
|
||||
if (isEncrypt && (config.method === HttpMethod.POST || config.method === HttpMethod.PUT)) {
|
||||
console.log("加密")
|
||||
// AES密钥
|
||||
const aesKey = Crypto.generateAesKey();
|
||||
// Base64(AES_KEY)
|
||||
const aesKeyBase64 = base64Encode(aesKey)
|
||||
|
||||
// RSA加密后的Header值
|
||||
|
||||
|
||||
header[HttpClient.encryptHeader] = rsaEncryptText(HttpClient.publicKey, aesKeyBase64, 'base64')
|
||||
console.log(header[HttpClient.encryptHeader]);
|
||||
const requestData = config.data
|
||||
|
||||
if (requestData != null) {
|
||||
data = Crypto.encryptWithAes(requestData, aesKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.loading == true) {
|
||||
LoadingManager.show()
|
||||
}
|
||||
|
||||
console.log("请求拦截,请求地址:" + url)
|
||||
try {
|
||||
return await new Promise<ApiResponse<T>>((resolve, reject) => {
|
||||
uni.request({
|
||||
@@ -118,26 +140,20 @@ export class HttpClient {
|
||||
enableChunked,
|
||||
success: (res) => {
|
||||
const responseData = res["data"] as UTSJSONObject
|
||||
|
||||
console.log(
|
||||
"响应拦截-响应状态码:" +
|
||||
JSON.stringify(responseData["code"])
|
||||
)
|
||||
|
||||
const apiResponse = new ApiResponse<T>()
|
||||
|
||||
if (responseData != null) {
|
||||
apiResponse.code = (responseData["code"] as number) ?? 0
|
||||
apiResponse.msg = (responseData["msg"] as string) ?? ""
|
||||
apiResponse.data = responseData["data"] as T
|
||||
}
|
||||
console.log("响应拦截-响应状态码:" + apiResponse.code)
|
||||
|
||||
resolve(apiResponse)
|
||||
},
|
||||
fail: (err) => {
|
||||
reject(err)
|
||||
},
|
||||
complete: () => {}
|
||||
complete: () => { }
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
+21
-35
@@ -1,55 +1,34 @@
|
||||
import { createRsaPair, base64Decode, base64Encode, } from "@/uni_modules/laoqianjunzi-crypto";
|
||||
// 在插件中,h5不会导出CryptoRsaPair,需要自定义,等待后续作者处理
|
||||
// #ifdef H5
|
||||
type CryptoRsaPair = {
|
||||
publicKey : string;
|
||||
privateKey : string;
|
||||
}
|
||||
// #endif
|
||||
// #ifdef APP-ANDROID
|
||||
import { CryptoRsaPair } from "@/uni_modules/laoqianjunzi-crypto";
|
||||
// #endif
|
||||
import { createRsaPair, base64Decode, base64Encode, defaultTextCipherOptions, aesEncryptText, } from "@/uni_modules/laoqianjunzi-crypto";
|
||||
|
||||
export class Crypto {
|
||||
/**
|
||||
* 随机生成32位的字符串
|
||||
* @returns {string}
|
||||
*/
|
||||
static generateRandomString() : string {
|
||||
const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
let result = ""
|
||||
for (let i = 0; i < 32; i++) {
|
||||
result += chars.charAt(
|
||||
Math.floor(
|
||||
Math.random() * chars.length
|
||||
)
|
||||
)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static generateRandomString(length = 16) : string {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||
let result = ''
|
||||
|
||||
private static readonly rsaBitsText = ref('2048')
|
||||
static currentRsaBits() : number {
|
||||
const parsed = parseInt(Crypto.rsaBitsText.value)
|
||||
if (parsed == 1024 || parsed == 2048 || parsed == 3072 || parsed == 4096) {
|
||||
return parsed
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length))
|
||||
}
|
||||
return 2048
|
||||
|
||||
return result
|
||||
}
|
||||
/**
|
||||
* 随机生成aes 密钥
|
||||
* @returns {string}
|
||||
*/
|
||||
static generateAesKey() : CryptoRsaPair {
|
||||
return createRsaPair(Crypto.currentRsaBits());
|
||||
static generateAesKey() : string {
|
||||
return Crypto.generateRandomString(16);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密base64
|
||||
* @returns {string}
|
||||
*/
|
||||
static encryptBase64(str : CryptoRsaPair) : string {
|
||||
return base64Encode(str.privateKey + str.publicKey);
|
||||
static encryptBase64(str : string) : string {
|
||||
return base64Encode(str);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,8 +44,15 @@ export class Crypto {
|
||||
* @param aesKey
|
||||
* @returns {string}
|
||||
*/
|
||||
static encryptWithAes() : string {
|
||||
return '';
|
||||
static encryptWithAes(data : any, aesKey : string) : string {
|
||||
// AES配置
|
||||
const options = defaultTextCipherOptions()
|
||||
options.mode = 'ECB'
|
||||
options.padding = 'PKCS7'
|
||||
options.keyLength = 128
|
||||
// 请求体JSON
|
||||
const json = JSON.stringify(data)
|
||||
return aesEncryptText(aesKey, json, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ export class JSEncrypt {
|
||||
* @returns Base64密文
|
||||
*/
|
||||
static encrypt(text : string) : string {
|
||||
if (!text) {
|
||||
if (text == "") {
|
||||
return "";
|
||||
}
|
||||
return rsaEncryptText(JSEncrypt.PUBLIC_KEY, text, "base64");
|
||||
@@ -34,7 +34,7 @@ export class JSEncrypt {
|
||||
* @returns 明文
|
||||
*/
|
||||
static decrypt(cipherText : string) : string {
|
||||
if (!cipherText) {
|
||||
if (cipherText=="") {
|
||||
return "";
|
||||
}
|
||||
return rsaDecryptText(JSEncrypt.PRIVATE_KEY, cipherText, "base64");
|
||||
|
||||
Reference in New Issue
Block a user