接口参数加密,通过请求控制加解密开关
This commit is contained in:
parent
aa29ef5646
commit
00d20664ac
|
|
@ -9,7 +9,7 @@
|
|||
// import ThemePicker from '@/components/ThemePicker'
|
||||
|
||||
import autofit from 'autofit.js'
|
||||
|
||||
import { get } from '@/utils/config'
|
||||
export default {
|
||||
name: 'App',
|
||||
// components: { ThemePicker },
|
||||
|
|
@ -25,7 +25,9 @@ export default {
|
|||
},
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
get();
|
||||
},
|
||||
mounted() {
|
||||
autofit.init({
|
||||
/* designHeight: 1080,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 获取系统配置
|
||||
export const getConfig = () => {
|
||||
return request({
|
||||
url: '/auth/getConfig',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 3.4 KiB |
|
|
@ -558,16 +558,19 @@ export default {
|
|||
roam: false,
|
||||
coordinateSystem: 'geo', //使用坐标系
|
||||
zlevel: 1,
|
||||
symbolSize: [80, 90],
|
||||
symbolSize: [60, 70],
|
||||
symbol: (value, params) => {
|
||||
if (params.data.errorNum>0) {
|
||||
return (
|
||||
'image://' + require('@/assets/img/myImage/point_red.png')
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
'image://' + require('@/assets/img/myImage/point.png')
|
||||
)
|
||||
if(params.data.devStatus==0){
|
||||
return ('image://' + require('@/assets/img/myImage/point_grey.png'))
|
||||
}else{
|
||||
return ('image://' + require('@/assets/img/myImage/point.png'))
|
||||
}
|
||||
|
||||
}
|
||||
/*if (params.name == '安徽省') {
|
||||
return 'image://' + require('@/assets/img/myImage/markPoints.png')
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ import TableModel from '@/components/TableModel'
|
|||
import JsonExcel from 'vue-json-excel'
|
||||
Vue.prototype.$eventBus = new Vue()
|
||||
|
||||
import global_ from '@/utils/globalUrl'
|
||||
// import global_ from '@/utils/globalUrl'
|
||||
|
||||
// 全局方法挂载
|
||||
Vue.prototype.getDicts = getDicts
|
||||
|
|
@ -60,7 +60,7 @@ Vue.prototype.selectDictLabels = selectDictLabels
|
|||
Vue.prototype.download = download
|
||||
Vue.prototype.downloadJson = downloadJson
|
||||
Vue.prototype.handleTree = handleTree
|
||||
Vue.prototype.globalUrl = global_
|
||||
// Vue.prototype.globalUrl = global_
|
||||
Vue.prototype.indexContinuation = indexContinuation
|
||||
|
||||
// 全局组件挂载
|
||||
|
|
|
|||
|
|
@ -232,3 +232,10 @@ export function tansParams(params) {
|
|||
export function blobValidate(data) {
|
||||
return data.type !== 'application/json'
|
||||
}
|
||||
|
||||
// 处理表格索引延续问题
|
||||
export function indexContinuation(num, size) {
|
||||
return (num - 1) * size + 1
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
let chapter = (text, companyName) => {
|
||||
let canvas = document.getElementById("canvas");
|
||||
let context = canvas.getContext("2d");
|
||||
|
||||
//let text = "XXX专用章";
|
||||
//let companyName = "XXX科技股份有限公司";
|
||||
|
||||
// 绘制印章边框
|
||||
let width = canvas.width / 2;
|
||||
let height = canvas.height / 2;
|
||||
context.lineWidth = 3;
|
||||
context.strokeStyle = "#f00";
|
||||
context.beginPath();
|
||||
context.arc(width, height, 80, 0, Math.PI * 2); //宽、高、半径
|
||||
context.stroke();
|
||||
|
||||
//画五角星
|
||||
create5star(context, width, height, 20, "#f00", 0);
|
||||
|
||||
// 绘制印章名称
|
||||
context.font = "14px 宋体";
|
||||
context.textBaseline = "middle"; //设置文本的垂直对齐方式
|
||||
context.textAlign = "center"; //设置文本的水平对对齐方式
|
||||
context.lineWidth = 1;
|
||||
context.strokeStyle = "#f00";
|
||||
context.strokeText(text, width, height + 50);
|
||||
|
||||
// 绘制印章单位
|
||||
context.translate(width, height); // 平移到此位置,
|
||||
context.font = "14px 宋体";
|
||||
let count = companyName.length; // 字数
|
||||
let angle = (4 * Math.PI) / (3 * (count - 1)); // 字间角度
|
||||
let chars = companyName.split("");
|
||||
let c;
|
||||
for (let i = 0; i < count; i++) {
|
||||
c = chars[i]; // 需要绘制的字符
|
||||
if (i == 0) {
|
||||
context.rotate((5 * Math.PI) / 6);
|
||||
} else {
|
||||
context.rotate(angle);
|
||||
}
|
||||
|
||||
context.save();
|
||||
context.translate(65, 0); // 平移到此位置,此时字和x轴垂直,公司名称和最外圈的距离
|
||||
context.rotate(Math.PI / 2); // 旋转90度,让字平行于x轴
|
||||
context.strokeText(c, 0, 0); // 此点为字的中心点
|
||||
context.restore();
|
||||
}
|
||||
|
||||
//绘制五角星
|
||||
function create5star(context, sx, sy, radius, color, rotato) {
|
||||
context.save();
|
||||
context.fillStyle = color;
|
||||
context.translate(sx, sy); //移动坐标原点
|
||||
context.rotate(Math.PI + rotato); //旋转
|
||||
context.beginPath(); //创建路径
|
||||
// let x = Math.sin(0);
|
||||
// let y = Math.cos(0);
|
||||
let dig = (Math.PI / 5) * 4;
|
||||
for (let i = 0; i < 5; i++) {
|
||||
//画五角星的五条边
|
||||
let x = Math.sin(i * dig);
|
||||
let y = Math.cos(i * dig);
|
||||
context.lineTo(x * radius, y * radius);
|
||||
}
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
context.fill();
|
||||
context.restore();
|
||||
}
|
||||
};
|
||||
|
||||
export default chapter;
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { getConfig } from '@/api/config';
|
||||
export function get() {
|
||||
getConfig()
|
||||
.then(response => {
|
||||
console.log(response)
|
||||
localStorage.setItem('systemConfig', JSON.stringify(response.data));
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Failed to fetch config:', error);
|
||||
});
|
||||
}
|
||||
|
|
@ -1,80 +1,23 @@
|
|||
// 密码强度级别常量
|
||||
const STRENGTH_LEVELS = {
|
||||
WEAK: 'weak', // 弱:一类字符
|
||||
MEDIUM: 'medium', // 中:两类字符
|
||||
STRONG: 'strong', // 强:三类字符
|
||||
VERY_STRONG: 'very-strong' // 非常强:四类字符
|
||||
}
|
||||
|
||||
// 数据设置常量
|
||||
const DATA_SETTINGS = {
|
||||
OPEN: true, // 开启
|
||||
CLOSE: false // 关闭
|
||||
}
|
||||
|
||||
// SM 配置
|
||||
const SM_CONFIG = {
|
||||
SALT: '2cc0c5f9f1749f1632efa9f63e902323' // SM3 盐值(16 字节)
|
||||
export const SM_CONFIG = {
|
||||
SALT: '2cc0c5f9f1749f1632efa9f63e902323', // SM3 盐值(16 字节)
|
||||
SM4_KEY: "78d1295afa99449b99d6f83820e6965c", // SM4 对称加密密钥
|
||||
SM4_SALT: "f555adf6c01d0ab0761e626a2dae34a2",
|
||||
SM2_PUBLIC_KEY: 'your-public-key', // SM2 公钥
|
||||
SM2_PRIVATE_KEY: 'your-private-key' // SM2 私钥
|
||||
}
|
||||
|
||||
// AES 配置
|
||||
const AES_CONFIG = {
|
||||
export const AES_CONFIG = {
|
||||
AES_KEY: 'zhgd@bonus@zhgd@bonus@1234567890', // AES key值
|
||||
AES_IV: '1234567812345678' // AES 偏移量
|
||||
}
|
||||
|
||||
// 登录配置
|
||||
const LOGIN_CONFIG = {
|
||||
CODE_PHONE_LOGIN: DATA_SETTINGS.CLOSE, // 手机号验证码登录(true:开启,false:关闭)
|
||||
CODE_EMAIL_LOGIN: DATA_SETTINGS.OPEN,// 邮箱验证码登录(true:开启,false:关闭)
|
||||
PHONE_LOGIN: DATA_SETTINGS.OPEN, // 手机号密码登录(true:开启,false:关闭)
|
||||
EMAIL_LOGIN: DATA_SETTINGS.CLOSE // 邮箱密码登录(true:开启,false:关闭)
|
||||
}
|
||||
//注册配置
|
||||
const REGISTER_CONFIG = {
|
||||
PHONE_REGISTER: DATA_SETTINGS.OPEN, // 手机号注册(true:开启,false:关闭)
|
||||
EMAIL_REGISTER: DATA_SETTINGS.CLOSE // 邮箱注册(true:开启,false:关闭)
|
||||
export function generateUUID() {
|
||||
// 使用当前时间戳和随机数生成一个 UUID
|
||||
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
const r = Math.random() * 16 | 0; // 生成随机数
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8); // 根据 UUID 规范生成相应的值
|
||||
return v.toString(16); // 转换为十六进制
|
||||
});
|
||||
}
|
||||
|
||||
// 配置设置
|
||||
const CONFIG = {
|
||||
STRENGTH: STRENGTH_LEVELS.STRONG,//密码强度配置
|
||||
IS_OPEN_REGISTER: REGISTER_CONFIG.PHONE_REGISTER || REGISTER_CONFIG.EMAIL_REGISTER, // 是否开启注册
|
||||
IS_CODE_LOGIN: LOGIN_CONFIG.CODE_EMAIL_LOGIN || LOGIN_CONFIG.CODE_PHONE_LOGIN, // 是否开启短信登录
|
||||
// 数据设置
|
||||
dataSettings: {
|
||||
integrityCheck: DATA_SETTINGS.OPEN, // 数据完整性校验(true:开启,false:关闭)
|
||||
encryptRequest: DATA_SETTINGS.OPEN, // 数据传输加密(true:开启,false:关闭)
|
||||
encryptResponse: DATA_SETTINGS.OPEN // 数据返回解密(true:开启,false:关闭)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取占位符文本的函数
|
||||
// 获取占位符文本的函数
|
||||
function getPlaceholderText() {
|
||||
const loginOptions = []
|
||||
if (LOGIN_CONFIG.PHONE_LOGIN) loginOptions.push('手机号')
|
||||
if (LOGIN_CONFIG.EMAIL_LOGIN) loginOptions.push('邮箱')
|
||||
return `用户名${loginOptions.length ? '/' + loginOptions.join('/') : ''}`
|
||||
}
|
||||
|
||||
// 获取占位符文本的函数
|
||||
// 获取占位符文本的函数
|
||||
function getCodePlaceholderText() {
|
||||
const loginOptions = []
|
||||
if (LOGIN_CONFIG.CODE_PHONE_LOGIN) loginOptions.push('手机号')
|
||||
if (LOGIN_CONFIG.CODE_EMAIL_LOGIN) loginOptions.push('邮箱')
|
||||
return loginOptions.length ? loginOptions.join('/') : ''
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
STRENGTH_LEVELS,
|
||||
DATA_SETTINGS,
|
||||
CONFIG,
|
||||
SM_CONFIG,
|
||||
AES_CONFIG,
|
||||
LOGIN_CONFIG,
|
||||
REGISTER_CONFIG,
|
||||
getPlaceholderText,
|
||||
getCodePlaceholderText
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Vue from 'vue'
|
||||
import { mergeRecursive } from "@/utils/ruoyi";
|
||||
import { mergeRecursive } from "@/utils/bonus";
|
||||
import DictMeta from './DictMeta'
|
||||
import DictData from './DictData'
|
||||
|
||||
|
|
@ -77,7 +77,6 @@ function loadDict(dict, dictMeta) {
|
|||
dicts.forEach(d => {
|
||||
Vue.set(dict.label[type], d.value, d.label)
|
||||
})
|
||||
// console.log(dict)
|
||||
return dicts
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { mergeRecursive } from "@/utils/ruoyi";
|
||||
import { mergeRecursive } from "@/utils/bonus";
|
||||
import DictOptions from './DictOptions'
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { mergeRecursive } from "@/utils/ruoyi";
|
||||
import { mergeRecursive } from "@/utils/bonus";
|
||||
import dictConverter from './DictConverter'
|
||||
|
||||
export const options = {
|
||||
|
|
@ -8,7 +8,6 @@ export const options = {
|
|||
* 字典请求,方法签名为function(dictMeta: DictMeta): Promise
|
||||
*/
|
||||
request: (dictMeta) => {
|
||||
console.log(`load dict ${dictMeta.type}`)
|
||||
return Promise.resolve([])
|
||||
},
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
// 下载blob文件
|
||||
export const downloadFile = ({ fileData, fileType, fileName }) => {
|
||||
const blob = new Blob([fileData], {
|
||||
type: fileType
|
||||
})
|
||||
const link = document.createElement('a')
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = fileName
|
||||
link.style.display = 'none'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
URL.revokeObjectURL(link.href)
|
||||
document.body.removeChild(link)
|
||||
}
|
||||
|
||||
// 通用a链接下载
|
||||
export const downloadFileByUrl = (url) => {
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', '')
|
||||
link.style.display = 'none'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
URL.revokeObjectURL(link.href)
|
||||
document.body.removeChild(link)
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
|
||||
// const qrUrl = 'http://192.168.0.14:21624/qrCode/qrCodePage?qrCode='; //测试
|
||||
// const qrUrl = 'http://112.29.103.165:21626/qrCode/qrCodePage?qrCode='; //重庆
|
||||
// const qrUrl = 'http://112.29.103.165:21624/qrCode/qrCodePage?qrCode='; //宁夏
|
||||
// const qrUrl = 'https://z.csgmall.com.cn/gl/qrCode/qrCodePage?qrCode='; //南网
|
||||
|
||||
// const qrUrl = process.env.NODE_ENV === 'production' ? 'http://192.168.0.14:18866/qrCode/qrCodePage?qrCode=' : 'http://192.168.0.14:21624/qrCode/qrCodePage?qrCode='
|
||||
let qrUrl = ''
|
||||
const origin = window.location.origin
|
||||
if (origin == 'http://112.29.103.165:21626') {
|
||||
qrUrl = 'http://112.29.103.165:21626/qrCode/qrCodePage?qrCode='
|
||||
} else {
|
||||
qrUrl = 'http://192.168.0.14:18866/qrCode/qrCodePage?qrCode='
|
||||
}
|
||||
|
||||
export default {
|
||||
qrUrl,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import { parseTime } from './ruoyi'
|
||||
import { parseTime } from './bonus'
|
||||
|
||||
/**
|
||||
* 表格时间格式化
|
||||
*/
|
||||
export function formatDate(cellValue) {
|
||||
if (cellValue == null || cellValue == "") return "";
|
||||
var date = new Date(cellValue)
|
||||
var date = new Date(cellValue)
|
||||
var year = date.getFullYear()
|
||||
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
|
||||
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
|
||||
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
|
||||
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
|
||||
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
|
||||
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
|
||||
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
|
||||
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
|
||||
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
|
||||
}
|
||||
|
|
@ -330,7 +330,7 @@ export function makeMap(str, expectsLowerCase) {
|
|||
? val => map[val.toLowerCase()]
|
||||
: val => map[val]
|
||||
}
|
||||
|
||||
|
||||
export const exportDefault = 'export default '
|
||||
|
||||
export const beautifierConf = {
|
||||
|
|
@ -387,4 +387,4 @@ export function camelCase(str) {
|
|||
export function isNumberStr(str) {
|
||||
return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
|
||||
|
||||
// 密钥对生成 http://web.chacuo.net/netrsakeypair
|
||||
|
||||
const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
|
||||
'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||
|
||||
const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
|
||||
'7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
|
||||
'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
|
||||
'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
|
||||
'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
|
||||
'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
|
||||
'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
|
||||
'UP8iWi1Qw0Y='
|
||||
|
||||
// 加密
|
||||
export function encrypt(txt) {
|
||||
const encryptor = new JSEncrypt()
|
||||
encryptor.setPublicKey(publicKey) // 设置公钥
|
||||
return encryptor.encrypt(txt) // 对数据进行加密
|
||||
}
|
||||
|
||||
// 解密
|
||||
export function decrypt(txt) {
|
||||
const encryptor = new JSEncrypt()
|
||||
encryptor.setPrivateKey(privateKey) // 设置私钥
|
||||
return encryptor.decrypt(txt) // 对数据进行解密
|
||||
}
|
||||
|
||||
|
|
@ -6,11 +6,10 @@ import errorCode from '@/utils/errorCode'
|
|||
import { tansParams, blobValidate } from '@/utils/bonus'
|
||||
import cache from '@/plugins/cache'
|
||||
import { saveAs } from 'file-saver'
|
||||
import { encryptCBC, decryptCBC } from '@/utils/aescbc'
|
||||
import { CONFIG } from '@/utils/configure'
|
||||
import { hashWithSM3AndSalt } from '@/utils/sm'
|
||||
|
||||
//let token = localStorage.getItem("tokens");
|
||||
import { decryptWithSM4, encryptWithSM4, hashWithSM3AndSalt } from '@/utils/sm'
|
||||
const systemConfig = JSON.parse(localStorage.getItem('systemConfig')) || {
|
||||
requestConfig: { encryptRequest: false, checkIntegrity: false, encryptResponse: false }
|
||||
};
|
||||
|
||||
let downloadLoadingInstance
|
||||
// 是否显示重新登录
|
||||
|
|
@ -40,11 +39,11 @@ service.interceptors.request.use(config => {
|
|||
|
||||
// 设置请求头
|
||||
//入参加密
|
||||
config.headers['encryptRequest'] = CONFIG.dataSettings.encryptRequest && encryptRequest ? 'true' : 'false'
|
||||
config.headers['encryptRequest'] = systemConfig.requestConfig.encryptRequest && encryptRequest ? 'true' : 'false'
|
||||
// 数据完整性校验
|
||||
config.headers['checkIntegrity'] = CONFIG.dataSettings.integrityCheck && checkIntegrity ? 'true' : 'false'
|
||||
config.headers['checkIntegrity'] = systemConfig.requestConfig.checkIntegrity && checkIntegrity ? 'true' : 'false'
|
||||
//回参是否加密
|
||||
config.headers['encryptResponse'] = CONFIG.dataSettings.encryptResponse && encryptResponse ? 'true' : 'false'
|
||||
config.headers['encryptResponse'] = systemConfig.requestConfig.encryptResponse && encryptResponse ? 'true' : 'false'
|
||||
|
||||
const isRepeatSubmit = repeatSubmit
|
||||
// 处理 Token
|
||||
|
|
@ -52,21 +51,6 @@ service.interceptors.request.use(config => {
|
|||
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义 token
|
||||
}
|
||||
|
||||
// // 处理 GET 请求
|
||||
// if (config.method === 'get' && config.params) {
|
||||
// let params = tansParams(config.params).slice(0, -1)
|
||||
// // 数据完整性校验
|
||||
// if (CONFIG.dataSettings.integrityCheck && checkIntegrity) {
|
||||
// config.headers['Params-Hash'] = hashWithSM3AndSalt(params)
|
||||
// }
|
||||
// // 加密参数
|
||||
// if (CONFIG.dataSettings.encryptRequest && encryptRequest) {
|
||||
// params = encryptCBC(params)
|
||||
// }
|
||||
// config.url = `${config.url}?${params}`
|
||||
// config.params = {}
|
||||
// }
|
||||
|
||||
// get请求映射params参数
|
||||
if (config.method === 'get' && config.params) {
|
||||
let url = config.url + '?' + tansParams(config.params);
|
||||
|
|
@ -78,15 +62,12 @@ service.interceptors.request.use(config => {
|
|||
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
|
||||
let data = typeof config.data === 'object' ? JSON.stringify(config.data) : config.data
|
||||
let contentType = config.headers['Content-Type']
|
||||
if (contentType.includes('application/json')) {
|
||||
// 数据完整性校验
|
||||
if (CONFIG.dataSettings.integrityCheck && checkIntegrity) {
|
||||
config.headers['Params-Hash'] = hashWithSM3AndSalt(data)
|
||||
config.data = data
|
||||
}
|
||||
if (contentType.includes('application/json') && typeof data !== 'undefined') {
|
||||
// 加密数据
|
||||
if (CONFIG.dataSettings.encryptRequest && encryptRequest) {
|
||||
config.data = encryptCBC(data)
|
||||
if (systemConfig.requestConfig.encryptRequest && encryptRequest) {
|
||||
console.log(data);
|
||||
console.log(hashWithSM3AndSalt(data));
|
||||
config.data = encryptWithSM4(data+"|"+hashWithSM3AndSalt(data))
|
||||
}
|
||||
}
|
||||
// 检查请求数据大小
|
||||
|
|
@ -115,8 +96,8 @@ service.interceptors.request.use(config => {
|
|||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(res => {
|
||||
if (res.headers.encryptresponse) {
|
||||
res.data = JSON.parse(decryptCBC(res.data))
|
||||
if (res.headers.encryptresponse && !res.data.hasOwnProperty('code')) {
|
||||
res.data = JSON.parse(decryptWithSM4(res.data))
|
||||
}
|
||||
// 未设置状态码则默认成功状态
|
||||
const code = res.data.code || 200
|
||||
|
|
@ -157,7 +138,6 @@ service.interceptors.response.use(res => {
|
|||
}
|
||||
},
|
||||
error => {
|
||||
console.log('err' + error)
|
||||
let { message } = error
|
||||
if (message == 'Network Error') {
|
||||
message = '后端接口连接异常'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
// src/utils/encryption.js
|
||||
import sm3 from 'sm-crypto/src/sm3'
|
||||
import { sm2, sm3, sm4 } from 'sm-crypto'
|
||||
// 配置项,例如盐值、SM2 公私钥、SM4 密钥
|
||||
import { SM_CONFIG } from './configure'
|
||||
import SM4 from 'sm-crypto/src/sm4'
|
||||
import { hexToArray } from 'sm-crypto/src/sm2/utils'
|
||||
|
||||
// SM3 哈希
|
||||
export function hashSM3(text) {
|
||||
|
|
@ -15,3 +18,33 @@ export function hashWithSM3AndSalt(text) {
|
|||
// 使用 SM3 进行哈希
|
||||
return hashSM3(textWithSalt)
|
||||
}
|
||||
|
||||
// SM2 加密
|
||||
export function encryptWithSM2(text) {
|
||||
// SM2 公钥加密
|
||||
return sm2.doEncrypt(text, SM_CONFIG.SM2_PUBLIC_KEY)
|
||||
}
|
||||
|
||||
// SM2 解密
|
||||
export function decryptWithSM2(encryptedText) {
|
||||
// SM2 私钥解密
|
||||
return sm2.doDecrypt(encryptedText, SM_CONFIG.SM2_PRIVATE_KEY)
|
||||
}
|
||||
/**
|
||||
* 加密函数
|
||||
* @param {string} plainText
|
||||
* @returns {string} 加密后的密文(Hex 编码格式)
|
||||
*/
|
||||
export function encryptWithSM4(plainText) {
|
||||
return sm4.encrypt(plainText, SM_CONFIG.SM4_KEY,{ mode: 'cbc', padding: 'pkcs#5',iv:SM_CONFIG.SM4_SALT});
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密函数
|
||||
* @param {string} cipherText
|
||||
* @returns {string} 解密后的明文
|
||||
*/
|
||||
export function decryptWithSM4(cipherText){
|
||||
return SM4.decrypt(cipherText, SM_CONFIG.SM4_KEY,{ mode: 'cbc', padding: 'pkcs#5' ,iv:SM_CONFIG.SM4_SALT});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import { CONFIG } from '@/utils/configure'
|
||||
import passwordConfig from '@/utils/passwordConfig'
|
||||
|
||||
const systemConfig = JSON.parse(localStorage.getItem('systemConfig'));
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {Boolean}
|
||||
|
|
@ -92,71 +90,12 @@ export function validPwd(value) {
|
|||
return reg.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 弱:长度至少为8个字符。
|
||||
* 一般:长度至少为8个字符,并包含至少一种字符类型。
|
||||
* 强:长度至少为8个字符,并包含至少两种字符类型。
|
||||
* 非常强:长度至少为8个字符,并包含所有四种字符类型。
|
||||
* @param rule
|
||||
* @param value
|
||||
* @param callback
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export function validatePassword(rule, value, callback) {
|
||||
if (!value) {
|
||||
return callback(new Error('请输入密码'))
|
||||
}
|
||||
|
||||
const lengthRegex = /^.{8,20}$/
|
||||
const uppercaseRegex = /[A-Z]/
|
||||
const lowercaseRegex = /[a-z]/
|
||||
const digitRegex = /\d/
|
||||
const specialCharRegex = /[!@#$%^&*(),.?":{}|<>]/
|
||||
|
||||
if (!lengthRegex.test(value)) {
|
||||
return callback(new Error('密码长度必须为8到20位'))
|
||||
}
|
||||
|
||||
const checks = [
|
||||
{ regex: uppercaseRegex, message: '必须包含至少一个大写字母' },
|
||||
{ regex: lowercaseRegex, message: '必须包含至少一个小写字母' },
|
||||
{ regex: digitRegex, message: '必须包含至少一个数字' },
|
||||
{ regex: specialCharRegex, message: '必须包含至少一个特殊字符' }
|
||||
]
|
||||
|
||||
let passedChecks = checks.filter(check => check.regex.test(value)).length
|
||||
|
||||
let requiredChecks
|
||||
switch (CONFIG.STRENGTH) {
|
||||
case 'weak':
|
||||
requiredChecks = 1
|
||||
break
|
||||
case 'medium':
|
||||
requiredChecks = 2
|
||||
break
|
||||
case 'strong':
|
||||
requiredChecks = 3
|
||||
break
|
||||
case 'very-strong':
|
||||
requiredChecks = 4
|
||||
break
|
||||
default:
|
||||
return callback(new Error('请选择有效的密码强度'))
|
||||
}
|
||||
|
||||
if (passedChecks < requiredChecks) {
|
||||
return callback(new Error(`密码至少包含 ${requiredChecks} 类字符(大写字母,小写字母,数字,特殊字符)`))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
export function validateNewPassword(rule, value, callback) {
|
||||
// 使用配置文件中的策略进行验证
|
||||
|
||||
// 1. 检查密码长度
|
||||
if (value.length < passwordConfig.minLength || value.length > passwordConfig.maxLength) {
|
||||
callback(new Error('密码长度应为' + passwordConfig.minLength + '至' + passwordConfig.maxLength + '位!'))
|
||||
if (value.length < systemConfig.passwordConfig.minLength || value.length > systemConfig.passwordConfig.maxLength) {
|
||||
callback(new Error('密码长度应为' + systemConfig.passwordConfig.minLength + '至' + systemConfig.passwordConfig.maxLength + '位!'))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -166,46 +105,113 @@ export function validateNewPassword(rule, value, callback) {
|
|||
const hasDigit = /\d/.test(value)
|
||||
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value)
|
||||
|
||||
if (passwordConfig.requireUpperCase && !hasUpperCase) {
|
||||
if (systemConfig.passwordConfig.requireUpperCase && !hasUpperCase) {
|
||||
callback(new Error('密码必须包含大写字母!'))
|
||||
return
|
||||
}
|
||||
if (passwordConfig.requireLowerCase && !hasLowerCase) {
|
||||
if (systemConfig.passwordConfig.requireLowerCase && !hasLowerCase) {
|
||||
callback(new Error('密码必须包含小写字母!'))
|
||||
return
|
||||
}
|
||||
if (passwordConfig.requireDigit && !hasDigit) {
|
||||
if (systemConfig.passwordConfig.requireDigit && !hasDigit) {
|
||||
callback(new Error('密码必须包含数字!'))
|
||||
return
|
||||
}
|
||||
if (passwordConfig.requireSpecialChar && !hasSpecialChar) {
|
||||
if (systemConfig.passwordConfig.requireSpecialChar && !hasSpecialChar) {
|
||||
callback(new Error('密码必须包含特殊字符!'))
|
||||
return
|
||||
}
|
||||
// 3. 检查是否包含弱密码
|
||||
for (const weakPwd of passwordConfig.weakPasswords) {
|
||||
if (value.includes(weakPwd)) {
|
||||
for (const weakPwd of systemConfig.passwordConfig.weakPasswords) {
|
||||
// 将密码和弱密码都转换为小写进行比较
|
||||
if (value.toLowerCase().includes(weakPwd.toLowerCase())) {
|
||||
callback(new Error(`密码包含常见的弱密码片段: ${weakPwd}`))
|
||||
return
|
||||
}
|
||||
}
|
||||
// 4. 检查是否包含超过规定数量的连续字符
|
||||
if (passwordConfig.restrictConsecutiveChars && containsConsecutiveCharacters(value, passwordConfig.maxConsecutiveChars)) {
|
||||
callback(new Error(`密码不能包含超过${passwordConfig.maxConsecutiveChars}位连续字符!`))
|
||||
if (systemConfig.passwordConfig.restrictConsecutiveChars && containsConsecutiveCharacters(value, systemConfig.passwordConfig.maxConsecutiveChars)) {
|
||||
callback(new Error(`密码不能包含超过${systemConfig.passwordConfig.maxConsecutiveChars}位连续字符!`))
|
||||
return
|
||||
}
|
||||
callback() // 验证成功
|
||||
}
|
||||
|
||||
function containsConsecutiveCharacters(password, maxConsecutive) {
|
||||
let count = 1
|
||||
for (let i = 1; i < password.length; i++) {
|
||||
if (password[i] === password[i - 1]) {
|
||||
count++
|
||||
if (count > maxConsecutive) return true
|
||||
} else {
|
||||
count = 1
|
||||
/**
|
||||
* 检查密码中是否包含超过 n 个连续相同字符、连续递增/递减的数字或字母(不区分大小写)
|
||||
*/
|
||||
function containsConsecutiveCharacters(password, n) {
|
||||
// 检查连续相同字符
|
||||
n = n + 1; // 允许最多 n 个连续字符
|
||||
for (let i = 0; i <= password.length - n; i++) {
|
||||
let consecutiveSameChar = true;
|
||||
for (let j = 1; j < n; j++) {
|
||||
if (password[i + j] !== password[i]) {
|
||||
consecutiveSameChar = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (consecutiveSameChar) {
|
||||
return true; // 包含超过 n 个连续相同字符
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
// 检查连续递增或递减的数字
|
||||
for (let i = 0; i <= password.length - n; i++) {
|
||||
let consecutiveIncreasing = true;
|
||||
let consecutiveDecreasing = true;
|
||||
for (let j = 1; j < n; j++) {
|
||||
const currentChar = password[i];
|
||||
const nextChar = password[i + j];
|
||||
|
||||
// 检查数字递增或递减
|
||||
if (/\d/.test(currentChar) && /\d/.test(nextChar)) {
|
||||
if (nextChar.charCodeAt(0) !== currentChar.charCodeAt(0) + j) {
|
||||
consecutiveIncreasing = false;
|
||||
}
|
||||
if (nextChar.charCodeAt(0) !== currentChar.charCodeAt(0) - j) {
|
||||
consecutiveDecreasing = false;
|
||||
}
|
||||
} else {
|
||||
consecutiveIncreasing = false;
|
||||
consecutiveDecreasing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (consecutiveIncreasing || consecutiveDecreasing) {
|
||||
return true; // 包含超过 n 个递增或递减的连续数字
|
||||
}
|
||||
}
|
||||
|
||||
// 检查连续递增或递减的字母(不区分大小写)
|
||||
for (let i = 0; i <= password.length - n; i++) {
|
||||
let consecutiveIncreasing = true;
|
||||
let consecutiveDecreasing = true;
|
||||
for (let j = 1; j < n; j++) {
|
||||
const currentChar = password[i].toLowerCase(); // 转为小写
|
||||
const nextChar = password[i + j].toLowerCase(); // 转为小写
|
||||
|
||||
// 检查字母递增或递减
|
||||
if (/[a-zA-Z]/.test(currentChar) && /[a-zA-Z]/.test(nextChar)) {
|
||||
if (nextChar.charCodeAt(0) !== currentChar.charCodeAt(0) + j) {
|
||||
consecutiveIncreasing = false;
|
||||
}
|
||||
if (nextChar.charCodeAt(0) !== currentChar.charCodeAt(0) - j) {
|
||||
consecutiveDecreasing = false;
|
||||
}
|
||||
} else {
|
||||
consecutiveIncreasing = false;
|
||||
consecutiveDecreasing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (consecutiveIncreasing || consecutiveDecreasing) {
|
||||
return true; // 包含超过 n 个递增或递减的连续字母
|
||||
}
|
||||
}
|
||||
|
||||
// 不包含连续相同字符、数字或字母序列
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -272,7 +272,9 @@ export default {
|
|||
handelCloseSub(){
|
||||
this.showProjSec = true
|
||||
this.showLefSec = false
|
||||
this.renderAllProj()
|
||||
setTimeout(()=>{
|
||||
this.getProjData()
|
||||
},500)
|
||||
},
|
||||
validForbid(value) { value = value.replace(/[`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、]/g, '').replace(/\s/g, ""); return value; }
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ export default {
|
|||
devNum: item.devNum,
|
||||
successNum: item.successNum,
|
||||
errorNum: item.errorNum,
|
||||
devStatus: item.devStatus,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: '#7DDEFF'
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ export default {
|
|||
devNum: item.devNum,
|
||||
successNum: item.successNum,
|
||||
errorNum: item.errorNum,
|
||||
devStatus: item.devStatus,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: '#7DDEFF',
|
||||
|
|
|
|||
Loading…
Reference in New Issue