Springboot+Vue加密通信

前言

  本文旨在给出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;}@Overridepublic 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;@Autowiredprivate 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;@Overridepublic 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);}@Overridepublic 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());
//            }
        }}@Overridepublic 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()*/@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new StringReader(requestBody));}/*** (non-Javadoc)** @see javax.servlet.ServletRequestWrapper#getInputStream()*/@Overridepublic ServletInputStream getInputStream() throws IOException {return new ServletInputStream() {private InputStream in = new ByteArrayInputStream(requestBody.getBytes(req.getCharacterEncoding()));@Overridepublic int read() throws IOException {return in.read();}@Overridepublic boolean isFinished() {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean isReady() {// TODO Auto-generated method stubreturn false;}@Overridepublic 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的方法*/@Overridepublic ServletOutputStream getOutputStream() throws IOException {return out;}/*** 重载父类获取writer的方法*/@Overridepublic PrintWriter getWriter() throws UnsupportedEncodingException {return writer;}/*** 重载父类获取flushBuffer的方法*/@Overridepublic void flushBuffer() throws IOException {if (out != null) {out.flush();}if (writer != null) {writer.flush();}}@Overridepublic 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;}@Overridepublic void write(int b) throws IOException {bos.write(b);}@Overridepublic void write(byte[] b) throws IOException {bos.write(b, 0, b.length);}@Overridepublic boolean isReady() {// TODO Auto-generated method stubreturn false;}@Overridepublic 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 {@Beanpublic 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】解密响应。响应加密有实例代码,已注释。 使用响应加密有前提,需要充分考虑响应内容,比如响应文件,图片等内容就不太合适,酌情考虑使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/736941.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VMware vSphere Tanzu部署_09_配置tanzu内容库

配置Tanzu内容库Tanzu内容库订阅地址为:https://wp-content.vmware.com/v2/latest/lib.json如下为配置步骤在vcenter中配置内容库即可

Centos7下安装Redis

1.安装依赖 redis是由C语言开发,因此安装之前必须要确保服务器已经安装了gcc,可以通过如下命令查看机器是否安装:gcc -v如果没有安装则通过以下命令安装:yum install -y gcc2.下载redis安装包并解压# 下载,我是在root下执行的下载,所以我的下载目录为:/root/redis-6.2.6…

Winform SynchronizationContext多线程更新画面控件

SynchronizationContext在通讯中充当传输者的角色,实现功能就是一个线程和另外一个线程的通讯。需要注意的是,不是每个线程都附加SynchronizationContext这个对象,只有UI线程是一直拥有的。故获取SynchronizationContext也只能在UI线程上进行SynchronizationContext context…

复数与复变函数选题

【参考】《复变函数论》钟玉泉编 《复变函数论学习指导书》钟玉泉编

Kubernetes基于helm安装 harbor

Kubernetes基于helm安装 harbor 之前harbor的安装都是借助docker完成一键安装部署,安装完成之后harbor组件均运行到一台机器上面,本文实践harbor在k8s环境中的部署。 准备工作 根据harbor官方要求:Kubernetes cluster 1.20+ Helm v3.2.0+结合ingress-nginx版本要求,建议K8S…

pdf的表格怎么转换成word?

Adobe设计PDF文件格式的目的是支持跨平台、多媒体集成的信息出版和发布,特别是对网络信息发布的支持。为了实现这一目标,PDF具有许多其他电子文件格式无法比拟的优势。如今,越来越多的电子书籍、产品描述、公司通知、网络数据和电子邮件开始使用PDF格式文件。但是PDF却不能直…

高效存储的秘诀:bitmap 数据结构在标签中的应用

在当今大数据和信息爆炸的时代,如何有效地管理和查询海量的数据成为了企业和开发者面临的重大挑战。其中,标签系统作为数据管理中的一种重要手段,被广泛应用于用户画像、商品分类、内容推荐等多个场景。然而,随着标签数量的急剧增加,传统的数据存储和查询方式已难以满足高…

2024年值得收藏的几款开源主机安全系统hids

随着云技术的迅速发展,主机安全系统HIDS作为服务器安全的最后一道防线,无论传统的硬件厂商,还是各大云厂商如阿里、腾讯云非常重视并闷声发大财。HIDS主机安全开源的项目虽多,但能实际用的极少,笔者经过大量搜索,找到以下几款优秀的产品供大家参考: 1、OpenHFWOpenHFW全…

代码随想录算法训练营第四十六天 | 买卖股票的最佳时机

121.买卖股票的最佳时机 题目链接 文章讲解 视频讲解动规五部曲:dp[j][0]: 表示持有股票的最大现金,dp[j][1]表示不持有股票的最大现金 递推公式:第j天持有股票的最大现金为:之前就持有这只股票和今天持有这只股票取最大值: dp[j][0] = max(dp[j-1][0], -prices[j]); 第j天…

Asp.Net Core -Authorizationz授权

授权的内部实现参考 动态授权参考动态授权 基于权限的授权 1.定义权限-Permissionspublic class Permissions{public const string Admin = "Admin";public const string Users = "Users";public const string UserCreate = Users+ ".Create";pu…

结对项目

命令行参数处理: 使用 argparse 或者 sys 模块处理命令行参数,以接收用户输入的题目数量和题目范围(例如最大数值)。 生成四则运算题目: 随机生成题目,每个题目包括两个操作数和一个运算符。 控制生成的题目数量和数值范围,确保生成的题目符合小学生的学习要求(比如操作…

详解微服务应用灰度发布最佳实践

本次分享是站在 DevOps 视角的灰度发布实践概述,主要内容包括以下四个方面:第一,灰度发布要解决的问题;第二,灰度发布的四种典型场景;第三,如何把灰度发布融入到应用的研发流程中,即把灰度发布与 DevOps 工作融合;第四,对于外部流量灰度场景,演示如何通过工具将其落…

OpenBMB Hugging Face THUNLP,联袂献上经典大模型课

这个夏天,THUNLP 携手 Hugging Face 和 OpenBMB,推出 大模型公开课第二季。在大模型公开课第二季中,将有全球知名开源社区 OpenBMB X Hugging Face 梦幻联动;MiniCPM、ChatDev、Ultra对齐 等明星开源项目作者亲自授课,带领同学从深度学习开始快速了解大模型的相关理论并进…

nginx开启https

生成自签名证书 如果是在运营商处获得的证书,则不用这一步点击查看代码 mkdir /etc/nginx/ssl cat > /etc/nginx/ssl/nginx.cnf <<EOF [ req ] default_bits = 2048 distinguished_name = req_distinguished_name req_extensions = req_ext x509_extension…

(三)JS逆向——爬取易车网

爬取易车网的车辆配置信息 所以主要就是确定这几个是怎么生成的 搜索x-sign值的位置,打断点,找到构建x-sign的函数 注意,一定要看清是否是正确的url,x-timestamp明显是时间戳 进入这个函数 x-sign值,是对这个字符串做md5,这个字符串由三部分构成,参数、时间戳、和一个固…

.Net知识技能大全

.Net知识技能大全 更多请见https://www.dotnetshare.com C#常见运算符一元运算符(+、-、!、~、++、--) 算术运算符(*、/、%、+、 – ) 移位运算符(<< 、>> ) 关系和类型测试运算符(==、!=、<、>、<=、>=、is 和 as) 逻辑运算符(&、^ 和 |…

Hello World with solidity

1. Solidity 是什么? Solidity 是一种面向对象的、静态类型的编程语言,专为在以太坊上编写智能合约而创建。由于以太坊上的智能合约可以处理真实世界的资产(比如加密货币),所以Solidity的设计非常关注安全性。 以下是 Solidity 的一些主要特点:类型安全和静态类型:这可以…

如何从Salesforce顾问转型为解决方案架构师?

在充满活力的Salesforce咨询领域,许多专业人士都希望从顾问转型为解决方案架构师。这种转型不仅需要掌握新技能,还需要从根本上改变思维方式。关键在于摆脱顾问典型的以职能为中心的思维模式,拥抱更广阔、更具战略性的视角。 从“如何”到“为什么” 当我们开始问“为什么”…

Modbus转Profibus主站网关连接温控表通讯技术

Modbus转ProfibusDP主站网关(XD-MDPBM20)是实现不同通讯协议设备之间联系的重要组件。本文将介绍Modbus转ProfibusDP主站网关(XD-MDPBM20)连接温控表实现高效通讯,涉及设备准备、协议介绍、配置步骤、测试通讯及注意事项。此技术可帮助实时监测温度并调节控制,提高生产效…

加解密技术原理

加解密技术概览 加解密定义 加密:数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码,通常称为“密文”,通过这样的途径来达到保护数据不被非法人员窃取、阅读的目的。解密:数据解密的过程就是对密文使用相应的算法和密钥进行解…