实现数据加密传输,成功调试登录接口
This commit is contained in:
+3
-2
@@ -18,6 +18,7 @@ export class LoginApi {
|
|||||||
clientId: Config.VITE_APP_CLIENT_ID,
|
clientId: Config.VITE_APP_CLIENT_ID,
|
||||||
grantType: 'password'
|
grantType: 'password'
|
||||||
}
|
}
|
||||||
|
// 请求参数处理
|
||||||
const Requestconfig = new RequestConfig()
|
const Requestconfig = new RequestConfig()
|
||||||
Requestconfig.url = "/auth/login"
|
Requestconfig.url = "/auth/login"
|
||||||
Requestconfig.headers!.isToken = false
|
Requestconfig.headers!.isToken = false
|
||||||
@@ -25,14 +26,14 @@ export class LoginApi {
|
|||||||
Requestconfig.headers!.repeatSubmit = false
|
Requestconfig.headers!.repeatSubmit = false
|
||||||
Requestconfig.method = HttpMethod.POST
|
Requestconfig.method = HttpMethod.POST
|
||||||
Requestconfig.data = configData
|
Requestconfig.data = configData
|
||||||
|
//发起请求
|
||||||
const res = await this.http.request<UTSJSONObject>(Requestconfig)
|
const res = await this.http.request<UTSJSONObject>(Requestconfig)
|
||||||
const data = res.data
|
const data = res.data
|
||||||
console.log(data);
|
console.log(data);
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
throw new Error("登陆失败")
|
throw new Error("登陆失败")
|
||||||
}
|
}
|
||||||
// 手动转换 UTSJSONObject 到 VerifyCodeResult
|
// 格式化返回数据 手动转换 UTSJSONObject 到 VerifyCodeResult
|
||||||
const result : LoginResult = {
|
const result : LoginResult = {
|
||||||
access_token: data["access_token"] as string
|
access_token: data["access_token"] as string
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -9,10 +9,10 @@ export class Config {
|
|||||||
|
|
||||||
|
|
||||||
// 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
// 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
||||||
static readonly VITE_APP_ENCRYPT = false
|
static readonly VITE_APP_ENCRYPT = true
|
||||||
// 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
|
// 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换 公钥
|
||||||
static readonly VITE_APP_RSA_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvEDuRIOM3oZPWj9Ukoc5pQklR4PFH6/clnjeFqjDLIgDyQvjxhgqAZQA+E9eD6qu6FsXPmK8djcL+nh3cFHz4pX473jDvO3Sve+8yL3VRQ0n2pRgQ2a01MJsy+WwTZCBYWf0VnLRIvANUoWQgy9vz94q7Va44dg7A1/3ICf+xAwIDAQAB'
|
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=='
|
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": {
|
"style": {
|
||||||
"navigationBarTitleText": "登录"
|
"navigationBarTitleText": "登录"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/test/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "加解密测试"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalStyle": {
|
"globalStyle": {
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('登录失败:', e)
|
console.error('登录失败:'+ e.message)
|
||||||
loadCode()
|
loadCode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<image class="logo" src="/static/logo.png"></image>
|
<image class="logo" src="/static/logo.png"></image>
|
||||||
<text class="title">{{title}}</text>
|
<text class="title">{{title}}</text>
|
||||||
<button @click="goLogin()">去登陆</button>
|
<button @click="goLogin()">去登陆</button>
|
||||||
|
<button @click="goLogin1()">加解密测试</button>
|
||||||
需要一个导航栏
|
需要一个导航栏
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -18,6 +19,14 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goLogin1() {
|
||||||
|
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/test/index'
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<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>
|
||||||
+26
-10
@@ -3,6 +3,9 @@ import { Config } from "@/config.uts"
|
|||||||
import { HttpMethod } from "@/enum/HttpMethod"
|
import { HttpMethod } from "@/enum/HttpMethod"
|
||||||
import { Auth } from "@/utils/Auth.uts"
|
import { Auth } from "@/utils/Auth.uts"
|
||||||
import { XiaoZhi } from "@/utils/XiaoZhi"
|
import { XiaoZhi } from "@/utils/XiaoZhi"
|
||||||
|
import { Crypto } from "@/utils/crypto.uts"
|
||||||
|
import { base64Encode, createRsaPair, rsaEncryptText } from "@/uni_modules/laoqianjunzi-crypto"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP请求客户端
|
* HTTP请求客户端
|
||||||
@@ -19,6 +22,11 @@ export class HttpClient {
|
|||||||
|
|
||||||
private static instance : HttpClient | null = null
|
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 标准响应对象
|
* @returns 标准响应对象
|
||||||
*/
|
*/
|
||||||
async request<T>(config : RequestConfig) : Promise<ApiResponse<T>> {
|
async request<T>(config : RequestConfig) : Promise<ApiResponse<T>> {
|
||||||
console.log("请求拦截")
|
console.log("请求拦截,请求地址:" + config.url)
|
||||||
|
|
||||||
let url : string = Config.BASE_URL + config.url
|
let url : string = Config.BASE_URL + config.url
|
||||||
let data = config.data ?? null
|
let data = config.data ?? null
|
||||||
@@ -96,14 +104,28 @@ export class HttpClient {
|
|||||||
*/
|
*/
|
||||||
if (Config.VITE_APP_ENCRYPT) {
|
if (Config.VITE_APP_ENCRYPT) {
|
||||||
if (isEncrypt && (config.method === HttpMethod.POST || config.method === HttpMethod.PUT)) {
|
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) {
|
if (config.loading == true) {
|
||||||
LoadingManager.show()
|
LoadingManager.show()
|
||||||
}
|
}
|
||||||
|
console.log("请求拦截,请求地址:" + url)
|
||||||
try {
|
try {
|
||||||
return await new Promise<ApiResponse<T>>((resolve, reject) => {
|
return await new Promise<ApiResponse<T>>((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
@@ -118,19 +140,13 @@ export class HttpClient {
|
|||||||
enableChunked,
|
enableChunked,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
const responseData = res["data"] as UTSJSONObject
|
const responseData = res["data"] as UTSJSONObject
|
||||||
|
|
||||||
console.log(
|
|
||||||
"响应拦截-响应状态码:" +
|
|
||||||
JSON.stringify(responseData["code"])
|
|
||||||
)
|
|
||||||
|
|
||||||
const apiResponse = new ApiResponse<T>()
|
const apiResponse = new ApiResponse<T>()
|
||||||
|
|
||||||
if (responseData != null) {
|
if (responseData != null) {
|
||||||
apiResponse.code = (responseData["code"] as number) ?? 0
|
apiResponse.code = (responseData["code"] as number) ?? 0
|
||||||
apiResponse.msg = (responseData["msg"] as string) ?? ""
|
apiResponse.msg = (responseData["msg"] as string) ?? ""
|
||||||
apiResponse.data = responseData["data"] as T
|
apiResponse.data = responseData["data"] as T
|
||||||
}
|
}
|
||||||
|
console.log("响应拦截-响应状态码:" + apiResponse.code)
|
||||||
|
|
||||||
resolve(apiResponse)
|
resolve(apiResponse)
|
||||||
},
|
},
|
||||||
|
|||||||
+21
-35
@@ -1,55 +1,34 @@
|
|||||||
import { createRsaPair, base64Decode, base64Encode, } from "@/uni_modules/laoqianjunzi-crypto";
|
import { createRsaPair, base64Decode, base64Encode, defaultTextCipherOptions, aesEncryptText, } 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
|
|
||||||
|
|
||||||
export class Crypto {
|
export class Crypto {
|
||||||
/**
|
/**
|
||||||
* 随机生成32位的字符串
|
* 随机生成32位的字符串
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
static generateRandomString() : string {
|
static generateRandomString(length = 16) : string {
|
||||||
const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||||
let result = ""
|
let result = ''
|
||||||
for (let i = 0; i < 32; i++) {
|
|
||||||
result += chars.charAt(
|
for (let i = 0; i < length; i++) {
|
||||||
Math.floor(
|
result += chars.charAt(Math.floor(Math.random() * chars.length))
|
||||||
Math.random() * chars.length
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly rsaBitsText = ref('2048')
|
return result
|
||||||
static currentRsaBits() : number {
|
|
||||||
const parsed = parseInt(Crypto.rsaBitsText.value)
|
|
||||||
if (parsed == 1024 || parsed == 2048 || parsed == 3072 || parsed == 4096) {
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return 2048
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 随机生成aes 密钥
|
* 随机生成aes 密钥
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
static generateAesKey() : CryptoRsaPair {
|
static generateAesKey() : string {
|
||||||
return createRsaPair(Crypto.currentRsaBits());
|
return Crypto.generateRandomString(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加密base64
|
* 加密base64
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
static encryptBase64(str : CryptoRsaPair) : string {
|
static encryptBase64(str : string) : string {
|
||||||
return base64Encode(str.privateKey + str.publicKey);
|
return base64Encode(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,8 +44,15 @@ export class Crypto {
|
|||||||
* @param aesKey
|
* @param aesKey
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
static encryptWithAes() : string {
|
static encryptWithAes(data : any, aesKey : string) : string {
|
||||||
return '';
|
// 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密文
|
* @returns Base64密文
|
||||||
*/
|
*/
|
||||||
static encrypt(text : string) : string {
|
static encrypt(text : string) : string {
|
||||||
if (!text) {
|
if (text == "") {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return rsaEncryptText(JSEncrypt.PUBLIC_KEY, text, "base64");
|
return rsaEncryptText(JSEncrypt.PUBLIC_KEY, text, "base64");
|
||||||
@@ -34,7 +34,7 @@ export class JSEncrypt {
|
|||||||
* @returns 明文
|
* @returns 明文
|
||||||
*/
|
*/
|
||||||
static decrypt(cipherText : string) : string {
|
static decrypt(cipherText : string) : string {
|
||||||
if (!cipherText) {
|
if (cipherText=="") {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return rsaDecryptText(JSEncrypt.PRIVATE_KEY, cipherText, "base64");
|
return rsaDecryptText(JSEncrypt.PRIVATE_KEY, cipherText, "base64");
|
||||||
|
|||||||
Reference in New Issue
Block a user