首页 > 其他分享 >Springboot+Vue加密通信

Springboot+Vue加密通信

时间:2024-07-03 17:23:23浏览次数:14  
标签:Vue 加密 Springboot java return import public String

前言

  本文旨在给出Springboot+Vue 框架下的加密通信具体实现,同时为照顾非行业内/初学读者,第一小节浅显的解释下加解密方式,老鸟直接跳过。

1 加解密方式

  常见的加解密方式大概分成对称加密、非对称加密与信息摘要算法三类。下面仅从使用角度简单介绍下加解密方式:

1.1 对称加密

  采用单钥密码的加密方法,同一个密钥可以同时用来加密和解密,这种加密方法称为对称加密,也称为单密钥加密。加解密过程如下:

1.2 非对称加密

  非对称加密是一种使用公钥和私钥对的加密方式,可以安全地公开公钥。通俗点理解,区别于对称加密的单密钥,非对称加密使用公钥-私钥密钥对,公钥仅用于加密,私钥解密。加解密过程如下:

 

1.3 信息摘要算法

  摘要算法又称哈希算法,它表示输入任意长度的数据,输出固定长度的数据,相同的输入数据始终得到相同的输出,不同的输入数据尽量得到不同的输出,主要用于校验数据的完整性。

2 Springboot+Vue RSA加密通信实现

  博主老抓娃,Spring全家桶用的飞起,短平快项目直接上ruoyi,但是碰上需要加密通信的场景,若依提供的加密通信方式还真是短平快,稍微太简单了点,故给他强化一下。

2.1 前后端加密通信过程

 

   流程上大致如图,页面请求公钥 >> 页面使用公钥进行加密 >> 提交请求 >> 服务端接收并使用私钥解密请求数据 >> 服务端执行业务逻辑 >> 服务端返回业务 >> 页面接收响应结果并处理。当然在这个流程里面还有很多可优化的点,比如:服务器端生成密钥对采用周期更新策略提高安全性;缓存密钥对提升性能;生成两组密钥对用于响应加密;针对客户端生成密钥对进一步提升安全性等等改进措施。这些改进点完全基于业务的需求度,并且需要充分考虑使用性能,越复杂的逻辑必然消耗越多的资源。话不多说,下面上代码,毕竟no code no bb。

2.2 实现代码

2.2.1 Vue前端代码

  • 添加依赖包,别问我为啥用这个,只要RSA加密方法支持数据分段加密都行,毕竟提交的内容长度不确定。
    "jsencrypt": "3.0.0-rc.1"
  • 获取公钥
// 增加api,必须能够匿名访问
import request from "@/utils/request";
// 获取公钥
export function getPublicKey() {
  return request({
    url: "/common/publicKey",
    method: "get",
  });
}


// 在合适的位置调用这个上面的getPublicKey方法,并保存publicKey, 毕竟不刷新页面就不用再次请求服务器获取公钥,比如我直接卸载App.vue的created()里面。
// 这个cache.local 你们就随意,只要存储到localstorage里面就行,后面要取
created() {
    // 查询公钥并更新
    getPublicKey().then((resp) => {
      this.$cache.local.set("publicKey", resp);
    });
  },
  • 创建jsencrypt.js
 1 import JSEncrypt from "jsencrypt/bin/jsencrypt.min";
 2 // 加密
 3 export function encrypt(txt, publicKey) {
 4   const encryptor = new JSEncrypt();
 5   encryptor.setPublicKey(publicKey); // 设置公钥
 6   return encryptor.encrypt(txt); // 对数据进行加密
 7 }
 8 
 9 // 解密
10 export function decrypt(txt, privateKey) {
11   const encryptor = new JSEncrypt();
12   encryptor.setPrivateKey(privateKey); // 设置私钥
13   return encryptor.decrypt(txt); // 对数据进行解密
14 }
  • 增加requset拦截器
// 这里只展示关键代码,至于怎么写具体根据项目自身情况,只要在请求提交之前处理即可

import axios from "axios";
import cache from "@/plugins/cache";
import { encrypt} from "@/utils/jsencrypt";

const service = axios.create({
  // axios中请求配置有baseURL选项,表示请求URL公共部分
  baseURL: process.env.VUE_APP_BASE_API,
  // 超时
  timeout: 10000,
});

// request拦截器
service.interceptors.request.use(
  (config) => {
    // .... 省略
    let requestBody = typeof config.data === "object"
        ? JSON.stringify(config.data)
        : config.data;
    // 选择加密,这里根据api的header标记来决定是否加密,当然也可以不要,看项目情况
    if (config.headers["encryption"] == true) {
      config.data = encrypt(requestBody, cache.local.get("publicKey"));
    }
    return config;
}, (error) => {
    // 省略...
});

需要加密的api方法,增加header["encryption"]=true 即可实现灵活配置。

2.2.2 后端代码

  • 增加Rsa加解密工具及辅助类
package xxxx;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA加密解密
 *
 * @author carliels
 **/
public class RsaUtils {

    private static final Logger logger = LoggerFactory.getLogger(RsaUtils.class);

    /*** RSA最大加密明文大小 */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /*** RSA最大解密密文大小 */
    private static final int MAX_DECRYPT_BLOCK = 128;

    private RsaUtils() {
    }

    /**
     * 公钥加密
     *
     * @param str
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static String encrypt(String str, String publicKey) throws Exception {
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
                .generatePublic(new X509EncodedKeySpec(decoded));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(1, pubKey);
        // 分段加密
        // URLEncoder编码解决中文乱码问题
        byte[] data = URLEncoder.encode(str, Constants.UTF8).getBytes(Constants.UTF8);
        // 加密时超过117字节就报错。为此采用分段加密的办法来加密
        byte[] enBytes = null;
        for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) {
            // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码
            byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK));
            enBytes = ArrayUtils.addAll(enBytes, doFinal);
        }
        String outStr = Base64.encodeBase64String(enBytes);
        return outStr;
    }

    /**
     * 私钥分段解密
     *
     * @param str
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String decrypt(String str, String privateKey) throws Exception {
        // 获取公钥
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA")
                .generatePrivate(new PKCS8EncodedKeySpec(decoded));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(2, priKey);
        byte[] data = Base64.decodeBase64(str.getBytes(Constants.UTF8));

        // 返回UTF-8编码的解密信息
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * 128;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return URLDecoder.decode(new String(decryptedData, Constants.UTF8), Constants.UTF8);
    }

    /**
     * 构建RSA密钥对
     *
     * @return 生成后的公私钥信息
     */
    public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        keyPairGen.initialize(1024, new SecureRandom());
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        String publicKeyString = Base64.encodeBase64String(publicKey.getEncoded());
        String privateKeyString = Base64.encodeBase64String(privateKey.getEncoded());
        RsaKeyPair rsaKeyPair = new RsaKeyPair();
        rsaKeyPair.setPrivateKey(privateKeyString);
        rsaKeyPair.setPublicKey(publicKeyString);
        return rsaKeyPair;
    }

}
RsaUtils.java
package xxx;

import org.apache.commons.lang3.builder.ToStringBuilder;

import java.io.Serializable;

/**
 * RSA 密钥对
 *
 * @author carliels
 */
public class RsaKeyPair implements Serializable {
    /**
     * 公钥
     */
    private String publicKey;
    /**
     * 私钥
     */
    private String privateKey;

    public RsaKeyPair() {
    }

    public RsaKeyPair(String publicKey, String privateKey) {
        this.publicKey = publicKey;
        this.privateKey = privateKey;
    }

    public String getPublicKey() {
        return publicKey;
    }

    public RsaKeyPair setPublicKey(String publicKey) {
        this.publicKey = publicKey;
        return this;
    }

    public String getPrivateKey() {
        return privateKey;
    }

    public RsaKeyPair setPrivateKey(String privateKey) {
        this.privateKey = privateKey;
        return this;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("publicKey", publicKey)
                .append("privateKey", privateKey)
                .toString();
    }
}
RsaKeyPair.java

 

  • 增加Rsa加密服务类,主要用于缓存密钥对
package xxx;

import xxx.Constants;
import xxx.RedisCache;
import xxx.RsaUtils;
import xxx.RsaKeyPair;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.concurrent.TimeUnit;

/**
 * 加密服务
 *
 * @author carliels
 */
@Slf4j
@Component
public class CryptoService {

    private static RsaKeyPair rsaKeyPair = null;
    @Autowired
    private RedisCache redisCache;

    /**
     * RSA 私钥解密
     *
     * @param ciphertext
     * @return
     * @throws Exception
     */
    public String decryptRsa(String ciphertext) throws Exception {
        RsaKeyPair rsaKeyPair = getRsaKeyPair();
        return RsaUtils.decrypt(rsaKeyPair.getPrivateKey(), ciphertext);
    }

    /**
     * 获取RSA密钥对
     *
     * @return
     */
    public RsaKeyPair getRsaKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException {
        if (rsaKeyPair != null) {
            redisCache.setCacheObject(Constants.RSA_KEY_PAIR, rsaKeyPair, 1, TimeUnit.DAYS);
            return rsaKeyPair;
        }
        rsaKeyPair = redisCache.getCacheObject(Constants.RSA_KEY_PAIR);
        if (rsaKeyPair == null) {
            rsaKeyPair = RsaUtils.generateKeyPair();
        }
        redisCache.setCacheObject(Constants.RSA_KEY_PAIR, rsaKeyPair, 1, TimeUnit.DAYS);
        return rsaKeyPair;
    }


}
CryptoService.java
  • 增加加解密过滤器
package xxx.filter;

import xxx.HttpHelper;
import xxx.RsaKeyPair;
import xxx.RsaUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 通信加密
 *
 * @author carliels
 */
@Slf4j
public class CryptRequestFilter implements Filter {

    public static final String EXCLUDE = "exclude";
    public static final String RSA_PUBLIC_KEY = "rsaPublicKey";
    public static final String RSA_PRIVATE_KEY = "rsaPrivateKey";
    private static final String ENCRYPTION_MARK = "Encryption";
    private final AntPathMatcher pathMatcher = new AntPathMatcher();
    private String[] excludePaths = new String[0];
    private RsaKeyPair rsaKeyPair;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String excludes = filterConfig.getInitParameter(EXCLUDE);
        if (org.apache.commons.lang3.StringUtils.isNotBlank(excludes)) {
            this.excludePaths = excludes.split(",");
        }
        String rsaPublicKey = filterConfig.getInitParameter(RSA_PUBLIC_KEY);
        String rsaPrivateKey = filterConfig.getInitParameter(RSA_PRIVATE_KEY);
        this.rsaKeyPair = new RsaKeyPair(rsaPublicKey, rsaPrivateKey);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        // 路径过滤
        String requestURI = request.getRequestURI();
        boolean skip = false;
        for (String excludePath : excludePaths) {
            if (pathMatcher.match(excludePath, requestURI)) {
                skip = true;
                break;
            }
        }
        if (skip) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            String requestBody = HttpHelper.getBodyString(servletRequest);
            // 获取请求头加密标记
            String encryptionMark = request.getHeader(ENCRYPTION_MARK);
            //解密请求报文
            String requestBodyMw = requestBody;

            // 这里的依赖于前端给定标记进行解密处理,强制情况下应当只根据uri来判定
            boolean encrypted = org.apache.commons.lang3.StringUtils.isNotBlank(encryptionMark)
                    && Boolean.TRUE.toString().equalsIgnoreCase(encryptionMark);

            if (encrypted && StringUtils.isNotBlank(requestBody)) {
                try {
                    requestBodyMw = RsaUtils.decrypt(requestBody, this.rsaKeyPair.getPrivateKey());
                } catch (Exception e) {
                    log.error("decrypt request body exception.", e);
                }
            }
            WrappedRequest wrapRequest = new WrappedRequest(request, requestBodyMw);
            filterChain.doFilter(wrapRequest, response);
//            if (!encrypted) {
//                filterChain.doFilter(wrapRequest, response);
//            } else {
//                WrappedResponse wrapResponse = new WrappedResponse(response);
//                byte[] data = wrapResponse.getResponseData();
//                String responseBodyMw = null;
//                try {
//                    responseBodyMw = RsaUtils.encrypt(data, this.rsaKeyPair.getPublicKey());
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//                System.out.println("加密返回数据: " + responseBodyMw);
//                response.addHeader("encrypt", "TRUE");
//                response.getOutputStream().write(responseBodyMw.getBytes());
//            }
        }
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
CryptRequestFilter.java
package xxx.filter;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

/**
 * @author carliels
 */
public class WrappedRequest extends HttpServletRequestWrapper {

    private String requestBody = null;
    HttpServletRequest req = null;

    public WrappedRequest(HttpServletRequest request) {
        super(request);
        this.req = request;
    }

    public WrappedRequest(HttpServletRequest request, String requestBody) {
        super(request);
        this.requestBody = requestBody;
        this.req = request;
    }

    /**
     * (non-Javadoc)
     *
     * @see javax.servlet.ServletRequestWrapper#getReader()
     */
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new StringReader(requestBody));
    }

    /**
     * (non-Javadoc)
     *
     * @see javax.servlet.ServletRequestWrapper#getInputStream()
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new ServletInputStream() {
            private InputStream in = new ByteArrayInputStream(
                    requestBody.getBytes(req.getCharacterEncoding()));

            @Override
            public int read() throws IOException {
                return in.read();
            }

            @Override
            public boolean isFinished() {
                // TODO Auto-generated method stub
                return false;
            }

            @Override
            public boolean isReady() {
                // TODO Auto-generated method stub
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
                // TODO Auto-generated method stub

            }
        };
    }
}
WrappedRequest.java
package xxx.filter;

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;

/**
 * @author carliels
 */
public class WrappedResponse extends HttpServletResponseWrapper {

    private ByteArrayOutputStream buffer = null;
    private ServletOutputStream out = null;
    private PrintWriter writer = null;

    public WrappedResponse(HttpServletResponse resp) throws IOException {
        super(resp);
        // 真正存储数据的流
        buffer = new ByteArrayOutputStream();
        out = new WapperedOutputStream(buffer);
        writer = new PrintWriter(new OutputStreamWriter(buffer,
                this.getCharacterEncoding()));
    }

    /**
     * 重载父类获取outputstream的方法
     */
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return out;
    }

    /**
     * 重载父类获取writer的方法
     */
    @Override
    public PrintWriter getWriter() throws UnsupportedEncodingException {
        return writer;
    }

    /**
     * 重载父类获取flushBuffer的方法
     */
    @Override
    public void flushBuffer() throws IOException {
        if (out != null) {
            out.flush();
        }
        if (writer != null) {
            writer.flush();
        }
    }

    @Override
    public void reset() {
        buffer.reset();
    }

    /**
     * 将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据
     */
    public byte[] getResponseData() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }

    /**
     * 内部类,对ServletOutputStream进行包装
     */
    private class WapperedOutputStream extends ServletOutputStream {
        private ByteArrayOutputStream bos = null;

        public WapperedOutputStream(ByteArrayOutputStream stream)
                throws IOException {
            bos = stream;
        }

        @Override
        public void write(int b) throws IOException {
            bos.write(b);
        }

        @Override
        public void write(byte[] b) throws IOException {
            bos.write(b, 0, b.length);
        }

        @Override
        public boolean isReady() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public void setWriteListener(WriteListener writeListener) {
            // TODO Auto-generated method stub

        }
    }
}
WrappedResponse.java
package com.casic.config;

import xxx.StringUtils;
import xxx.RsaKeyPair;
import xxx.utils.spring.SpringUtils;
import xxx.filter.CryptRequestFilter;
import xxx.service.CryptoService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.DispatcherType;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.HashMap;
import java.util.Map;

/**
 * Filter配置
 *
 * @author carliels
 */
@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean cryptRequestFilter() throws NoSuchAlgorithmException, NoSuchProviderException {
        CryptoService cryptoService = SpringUtils.getBean(CryptoService.class);
        RsaKeyPair rsaKeyPair = cryptoService.getRsaKeyPair();
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new CryptRequestFilter());
        registration.addUrlPatterns("/*");
        Map<String, String> initParameters = new HashMap<String, String>();
        initParameters.put(CryptRequestFilter.EXCLUDE, "/**/publicKey,/captchaImage");
        initParameters.put(CryptRequestFilter.RSA_PUBLIC_KEY, rsaKeyPair.getPublicKey());
        initParameters.put(CryptRequestFilter.RSA_PRIVATE_KEY, rsaKeyPair.getPrivateKey());
        registration.setInitParameters(initParameters);
        registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
        return registration;
    }


}
FilterConfig.java
  • 开放公钥获取接口
@Anonymous
@GetMapping("/publicKey")
public String publicKey() throws NoSuchAlgorithmException, NoSuchProviderException {
   RsaKeyPair rsaKeyPair = cryptoService.getRsaKeyPair();
   return rsaKeyPair.getPublicKey();
}

以上大体就是整个实现代码了, 糙是糙了点,将就用。

3 改进建议

  • 现有代码密钥对更新周期为1天, 有一个Bug, 如果刚好卡点,会形成使用上一次的公钥加密数据,解密时为新生成的私钥,导致解密失败。解决思路:缓存一个以上密钥对,解密的时候按密钥对生成时间倒序匹配,当然密钥对数量根据更新周期来定,性能上损耗较大。 也可以在请求头中增加密钥对时间,通过时间直接获取指定私钥解密。要不要改进这一点,视情况而定。 比如我这里,我就不改,在页面报错提示上优化"您的页面已过期,请刷新", 这样刷新页面后客户端公钥也更新成了新的。
  • 响应加密。如果需要对响应加密,则可使用两组密钥对来实现,客户端保存【公钥1】和【私钥2】,使用【公钥1】加密请求参数,使用【私钥2】解密响应。响应加密有实例代码,已注释。 使用响应加密有前提,需要充分考虑响应内容,比如响应文件,图片等内容就不太合适,酌情考虑使用。

标签:Vue,加密,Springboot,java,return,import,public,String
From: https://www.cnblogs.com/carliels-lullaby/p/18282211

相关文章

  • 企业做源代码加密/防泄密,需要如何选择产品?
    一:稳定性是选型最重要的标准    没有稳定不谈应用看过国内品牌繁多的加密软件后,很多人感觉加密软件没有太多技术含量。选型首先就问就问是不是BS架构呀、有没有桌面管控、能不能远程推送、能不能兼容LINUX等功能。我认为这些花哨的功能的确能让应用锦上添花,但忽视加密......
  • springboot使用注解方式打印方法日志
    springboot使用注解方式打印方法日志,可以很方便的打印日志,通用性很强。耦合很低,很好。作为程序员的我不废话,咱们直接上代码先创建个注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceShowLog{/***日志描......
  • vue elementUI el-tree 下拉树功能(包括搜索/默认高亮/展开下拉框默认定位于选中项的位
    <template><div><el-form:model="formData"ref="refFormData"label-width="180px"><el-form-itemlabel="景点"prop="location_id"><el-selectv-model="formData.location_name&qu......
  • Vue3快速上手
    好久没上传了,闲来无事把囤积已久的笔记给上传上传1.Vue3简介2020年9月18日,Vue.js发布版3.0版本,代号:OnePiece(n经历了:4800+次提交、40+个RFC、600+次PR、300+贡献者官方发版地址:Releasev3.0.0OnePiece·vuejs/core截止2023年10月,最新的公开版本为:3.3.41.1.......
  • springboot的MultipartFile转File读取
    在SpringBoot中,处理文件上传时,MultipartFile接口被用来封装上传的文件信息。如果需要将MultipartFile转换为Java标准的File对象进行读取。以下是具体的操作流程:1.创建临时文件        首先,需要将接收到的MultipartFile对象转换为一个临时File对象。      ......
  • 基于Java+Vue的智慧园区管理系统:创新园区运营模式,构建全方位企业服务体系(代码分享)
     前言:智慧园区管理系统是一个集成了多种功能的综合性系统,旨在通过信息化、智能化手段提升园区的管理效率和服务质量。以下是针对系统的各个功能模块的简要描述:一、楼栋管理会务管理:管理园区内的会议预约、会议室使用等。园区信息:展示园区的基本信息,如位置、面积、规划等。......
  • 基于Java+Vue的企事业移动培训考试系统:体系化培训管理,保障培训效果(代码分享)
     前言:企事业移动培训考试系统是一个集成多种功能的综合性平台,旨在为企业提供便捷、高效、灵活的在线培训和考试解决方案。以下是针对平台所列出的八个主要功能的详细解释:一、文档管理及在线预览允许企业上传、存储、管理和分享各种培训文档,如PPT、PDF、Word等。提供在线预......
  • Vue3全局配置Axios并解决跨域请求问题示例详解
    背景对于前后端分离项目,前端和后端端口不能重复,否则会导致前端或者后端服务起不来。例如前端访问地址为: http://localhost:8080/ ,后端访问地址为 http://localhost:8081/ 。后端写好Controller,当用Axios访问该接口时,将会报错:AccesstoXMLHttpRequestat'http://localh......
  • springboot-mybatis-db2
    工程pom.xml文件增加如下依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency>&......
  • 从0到1写一个vue2管理后台项目(二)
    前言:现在有很多vue2的管理后台系统,为什么还要自己写一个呢?我觉得会用总没有会写理解得深一、有了基础框架(一)之后,对于一个vue开发工程师来说如何自己搭建自己的后台管理系统呢?当然可以直接抄一些现有的开源系统的代码,比如admin、ruoyi等,同样我想试试通过AI来写,一步一步完善,于是用......