当前位置: 首页 > news >正文

Java 后端接口入参 - 联合前端VUE 使用AES完成入参出参加密解密

加密效果:

解密后的数据就是正常数据:

后端:使用的是spring-cloud框架,在gateway模块进行操作

        <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.0-jre</version></dependency>

编写一个AES加密的utils

package com.medical.gateway.utils;import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;/*** AES加解密工具类* Rui*/
public class   AESUtil {private static final String ENCODE_RULES = "mjfeng";/*** 加密* 1.构造密钥生成器* 2.根据ecnodeRules规则初始化密钥生成器* 3.产生密钥* 4.创建和初始化密码器* 5.内容加密* 6.返回字符串*/public static String aesEncode(String content) {try {//1.构造密钥生成器,指定为AES算法,不区分大小写KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");//2.根据ecnodeRules规则初始化密钥生成器//生成一个128位的随机源,根据传入的字节数组SecureRandom random = SecureRandom.getInstance("SHA1PRNG");random.setSeed(ENCODE_RULES.getBytes());keyGenerator.init(128, random);//3.产生原始对称密钥SecretKey originalKey = keyGenerator.generateKey();//4.获得原始对称密钥的字节数组byte[] raw = originalKey.getEncoded();//5.根据字节数组生成AES密钥SecretKey key = new SecretKeySpec(raw, "AES");//6.根据指定算法AES自成密码器Cipher cipher = Cipher.getInstance("AES");//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEYcipher.init(Cipher.ENCRYPT_MODE, key);//8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码byte[] byteEncode = content.getBytes("utf-8");//9.根据密码器的初始化方式--加密:将数据加密byte[] byteAES = cipher.doFinal(byteEncode);//10.将加密后的数据转换为字符串//这里用Base64Encoder中会找不到包//解决办法://在项目的Build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。String aesEncode = new String(Base64.getEncoder().encode(byteAES));//11.将字符串返回return aesEncode;} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}//如果有错就返加nulllreturn null;}/*** 解密* 解密过程:* 1.同加密1-4步* 2.将加密后的字符串反纺成byte[]数组* 3.将加密内容解密*/public static String aesDecode(String content) {try {//1.构造密钥生成器,指定为AES算法,不区分大小写KeyGenerator keygen = KeyGenerator.getInstance("AES");//2.根据ecnodeRules规则初始化密钥生成器//生成一个128位的随机源,根据传入的字节数组SecureRandom random = SecureRandom.getInstance("SHA1PRNG");random.setSeed(ENCODE_RULES.getBytes());keygen.init(128, random);//3.产生原始对称密钥SecretKey originalKey = keygen.generateKey();//4.获得原始对称密钥的字节数组byte[] raw = originalKey.getEncoded();//5.根据字节数组生成AES密钥SecretKey key = new SecretKeySpec(raw, "AES");//6.根据指定算法AES自成密码器Cipher cipher = Cipher.getInstance("AES");//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEYcipher.init(Cipher.DECRYPT_MODE, key);//8.将加密并编码后的内容解码成字节数组byte[] byteContent = Base64.getDecoder().decode(content);/** 解密*/byte[] byteDecode = cipher.doFinal(byteContent);String aesDecode = new String(byteDecode, "utf-8");return aesDecode;} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();}//如果有错就返加nulllreturn null;}/*** 解密* 解密过程:* 1.同加密1-4步* 2.将加密后的字符串反纺成byte[]数组* 3.将加密内容解密*/public static String aesDecodeByKey(String content, String aesKey) {try {byte[] byteContent = Base64.getDecoder().decode(content);SecretKeySpec secretKeySpec = new SecretKeySpec(aesKey.getBytes(), "AES");Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);/** 解密*/byte[] byteDecode = cipher.doFinal(byteContent);String aesDecode = new String(byteDecode, "utf-8");return aesDecode;} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();}//如果有错就返加nulllreturn null;}/*** 加密* 1.构造密钥生成器* 2.根据ecnodeRules规则初始化密钥生成器* 3.产生密钥* 4.创建和初始化密码器* 5.内容加密* 6.返回字符串*/public static String aesEncodeByKey(String content, String aesKey) {try {SecretKeySpec secretKeySpec = new SecretKeySpec(aesKey.getBytes(), "AES");Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);//8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码byte[] byteEncode = content.getBytes("utf-8");//9.根据密码器的初始化方式--加密:将数据加密byte[] byteAES = cipher.doFinal(byteEncode);//10.将加密后的数据转换为字符串//这里用Base64Encoder中会找不到包//解决办法://在项目的Build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。String aesEncode = new String(Base64.getEncoder().encode(byteAES));//11.将字符串返回return aesEncode;} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}//如果有错就返加nulllreturn null;}}

需要AuthFilter,过滤器中加上相关方法,在这个方法的下面开始加

 private ServerHttpRequest getServerHttpRequest(ServerHttpRequest request) {String bodyStr = resolveBodyFromRequest(request);System.out.println("PUT OR POST bodyStr: " + bodyStr);String decode = "";try {if(StringUtils.isEmpty(bodyStr)){return request;}decode = AESUtil.aesDecodeByKey(bodyStr, "MP5v0^zee5Qlgq5V");//解密方法可以自由替换,这里使用AES为例。} catch (Exception e) {e.printStackTrace();}URI uri = request.getURI();DataBuffer bodyDataBuffer = stringBuffer(decode);Flux<DataBuffer> bodyFlux1 = Flux.just(bodyDataBuffer);request = new ServerHttpRequestDecorator(request) {@Overridepublic Flux<DataBuffer> getBody() {return bodyFlux1;}};return request;}private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {//获取请求体Flux<DataBuffer> body = serverHttpRequest.getBody();AtomicReference<String> bodyRef = new AtomicReference<>();body.subscribe(buffer -> {CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());DataBufferUtils.release(buffer);bodyRef.set(charBuffer.toString());});//获取request bodyreturn bodyRef.get();}private DataBuffer stringBuffer(String value) {
//        this.value = value;byte[] bytes = value.getBytes(StandardCharsets.UTF_8);NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);buffer.write(bytes);return buffer;}

然后需要加另一个类ParamsEncryptionFilter

package com.medical.gateway.filter;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;@Component
public class ParamsEncryptionFilter implements GlobalFilter, Ordered {private static final Logger log = LoggerFactory.getLogger(ParamsEncryptionFilter.class);@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {/*** save request path and serviceId into gateway context*/ServerHttpRequest request = exchange.getRequest();HttpHeaders headers = request.getHeaders();// 处理参数MediaType contentType = headers.getContentType();long contentLength = headers.getContentLength();if (contentLength > 0) {if (MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType)) {readBody(exchange, chain);return readBody(exchange, chain);}}return chain.filter(exchange);}/*** default HttpMessageReader*/private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();/*** ReadJsonBody** @param exchange* @param chain* @return*/private Mono<Void> readBody(ServerWebExchange exchange, GatewayFilterChain chain) {/*** join the body*/return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {byte[] bytes = new byte[dataBuffer.readableByteCount()];dataBuffer.read(bytes);DataBufferUtils.release(dataBuffer);Flux<DataBuffer> cachedFlux = Flux.defer(() -> {DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);DataBufferUtils.retain(buffer);return Mono.just(buffer);});/*** repackage ServerHttpRequest*/ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {@Overridepublic Flux<DataBuffer> getBody() {return cachedFlux;}};/*** mutate exchage with new ServerHttpRequest*/ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();/*** read body string with default messageReaders*/return ServerRequest.create(mutatedExchange, messageReaders).bodyToMono(String.class).doOnNext(objectValue -> {log.debug("[GatewayContext]Read JsonBody:{}", objectValue);}).then(chain.filter(mutatedExchange));});}@Overridepublic int getOrder() {return HIGHEST_PRECEDENCE;}}

还有一个类也需要加入ResponseFilter

package com.medical.gateway.filter;import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
import com.google.common.base.Joiner;
import com.google.common.base.Throwables;import com.medical.gateway.config.properties.IgnoreWhiteProperties;
import com.medical.gateway.utils.AESUtil;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.List;import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR;@Component
public class ResponseFilter implements GlobalFilter, Ordered {private static final Logger log = LoggerFactory.getLogger(ResponseFilter.class);private static Joiner joiner = Joiner.on("");// 排除过滤的 uri 地址,nacos自行添加@Autowiredprivate IgnoreWhiteProperties ignoreWhite;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();ServerHttpRequest.Builder mutate = request.mutate();String url = request.getURI().getPath();String methodValue = request.getMethodValue();ServerHttpResponse originalResponse = exchange.getResponse();DataBufferFactory bufferFactory = originalResponse.bufferFactory();log.info("进入响应拦截");ServerHttpResponse response = exchange.getResponse();DataBufferFactory dataBufferFactory = response.bufferFactory();ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {@Overridepublic Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {if (getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) {// 获取ContentType,判断是否返回JSON格式数据String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);if (StringUtils.isNotBlank(originalResponseContentType) && originalResponseContentType.contains("application/json")) {Flux<? extends DataBuffer> fluxBody = Flux.from(body);//(返回数据内如果字符串过大,默认会切割)解决返回体分段传输return super.writeWith(fluxBody.buffer().map(dataBuffers -> {List<String> list = Lists.newArrayList();dataBuffers.forEach(dataBuffer -> {try {byte[] content = new byte[dataBuffer.readableByteCount()];dataBuffer.read(content);DataBufferUtils.release(dataBuffer);list.add(new String(content, "utf-8"));} catch (Exception e) {log.info("加载Response字节流异常,失败原因:{}", Throwables.getStackTraceAsString(e));}});String responseData = joiner.join(list);System.out.println("responseData:"+responseData);String s = AESUtil.aesEncodeByKey(responseData, "MP5v0^zee5Qlgq5V");s =  s.replaceAll("\r\n", "").replaceAll("\n","");byte[] uppedContent = new String(s.getBytes(), Charset.forName("UTF-8")).getBytes();originalResponse.getHeaders().setContentLength(uppedContent.length);return bufferFactory.wrap(uppedContent);}));}}return super.writeWith(body);}};return chain.filter(exchange.mutate().response(decoratedResponse).build());}@Overridepublic int getOrder() {return -2;}private String Bytes2String(byte[] bytes){Charset cs = Charset.forName("UTF-8");ByteBuffer bb = ByteBuffer.allocate(bytes.length);bb.put(bytes).flip();CharBuffer cb = cs.decode(bb);String res = new String(cb.array());return res;}
}

到这里后端代码已经基本完成,下面开始操作前段代码

在utils包下面编写一个AES.js,用于AES加密和解密

    import CryptoJS from "crypto-js";export default {// 加密encrypt(word, keyStr) {keyStr = keyStr ? keyStr : "MP5v0^zee5Qlgq5V";let key = CryptoJS.enc.Utf8.parse(keyStr);let srcs = CryptoJS.enc.Utf8.parse(word);let encrypted = CryptoJS.AES.encrypt(srcs, key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});return encrypted.toString();},// 解密decrypt(word, keyStr) {keyStr = keyStr ? keyStr : "MP5v0^zee5Qlgq5V";var key = CryptoJS.enc.Utf8.parse(keyStr);var decrypt = CryptoJS.AES.decrypt(word, key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});return CryptoJS.enc.Utf8.stringify(decrypt).toString();}
};

下面需要注意,我使用的是ruoyi的前段框架,在这里我不能直接将request.js直接替换掉原有的request.js,我是直接手写需要的代码,但是我会给request.js文件放在下面

request.js 仅供参考

import axios from 'axios'
import { Notification, MessageBox, Message, Loading } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from "@/utils/shop";
import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'
import AES from '@/utils/AES'let downloadLoadingInstance;
// 是否显示重新登录
export let isRelogin = { show: false };axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({// axios中请求配置有baseURL选项,表示请求URL公共部分baseURL: process.env.VUE_APP_BASE_API,// 超时timeout: 10000
})// request拦截器
service.interceptors.request.use(config => {// 是否需要设置 tokenconst isToken = (config.headers || {}).isToken === false// 是否需要防止数据重复提交const isRepeatSubmit = (config.headers || {}).repeatSubmit === falseif (getToken() && !isToken) {config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改}// get请求映射params参数if (config.method === 'get' && config.params) {let url = config.url + '?' + tansParams(config.params);url = url.slice(0, -1);config.params = {};config.url = url;}if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {const requestObj = {url: config.url,data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,time: new Date().getTime()}const requestSize = Object.keys(JSON.stringify(requestObj)).length; // 请求数据大小const limitSize = 5 * 1024 * 1024; // 限制存放数据5Mif (requestSize >= limitSize) {console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。')return config;}const sessionObj = cache.session.getJSON('sessionObj')config.data = AES.encrypt(JSON.stringify(config.data))if (sessionObj === undefined || sessionObj === null || sessionObj === '') {cache.session.setJSON('sessionObj', requestObj)} else {const s_url = sessionObj.url;                  // 请求地址const s_data = sessionObj.data;                // 请求数据const s_time = sessionObj.time;                // 请求时间const interval = 1000;                         // 间隔时间(ms),小于此时间视为重复提交if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {const message = '数据正在处理,请勿重复提交';console.warn(`[${s_url}]: ` + message)return Promise.reject(new Error(message))} else {cache.session.setJSON('sessionObj', requestObj)}}}return config
}, error => {console.log(error)Promise.reject(error)
})// 响应拦截器
service.interceptors.response.use(res => {// 未设置状态码则默认成功状态const code = res.data.code || 200;// 获取错误信息const msg = errorCode[code] || res.data.msg || errorCode['default']// 二进制数据则直接返回if (res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer') {return res.data}if (code === 401) {if (!isRelogin.show) {isRelogin.show = true;MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {isRelogin.show = false;store.dispatch('LogOut').then(() => {location.href = '/index';})}).catch(() => {isRelogin.show = false;});}return Promise.reject('无效的会话,或者会话已过期,请重新登录。')} else if (code === 500) {Message({ message: msg, type: 'error' })return Promise.reject(new Error(msg))} else if (code === 601) {Message({ message: msg, type: 'warning' })return Promise.reject('error')} else if (code !== 200) {Notification.error({ title: msg })return Promise.reject('error')} else {if ( res.request.responseURL.indexOf('code') == -1 || res.config.method === 'post' || res.config.method=== 'put'  || res.config.method=== 'delete') {let decrypt = AES.decrypt(res.data);console.log(decrypt)return JSON.parse(decrypt)}return res.data}},error => {console.log('err' + error)let { message } = error;if (message == "Network Error") {message = "后端接口连接异常";} else if (message.includes("timeout")) {message = "系统接口请求超时";} else if (message.includes("Request failed with status code")) {message = "系统接口" + message.substr(message.length - 3) + "异常";}Message({ message: message, type: 'error', duration: 5 * 1000 })return Promise.reject(error)}
)// 通用下载方法
export function download(url, params, filename, config) {downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })return service.post(url, params, {transformRequest: [(params) => { return tansParams(params) }],headers: { 'Content-Type': 'application/x-www-form-urlencoded' },responseType: 'blob',...config}).then(async (data) => {const isBlob = blobValidate(data);if (isBlob) {const blob = new Blob([data])saveAs(blob, filename)} else {const resText = await data.text();const rspObj = JSON.parse(resText);const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']Message.error(errMsg);}downloadLoadingInstance.close();}).catch((r) => {console.error(r)Message.error('下载文件出现错误,请联系管理员!')downloadLoadingInstance.close();})
}export default service

主要编写的地方是--->

引入AES

在这个地方进行加密操作

在这个地方进行解密操作

我的代码放在这里可以和上面的request.js对比

import axios from 'axios'
import { Notification, MessageBox, Message, Loading } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from "@/utils/ruoyi";
import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'
import AES from '@/utils/AES'let downloadLoadingInstance;
// 是否显示重新登录
export let isRelogin = { show: false };axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({// axios中请求配置有baseURL选项,表示请求URL公共部分baseURL: process.env.VUE_APP_BASE_API,// 超时timeout: 10000
})// request拦截器
service.interceptors.request.use(config => {// 是否需要设置 tokenconst isToken = (config.headers || {}).isToken === false// 是否需要防止数据重复提交const isRepeatSubmit = (config.headers || {}).repeatSubmit === falseif (getToken() && !isToken) {config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改}// get请求映射params参数if (config.method === 'get' && config.params) {let url = config.url + '?' + tansParams(config.params);url = url.slice(0, -1);config.params = {};config.url = url;}if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {const requestObj = {url: config.url,data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,time: new Date().getTime()}const requestSize = Object.keys(JSON.stringify(requestObj)).length; // 请求数据大小const limitSize = 5 * 1024 * 1024; // 限制存放数据5Mif (requestSize >= limitSize) {console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。')return config;}const sessionObj = cache.session.getJSON('sessionObj')config.data = AES.encrypt(JSON.stringify(config.data))if (sessionObj === undefined || sessionObj === null || sessionObj === '') {cache.session.setJSON('sessionObj', requestObj)} else {const s_url = sessionObj.url;                  // 请求地址const s_data = sessionObj.data;                // 请求数据const s_time = sessionObj.time;                // 请求时间const interval = 1000;                         // 间隔时间(ms),小于此时间视为重复提交if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {const message = '数据正在处理,请勿重复提交';console.warn(`[${s_url}]: ` + message)return Promise.reject(new Error(message))} else {cache.session.setJSON('sessionObj', requestObj)}}}return config
}, error => {console.log(error)Promise.reject(error)
})// 响应拦截器
service.interceptors.response.use(res => {// 未设置状态码则默认成功状态const code = res.data.code || 200;// 获取错误信息const msg = errorCode[code] || res.data.msg || errorCode['default']// 二进制数据则直接返回if (res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer') {return res.data}if (code === 401) {if (!isRelogin.show) {isRelogin.show = true;MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {isRelogin.show = false;store.dispatch('LogOut').then(() => {location.href = '/index';})}).catch(() => {isRelogin.show = false;});}return Promise.reject('无效的会话,或者会话已过期,请重新登录。')} else if (code === 500) {Message({ message: msg, type: 'error' })return Promise.reject(new Error(msg))} else if (code === 601) {Message({ message: msg, type: 'warning' })return Promise.reject('error')} else if (code !== 200) {Notification.error({ title: msg })return Promise.reject('error')} else {if ( res.request.responseURL.indexOf('code') == -1 || res.config.method === 'post' || res.config.method=== 'put'  || res.config.method=== 'delete') {let decrypt = AES.decrypt(res.data);console.log(decrypt)return JSON.parse(decrypt)}return res.data}},error => {console.log('err' + error)let { message } = error;if (message == "Network Error") {message = "后端接口连接异常";} else if (message.includes("timeout")) {message = "系统接口请求超时";} else if (message.includes("Request failed with status code")) {message = "系统接口" + message.substr(message.length - 3) + "异常";}Message({ message: message, type: 'error', duration: 5 * 1000 })return Promise.reject(error)}
)// 通用下载方法
export function download(url, params, filename, config) {downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })return service.post(url, params, {transformRequest: [(params) => { return tansParams(params) }],headers: { 'Content-Type': 'application/x-www-form-urlencoded' },responseType: 'blob',...config}).then(async (data) => {const isBlob = blobValidate(data);if (isBlob) {const blob = new Blob([data])saveAs(blob, filename)} else {const resText = await data.text();const rspObj = JSON.parse(resText);const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']Message.error(errMsg);}downloadLoadingInstance.close();}).catch((r) => {console.error(r)Message.error('下载文件出现错误,请联系管理员!')downloadLoadingInstance.close();})
}export default service

相关文章:

Java 后端接口入参 - 联合前端VUE 使用AES完成入参出参加密解密

加密效果&#xff1a; 解密后的数据就是正常数据&#xff1a; 后端&#xff1a;使用的是spring-cloud框架&#xff0c;在gateway模块进行操作 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30…...

HarmonyOS开发之使用PhotoViewPicker(图库选择器)保存图片

一&#xff1a;效果图 二&#xff1a;添加依赖 import fs from ohos.file.fs;//文件管理 import picker from ohos.file.picker//选择器 三&#xff1a;下载&#xff0c;保存图片的实现 // 下载图片imgUrldownloadAndSaveImage(imgUrl: string) {http.createHttp().request(…...

跨境独立站支付收款常见问题排雷篇1.0丨出海笔记

最近小伙伴们在社群讨论挺多关于独立站支付问题的&#xff0c;鉴于不少朋友刚接触独立站&#xff0c;我整理了一些独立站支付相关的问题和解决方案&#xff0c;供大家参考&#xff0c;百度网上一堆媒体的那些软文大家就别看了&#xff0c;都是软广或者抄来抄去&#xff0c;让大…...

uni-app实现web-view和App之间的相互通信

双向实时 如果app端部署成网站&#xff0c;则web-view就是iframe&#xff0c;使用也可以双向通讯 https://uniapp.dcloud.net.cn/component/web-view.html APP端代码 index.vue: <template><web-viewid"m-webview":fullscreen"true":src"…...

HTB-Vaccine(suid提权、sqlmap、john2zip)

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天来为大家讲解Vaccine靶机 渗透过程 信息搜集 服务器开放了 21FTP服务、22SSH服务、80HTTP服务 通过匿名登录FTP服务器 通过匿名登录到服务器&#xff0c;发现backup.zip文件&#xff0c;可能存在账号密码 发现b…...

【达梦数据库】异构数据库迁移到达梦

目录 1、迁移准备2、正式迁移3、问题处理3.1、return附近出现错误3.1.1、排查过程3.1.2、问题原因3.1.2、解决方法 3.2、对象[XXX]处于无效状态-类型13.2.1、排查过程3.2.2、问题原因3.2.3、解决方法 3.3、对象[XXX]处于无效状态-类型23.3.1、排查过程3.3.2、问题原因3.3.3、解…...

抽象类和接口(1)

抽象类&#xff1a; 什么是抽象类&#xff1a; 听着就很抽象&#xff0c;确实挺抽象&#xff0c;先来写一个抽象类感觉一下&#xff1a; 这就是抽象类&#xff01; 在 Java 中&#xff0c;一个类如果被 abstract 修饰称为抽象类&#xff0c;抽象类中被 abstract 修饰的方法…...

epoll内核原理与实现详解

目录 1 epoll相关理论基础 1.1 I/O多路复用技术 1.2 事件驱动模型 1.2.1 基本概念 1.2.2 优缺点分析 1.2.3 与epoll的关联 1.3 epoll机制简介 1.3.1 核心原理 1.3.2 优点 2 epoll内核原理 2.1 epoll数据结构 2.1.1 主要数据结构 2.1.2 数据结构关系 2.2 epoll工作…...

被低估的SQL

SQL是现代数据库管理系统中不可或缺的一部分。尽管它的使用已十分普遍&#xff0c;但在数据处理领域&#xff0c;SQL的某些功能和潜力仍然被许多人低估。接下来&#xff0c;小编将与您一起&#xff0c;探讨SQL的一些被忽视的特性&#xff0c;揭示它在数据管理中的真正实力。 1.…...

数字证书、数字签名及其关系

一.数字证书与数字签名 1.数字证书是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。简单地说&#xff0c;数字证书是一段包含用户身份信息、用户公钥信息以及份验证机构数字签名的数据。 通俗理解&#xff1a;数字证书相当于【身份证】 —— 确认你…...

一文读懂:如何将广告融入大型语言模型(LLM)输出

本文是我翻译过来的&#xff0c;讨论了在线广告行业的现状以及如何将大型语言模型&#xff08;LLM&#xff09;应用于在线广告。 原文请参见”阅读原文“。 在2024年&#xff0c;预计全球媒体广告支出的69%将流向数字广告市场。这个数字预计到2029年将增长到79%。在Meta的2024…...

godotenv拜读

简介 应用提倡将配置存储在环境变量中。任何从开发环境切换到生产环境时需要修改的东西都从代码抽取到环境变量里。 但是在实际开发中&#xff0c;如果同一台机器运行多个项目&#xff0c;设置环境变量容易冲突&#xff0c;不实用。godotenv库从.env文件中读取配置&#xff0c;…...

解析REST API与OpenAPI之差异:避免混淆

在网络API领域&#xff0c;常提及的两种术语为Rest API与Open API&#xff0c;其既存在差异亦存在联系。前者是一种API设计方式&#xff0c;后者则是一种API描述及定义规范。值得注意的是&#xff0c;OpenAPI 可用于描述和定义REST API。 什么是REST API&#xff1f; REST API …...

一篇文章就搞懂了:过虑器 、拦截器 、监听器是什么

java 过虑器 、拦截器 、监听器的区别? ‌实现原理‌&#xff1a; 过滤器基于函数回调实现。拦截器基于Java的反射机制实现。监听器用于监听特定事件的发生&#xff0c;并作出相应处理。 ‌使用范围‌&#xff1a; 过滤器依赖于Tomcat等容器&#xff0c;主要用于Web程序。拦截…...

本体映射与本体集成

文章目录 本体映射与本体集成本体映射分类知识挖掘是从己有的实体及实体关系出发挖掘新的知识,具体包括知识内容挖掘和知识结构挖掘。 本体映射与本体集成 解决本体异构的通用方法是本体集成与本体映射。本体集成直接将多个本体合并为一个大本体,本体映射则寻找本体间的映射…...

华媒舍:10种提升推特大V发文推广曝光率的方式

在社交媒体时代&#xff0c;推特已成为许多大V达到更广泛受众的重要渠道。并非所有的推文都能获得理想的曝光率。为了帮助大V们提升推文的曝光率&#xff0c;本文将介绍10种有效的方式。 1. 精心构思内容 好的内容是吸引读者和提升曝光率的关键。大V们应该从听众的角度出发&am…...

前端本地存储数据:深入解析与代码示例(Cookie、LocalStorage、SessionStorage和IndexedDB)

在现代Web应用中&#xff0c;前端本地存储是实现用户个性化体验的关键技术。本文将深入探讨前端本地存储的四种主要技术&#xff1a;Cookie、LocalStorage、SessionStorage和IndexedDB&#xff0c;并提供具体的代码示例。 Cookie 简介 Cookie是由服务器创建并存储在用户浏览…...

Java语言程序设计基础篇_编程练习题*18.21 (将十进制数转换为二进制数)

*18.21 (将十进制数转换为二进制数) 编写一个递归方法&#xff0c;将一个十进制数转换为一个二进制数的字符串。方法头如下: public static String dec2Bin(int value)编写一个测试程序&#xff0c;提示用户输入一个十进制数&#xff0c;然后显示等价的二进制数。 代码示例 …...

中年转行新可能:18 个月迈向大模型提示词工程师

【导读】 人到中年&#xff0c;想半路转行成为大模型提示词工程师&#xff0c;这可行吗&#xff1f;最近&#xff0c;一位国外小哥成功转行&#xff0c;他在一篇干货满满的硬核博客中给出了答案&#xff1a;完全可行&#xff01; 转行成为大模型提示词工程师&#xff0c;到底…...

C++通过返回值和输出参数的原理是什么?分别有什么优势和缺点?

C中&#xff0c;通过返回值和输出参数&#xff08;通常是通过引用或指针&#xff09;是函数与外部世界交换数据的两种主要方式。它们各自有着不同的原理和优缺点。 通过返回值 原理&#xff1a; 当函数通过返回值向调用者传递数据时&#xff0c;它实际上是在函数执行完毕后&…...

AI客服机器人开启企业客户服务新纪元

随着人工智能(AI)技术的迅猛发展&#xff0c;使得AI客服机器人走进了我们的视野&#xff0c;成为提高客户满意度和业务效率的不二法宝。这些智能机器人不仅能够处理海量信息&#xff0c;还能为客户提供个性化的服务体验。 一、AI客服机器人的基本原理 AI客服机器人是基于人工智…...

TPM项目课题的确定需要考虑哪些因素?

确定一个合适的TPM项目课题&#xff0c;是企业启动并成功实施TPM计划的第一步。这一过程不仅需要深入洞察企业现状&#xff0c;还需前瞻性地规划未来发展。详情如深圳天行健精益化生产管理咨询公司下文所述&#xff1a; 一、企业战略目标 1.与企业长期发展规划的契合度 -TPM项…...

Rust 数据类型

文章目录 发现宝藏1. 标量类型1.1 整型1.2 浮点型1.3 布尔型1.4 字符型 2. 复合类型2.1 元组2.2 数组 3. 总结 发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【宝藏入口】。 Rust 是一种静态…...

C#无标题栏窗体拖动

要实现C#无标题栏窗体的拖动功能&#xff0c;可以通过以下步骤实现&#xff1a; 在窗体的构造函数中添加以下代码&#xff0c;以去掉标题栏&#xff1a; this.FormBorderStyle FormBorderStyle.None;然后&#xff0c;添加以下代码以处理鼠标按下事件&#xff1a; private c…...

MySQL容器配置连接数数,镜像重启生效

若有不理解&#xff0c;可以问一下这几个免费的AI网站 https://ai-to.cn/chathttp://m6z.cn/6arKdNhttp://m6z.cn/6b1quhhttp://m6z.cn/6wVAQGhttp://m6z.cn/63vlPw 方法一 在 docker-compose.yml 中配置 MySQL 的连接数&#xff0c;可以通过环境变量或配置文件来实现。以下…...

《OpenCV计算机视觉》—— 身份证号码识别案例

文章目录 一、案例实现的整体思路二、代码实现1.首先定义两个函数2.模板图像中数字的定位处理3.身份证号码数字的定位处理4.使用模板匹配&#xff0c;计算匹配得分&#xff0c;找到正确结果 一、案例实现的整体思路 下面是一个数字0~9的模板图片 案例身份证如下&#xff1a; 对…...

如何使用正则表达式替换字符串中的特定位置数字

如何使用正则表达式替换字符串中的特定位置数字 1、效果 把字符串中的第一个123替换掉: 2、代码 使用正则中的sub函数: re.sub(pattern,repl,string,count=0,flags=0) pattern:表示需要匹配的模式,即需要被替换的字符或字符串。 repl:表示替换后的字符串或函数,用于…...

【SQL】在SQL中,行转列

在SQL中&#xff0c;行转列通常是指将数据从水平方向&#xff08;行&#xff09;转换为垂直方向&#xff08;列&#xff09;&#xff0c;这可以通过使用CASE语句或数据库特有的函数如PIVOT&#xff08;在SQL Server中&#xff09;来实现。下面我将通过一个具体的例子来说明如何…...

95. UE5 GAS RPG 实现创建多段飞弹攻击敌人

从这篇开始&#xff0c;我们将实现一些技能&#xff0c;比如多段火球术&#xff0c;闪电链等等。 在这一篇里&#xff0c;我们先实现多段火球术&#xff0c;技能可以通过配置发射出多个火球术进行攻击。 创建多段火球函数 首先在我们之前创建的RPGFireBolt.h类里面增加一个生…...

分布式集群下如何做到唯一序列号

优质博文&#xff1a;IT-BLOG-CN 分布式架构下&#xff0c;生成唯一序列号是设计系统常常会遇到的一个问题。例如&#xff0c;数据库使用分库分表的时候&#xff0c;当分成若干个sharding表后&#xff0c;如何能够快速拿到一个唯一序列号&#xff0c;是经常遇到的问题。实现思…...