feat(安全): 添加加密解密功能并更新考试组件

- 新增crypto-js依赖用于数据加密解密
- 添加decrypt.js工具文件实现AES解密功能
- 修改Exam.vue组件使用加密接口获取考试数据
- 清理main.js中多余空行并格式化代码
This commit is contained in:
yindongqi 2025-08-15 10:49:17 +08:00
parent efb38e1106
commit f7eca311fa
5 changed files with 7157 additions and 7576 deletions

View File

@ -19,6 +19,7 @@
"console": "^0.7.2",
"core-js": "^3.39.0",
"crypto": "^1.0.1",
"crypto-js": "^4.2.0",
"cssnano": "^4.1.10",
"exif-js": "^2.3.0",
"font-awesome": "^4.7.0",

View File

@ -50,7 +50,7 @@
<div class="lianxiTiMu" v-for="(item, index) in current_question_data.content" :key="index"
@click="responseQ(item)">
<span class="lianxiXueZe" v-bind:class="{ active: current_question_data.response == item }">{{ xuhao[index]
}}</span>
}}</span>
<span class="lianxiXueZe1">{{ item }}</span>
</div>
</div>
@ -62,7 +62,7 @@
<div class="lianxiTiMu" v-for="(item, index) in current_question_data.content" :key="index"
@click="responseQ(item)">
<span class="lianxiXueZe" v-bind:class="{ active: current_question_data.response == item }">{{ xuhao[index]
}}</span>
}}</span>
<span class="lianxiXueZe1">{{ item }}</span>
</div>
</div>
@ -101,7 +101,7 @@
<div class="lianxiBottomR" @click="popupVisible = true">
<img src="../../../static/img/gengduo.png" alt="" /><span>{{
current_question + 1
}}</span>/{{ questions.length }}
}}</span>/{{ questions.length }}
</div>
</div>
</div>
@ -116,7 +116,8 @@
</div>
</div>
</mt-popup>
<mt-popup v-model="popupVisible2" popup-transition="popup-fade" style="background-color: #5e5e5e; border-radius: 20px;">
<mt-popup v-model="popupVisible2" popup-transition="popup-fade"
style="background-color: #5e5e5e; border-radius: 20px;">
<div style=" top: 5px; font-size: 20px; font-weight: bold; margin: 20px 0; color: white;">请选择要考试的课程</div>
<div style=" margin: 30px; width: 250px;" v-for="(item, index) in courseData" :key="index">
<mt-button @click="btn_courseID(item.id)" type="default" class=""
@ -143,6 +144,7 @@ import { getStore } from "@/utils/storage";
import { Toast } from "mint-ui";
import { MessageBox } from "mint-ui";
import WXTake from "../study/weixinTake.vue";
import { decrypt } from "@/utils/decrypt";
export default {
name: "LiuYan",
components: { Back, WXTake },
@ -179,17 +181,17 @@ export default {
//
btn_course(num) {
this.num = num;
//
this.getData("/Question/getCourse", {
token: getStore("token"),
}).then((data) => {
this.courseData = data.data;
if(this.courseData.length === 1){
if (this.courseData.length === 1) {
this.btn_courseID(this.courseData[0].id);
}else if(this.courseData.length > 1){
} else if (this.courseData.length > 1) {
this.popupVisible2 = true;
}else {
} else {
Toast("暂无考试");
}
});
@ -267,19 +269,20 @@ export default {
var current_time_stamp = new Date().getTime();
//
this.getData("/Question/getExamQuestions", {
// this.getData("/Question/getExamQuestions", {
this.getData("/Question/getExamQuestionsSecurity", {
token: getStore("token"),
course_id: this.courseID,
}).then(
(data) => {
// console.log("data=>",data.data.exam_data);
if (data.code == 1) {
const questions = decrypt(data.data);
let _this = this;
this.is_start = 1;
this.id = data.data.id;
this.questions = data.data.question_data;
this.current_question_data = data.data.question_data[0];
this.exam_data = data.data.exam_data;
this.id = questions.id;
this.questions = questions.question_data;
this.current_question_data = questions.question_data[0];
this.exam_data = questions.exam_data;
this.title = "正式考试";
this.monishow = false;
var count = 1;
@ -873,6 +876,7 @@ export default {
align-items: center;
z-index: 1000;
}
.mask-content {
background: white;
padding: 30px;
@ -881,6 +885,7 @@ export default {
width: 80%;
max-width: 400px;
}
.mask-content p {
margin-bottom: 20px;
font-size: 16px;

View File

@ -25,7 +25,6 @@ import "regenerator-runtime/runtime";
let wx = require('weixin-js-sdk')
Vue.config.productionTip = false
// mintui模块
Vue.component(Button.name, Button)
Vue.component(Swipe.name, Swipe);

29
src/utils/decrypt.js Normal file
View File

@ -0,0 +1,29 @@
import CryptoJS from 'crypto-js'
const SECRET_KEY = 'mIS*fo4T2ioFSw91Flaovn@ofiq89Fqe';
export function decrypt(encryptedResponse) {
// 1. Base64解码
const iv = CryptoJS.enc.Base64.parse(encryptedResponse.iv)
const payload = CryptoJS.enc.Base64.parse(encryptedResponse.payload)
// 2. 执行AES解密
const decrypted = CryptoJS.AES.decrypt(
{ ciphertext: payload },
CryptoJS.enc.Utf8.parse(SECRET_KEY),
{
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
)
// 3. 转为UTF-8字符串并解析JSON
try {
return JSON.parse(decrypted.toString(CryptoJS.enc.Utf8))
} catch (e) {
console.error("解密失败: ", e)
return null
}
}

14669
yarn.lock

File diff suppressed because it is too large Load Diff