SpringBoot + Vue 实现 AES 加密和 AES 工具类总结

目录

一、加密知识回顾 

AES加密模式

二、Java 自定义 AES 工具类 

三、SpringBoot 实现 AES 加密登陆

controller 层

server 层

四、Vue 实现 AES 加密登陆

五、前端AES工具类

六、实现结果


一、加密知识回顾 

        密钥是AES算法实现加密和解密的根本。对称加密算法之所以对称,是因为这类算法对明文的加密和解密需要使用同一个密钥。
AES支持三种长度的密钥:  128位,192位,256位 
平时大家所说的AES128,AES192,AES256,实际上就是指的AES算法对不同长度密钥的使用。从安全性来看,AES256 安全性最高。从性能来看,AES128 性能最高。本质原因是它们的加密处理轮数不同。

对称加密与非对称加密有什么区别,敏感数据怎么加解密和传输

AES加密模式

1 : ECB(Electronic Code Book电子密码本)模式
ECB模式是最早采用和最简单的模式,它将加密的数据分成若干组,每组的大小跟加密密钥长度相同,然后每组都用相同的密钥进行加密。


优点:1、简单;2:有利于并行计算;3、误差不会被传送; 
缺点:1、不能隐藏明文的模式;2、可能对明文进行主动攻击;
因此,此模式适于加密小消息。

2 : CBC(Cipher Block Chaining,加密块链)模式
优点:1、不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。
缺点:1、不利于并行计算;2、误差传递;3、需要初始化向量IV

3 : CFB(Cipher FeedBack Mode,加密反馈)模式
优点:1、隐藏了明文模式;2、分组密码转化为流模式;3、可以及时加密传送小于分组的数据;
缺点:1、不利于并行计算;2、误差传送:一个明文单元损坏影响多个单元;3、唯一的IV;

4 : OFB(Output FeedBack,输出反馈)模式
优点:1、隐藏了明文模式;2、分组密码转化为流模式;3、可以及时加密传送小于分组的数据;
缺点:1、不利于并行计算;2、对明文的主动攻击是可能的;3、误差传送:一个明文单元损坏影响多个单元;

        ECB 不够安全,只适合于短数据的加密,而 CBC 和 CFB 相较于 ECB 更加安全,因为前一个密文块会影响当前明文块,使攻击者难以预测密文的结构。总的来说,选择 AES 的算法模式需要根据加密需要的安全性和速度来进行选择,通常推荐使用CBC 或 CFB 模式,而不是 ECB 模式。

二、Java 自定义 AES 工具类 

AES 算法在 Java 的 javax.crypto 包里有很好的封装,我们来看一下调用的方法:

public class AesSingleUtil {private static volatile AesSingleUtil aESUtil;private static String encodeRules = "9JLsB1ukY3o4mdTuuE90+Q==";private AesSingleUtil() {}public static AesSingleUtil getSingleton() {if (aESUtil == null) {Class var0 = AesSingleUtil.class;synchronized(AesSingleUtil.class) {if (aESUtil == null) {aESUtil = new AesSingleUtil();}}}return aESUtil;}public String AESEncode(String content) throws Exception {KeyGenerator keygen = KeyGenerator.getInstance("AES");keygen.init(128, new SecureRandom(encodeRules.getBytes()));SecretKey original_key = keygen.generateKey();byte[] raw = original_key.getEncoded();SecretKey key = new SecretKeySpec(raw, "AES");Cipher cipher = Cipher.getInstance("AES");cipher.init(1, key);byte[] byte_encode = content.getBytes("utf-8");byte[] byte_AES = cipher.doFinal(byte_encode);String AES_encode = new String(Base64.getEncoder().encode(byte_AES));return AES_encode;}public String AESDecode(String content) throws Exception {KeyGenerator keygen = KeyGenerator.getInstance("AES");keygen.init(128, new SecureRandom(encodeRules.getBytes()));SecretKey original_key = keygen.generateKey();byte[] raw = original_key.getEncoded();SecretKey key = new SecretKeySpec(raw, "AES");Cipher cipher = Cipher.getInstance("AES");cipher.init(2, key);byte[] byte_content = Base64.getDecoder().decode(content);byte[] byte_decode = cipher.doFinal(byte_content);String AES_decode = new String(byte_decode, "utf-8");return AES_decode;}
}

 AesSingleUtil 类:这是一个单例类,使用了双重检查锁(double-checked locking)实现按需延迟初始化。在 getSingleton 方法中,通过检查静态变量 aESUtil 是否为 null,来确定是否需要创建新的实例。通过单例模式确保了整个应用程序中只有一个实例存在,节省了资源并避免了不必要的重复创建对象。

encodeRules 变量:存储着用于生成 AES 密钥的编码规则。

AESEncode 方法:接受一个字符串作为参数,使用该字符串作为密钥的种子,生成 AES 密钥,并对输入的内容进行 AES 加密,最终返回 Base64 编码的加密结果。

AESDecode 方法:与 AESEncode 方法相对应,接受一个经过 Base64 编码的加密字符串作为参数,使用同样的密钥规则生成 AES 密钥,并对传入的加密字符串进行 AES 解密,返回解密后的原始字符串。

import com.ss.check.NotNullCheck;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class AesWebUtil {private static Logger log = LoggerFactory.getLogger(AesWebUtil.class);private static String KEY = "kl9o56u98gvjhw9i";private static String IV = "grjei564389tj843";public AesWebUtil() {}public String encrypt(String data, String key, String iv) {try {Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");int blockSize = cipher.getBlockSize();byte[] dataBytes = data.getBytes("UTF-8");int plaintextLength = dataBytes.length;if (plaintextLength % blockSize != 0) {plaintextLength += blockSize - plaintextLength % blockSize;}byte[] plaintext = new byte[plaintextLength];System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());cipher.init(1, keyspec, ivspec);byte[] encrypted = cipher.doFinal(plaintext);return (new Base64()).encodeToString(encrypted);} catch (Exception var12) {return null;}}public String desEncrypt(String data, String key, String iv) {try {byte[] encrypted1 = (new Base64()).decode(data.getBytes("UTF-8"));Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());cipher.init(2, keyspec, ivspec);byte[] original = cipher.doFinal(encrypted1);String originalString = new String(original);return originalString;} catch (Exception var10) {return null;}}public static String jiaMi(String data) {String mm = "";try {if (NotNullCheck.str(data)) {mm = (new AesWebUtil()).encrypt(data, KEY, IV).trim();}} catch (Exception var3) {}return mm;}public static String jieMi(String data) {String mm = "";try {if (NotNullCheck.str(data)) {mm = (new AesWebUtil()).desEncrypt(data, KEY, IV).trim();}} catch (Exception var3) {}return mm;}public static void main(String[] args) throws Exception {String jiemi = (new AesWebUtil()).desEncrypt("oLlc7AJnsgl93vaAEYMgGd2/G8zLFX8HrCN9HaBwS9rLJYFT8+RmlReuNHHdX+NacpP6MAByLIdxSeMBQ/9a5HA1oikanQY5I1kTCxpaN639WvA/Rj8I74PicVYemYqiMr/W6dDwpyJt7H5jL7Sofw6bnNmQAtsF9UNQthDS73ddxo19ThLpxOajVE5LF+Mu", KEY, IV).trim();System.out.println("解密:" + jiemi);new AesWebUtil();String jiami = jiaMi("123456");System.out.println("加密:" + jiami);}
}

三、SpringBoot 实现 AES 加密登陆

controller 层

    @PostMapping("login")public String userLogin(@RequestBody String user, HttpServletRequest request) {return userService.userLogin(user, request);}

server 层

    public String userLogin(String user, HttpServletRequest request) {String jiemiUserStr = null;try {jiemiUserStr = AesWebUtil.jieMi(user);} catch (Exception e) {throw new SSError(GlobalCodeEnum.SystemLogicError, "非法登录!");}UserAll userAll = GsonUtil.jsonToObject(jiemiUserStr, UserAll.class);if (userAll != null && userAll.getLoginType() != null) {UserAll loginBack = null;if (userAll.getLoginType() == 1 && NotNullCheck.str(userAll.getUserLoginName())&& NotNullCheck.str(userAll.getUserPassword())) {loginBack = userMapper.getUserByLoginName(userAll.getUserLoginName());if(loginBack==null){throw new SSError(GlobalCodeEnum.SystemLogicError, "用户不存在");}else if(loginBack!=null&&!AesWebUtil.jiaMi(userAll.getUserPassword()).equals(loginBack.getUserPassword())){throw new SSError(GlobalCodeEnum.SystemLogicError, "密码不正确");}} else if (userAll.getLoginType() == 2 && NotNullCheck.str(userAll.getUserCard())) {loginBack = userMapper.getUserByUserCard(userAll);}if (loginBack == null) {throw new SSError(GlobalCodeEnum.SystemLogicError, "登录信息错误");} else {checkUserRightState(loginBack);try {loginBack.setUserPassword(JwtUtil.generateToken());} catch (Exception e) {e.printStackTrace();}
//                log.info("****************登录成功:" + loginBack);//埋点登录return AesWebUtil.jiaMi(GsonUtil.objectToJson(loginBack));}}throw new SSError(GlobalCodeEnum.SystemLogicError, "登录信息不完整");}

四、Vue 实现 AES 加密登陆

<template><common-layout><div class="top"><div class="header"><img alt="logo" class="logo" src="@/assets/img/logo.png" /><span class="title">{{ systemName }}</span></div><div class="desc"></div></div><div class="login"><a-form @submit.native="onSubmit" :form="form"><a-tabssize="large":tabBarStyle="{ textAlign: 'center' }"style="padding: 0 2px"@change="userLoginTypeChange"><a-tab-pane tab="账户密码登录" key="1"><a-alerttype="error":closable="true"v-show="error":message="error"showIconstyle="margin-bottom: 24px"/><a-form-item><a-inputref="userNameRef"autocomplete="autocomplete"size="large"allowClearplaceholder="登录用户名"v-decorator="['name',{rules: [{required: true,message: '请输入账户名',whitespace: true,},],},]"><a-icon slot="prefix" type="user" /></a-input></a-form-item><a-form-item><a-inputsize="large"placeholder="登录密码"autocomplete="autocomplete"type="password"allowClearv-decorator="['password',{rules: [{required: true,message: '请输入密码',whitespace: true,},],},]"><a-icon slot="prefix" type="lock" /></a-input></a-form-item></a-tab-pane><a-tab-pane tab="工号登录" key="2"><a-form-item><a-inputsize="large"placeholder="员工工号"v-model="userCard"ref="userCardRef"allowClear><a-icon slot="prefix" type="solution" /></a-input></a-form-item><!-- <a-form-item> --><!-- <a-row :gutter="8" style="margin: 0 -4px"><a-col :span="16"> --><!--   <a-input size="large" placeholder="captcha"><a-icon slot="prefix" type="lock" /></a-input> --><!-- </a-col> --><!-- <a-col :span="8" style="padding-left: 4px"><a-buttonstyle="width: 100%"class="captcha-button"size="large">获取验证码</a-button></a-col> --><!-- </a-row> --><!-- </a-form-item> --></a-tab-pane></a-tabs><!-- <div><a-checkbox :checked="true">自动登录</a-checkbox></div> --><a-form-item><a-button:loading="logging"style="width: 100%; margin-top: 24px"size="large"htmlType="submit"type="primary">登录</a-button></a-form-item></a-form></div></common-layout>
</template><script>
import CommonLayout from "@/layouts/CommonLayout";
// import { login, getRoutesConfig } from "@/services/user";
// import { setAuthorization } from "@/utils/request";
// import { loadRoutes } from "@/utils/routerUtil";import { mapMutations } from "vuex";
import { userLogin } from "@/api/user";import { aesJiami, aesJiemi } from "@/utils/aes";const timeList = ["早上好", "上午好", "中午好", "下午好", "晚上好"];export default {name: "Login",components: { CommonLayout },data() {return {logging: false,error: "",form: this.$form.createForm(this),userLgoinType: 1,userCard: "",};},computed: {systemName() {return this.$store.state.setting.systemName;},},mounted() {// 禁用浏览器返回键history.pushState(null, null, document.URL);window.addEventListener("popstate", this.disableBrowserBack);if (this.$refs.userNameRef) {this.$refs.userNameRef.focus();}},destroyed() {// 清除popstate事件 否则会影响到其他页面window.removeEventListener("popstate", this.disableBrowserBack, false);},methods: {...mapMutations("account", ["setUser", "setPermissions", "setRoles"]),disableBrowserBack() {history.pushState(null, null, document.URL);},userLoginTypeChange(key) {setTimeout(() => {if (key === "1") {this.$refs.userNameRef.focus();} else if (key === "2") {this.$refs.userCardRef.focus();}}, 100);this.userLgoinType = key;},onSubmit(e) {e.preventDefault();let checkUserLoginInput = false;if (this.userLgoinType === 1) {this.form.validateFields((err) => {if (!err) {checkUserLoginInput = true;}});} else {if (this.userCard &&this.userCard.replace(/↵/g, "").replace(/[/n/r]/g, "")) {checkUserLoginInput = true;}}if (!checkUserLoginInput) {return;}this.logging = true;const name = this.form.getFieldValue("name");const password = this.form.getFieldValue("password");// login(name, password).then(this.afterLogin);const userLoginInfo = {userLoginName: name,userPassword: password,userCard: this.userCard,loginType: this.userLgoinType,};userLogin(aesJiami(JSON.stringify(userLoginInfo))).then((res) => {if (!res) {return;}try {res = JSON.parse(aesJiemi(res));} catch (error) {this.$message.error("登录失败,服务器校验不通过");return;}let permissions = null;if (res) {permissions = [{ id: res.userAuth, operation: ["add", "edit"] }];if (!permissions) {this.$message.error("用户权限校验失败");return;}this.setPermissions(aesJiami(JSON.stringify(permissions)));this.$router.push({ name: "主页" });const time = new Date();const hour = time.getHours();const welcomeTime =hour < 9? timeList[0]: hour <= 11? timeList[1]: hour <= 13? timeList[2]: hour <= 17? timeList[3]: timeList[4];this.$message.success(welcomeTime + "," + res.userName);const roles = [{ id: res.userAuth, operation: ["add", "edit", "delete"] },];res.name = res.userName;this.setRoles(aesJiami(JSON.stringify(roles)));this.setUser(aesJiami(JSON.stringify(res)));}}).finally(() => {this.logging = false;});},/*  afterLogin(res) {this.logging = false;const loginRes = res.data;if (loginRes.code >= 0) {const { user, permissions, roles } = loginRes.data;this.setUser(user);this.setPermissions(permissions);this.setRoles(roles);setAuthorization({token: loginRes.data.token,expireAt: new Date(loginRes.data.expireAt),});// 获取路由配置getRoutesConfig().then((result) => {const routesConfig = result.data.data;loadRoutes(routesConfig);this.$router.push("/dashboard/workplace");this.$message.success(loginRes.message, 3);});} else {this.error = loginRes.message;}}, */},
};
</script><style lang="less" scoped>
.common-layout {.top {text-align: center;.header {height: 44px;line-height: 44px;a {text-decoration: none;}.logo {height: 44px;vertical-align: top;margin-right: 16px;}.title {font-size: 33px;color: @title-color;font-family: "Myriad Pro", "Helvetica Neue", Arial, Helvetica,sans-serif;font-weight: 600;position: relative;top: 2px;}}.desc {font-size: 14px;color: @text-color-second;margin-top: 12px;margin-bottom: 40px;}}.login {width: 368px;margin: 0 auto;@media screen and (max-width: 576px) {width: 95%;}@media screen and (max-width: 320px) {.captcha-button {font-size: 14px;}}.icon {font-size: 24px;color: @text-color-second;margin-left: 16px;vertical-align: middle;cursor: pointer;transition: color 0.3s;&:hover {color: @primary-color;}}}
}
</style>

五、前端AES工具类

import CryptoJS from "crypto-js";const KEY = CryptoJS.enc.Utf8.parse("kl9o56u98gvjhw9i");
const IV = CryptoJS.enc.Utf8.parse("grjei564389tj843");export const aesJiami = (word, keyStr, ivStr) => {let key = KEY;let iv = IV;if (keyStr) {key = CryptoJS.enc.Utf8.parse(keyStr);iv = CryptoJS.enc.Utf8.parse(ivStr);}let srcs = CryptoJS.enc.Utf8.parse(word);var encrypted = CryptoJS.AES.encrypt(srcs, key, {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.ZeroPadding,});// console.log("-=-=-=-", encrypted.ciphertext)return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
};export const aesJiemi = (word, keyStr, ivStr) => {let key = KEY;let iv = IV;if (keyStr) {key = CryptoJS.enc.Utf8.parse(keyStr);iv = CryptoJS.enc.Utf8.parse(ivStr);}let base64 = CryptoJS.enc.Base64.parse(word);let src = CryptoJS.enc.Base64.stringify(base64);var decrypt = CryptoJS.AES.decrypt(src, key, {iv: iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.ZeroPadding,});var decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);return decryptedStr.toString();
};

 这段前端代码的逻辑是:

  1. 导入 CryptoJS 库:通过 import CryptoJS from "crypto-js"; 这行代码导入了 CryptoJS 库,用于进行加密和解密操作。

  2. 定义加密所需的密钥和向量:

    KEY 和 IV 分别是用于加密和解密的密钥和初始化向量。在这段代码中,它们被硬编码为固定值。
  3. 定义 aesJiami 函数:

    aesJiami 函数用于对传入的字符串进行加密操作。如果有自定义的密钥和向量参数,则使用传入的参数,否则使用预设的 KEY 和 IV。将传入的字符串转换为 UTF-8 编码格式。使用 AES 加密算法对字符串进行加密,加密模式为 CBC,填充方式为 ZeroPadding。将加密后的结果转换为 Base64 字符串并返回。
  4. 定义 aesJiemi 函数:

    aesJiemi 函数用于对传入的 Base64 加密字符串进行解密操作。如果有自定义的密钥和向量参数,则使用传入的参数,否则使用预设的 KEY 和 IV。将传入的 Base64 加密字符串解析为 Base64 对象。使用 AES 解密算法对 Base64 对象进行解密,解密模式为 CBC,填充方式为 ZeroPadding。将解密后的结果转换为 UTF-8 编码格式的字符串并返回。

        总的来说,这段代码实现了对字符串的 AES 加密和解密操作,使用了 CryptoJS 库提供的加密算法和相关函数。通过传入不同的参数,可以实现自定义密钥和向量进行加密解密的功能

六、实现结果

{"reqID": 1,"method": "","sender": "ss-smartdata-web-browser Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0","token": "Bearer eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE3MTg5MzkzNzl9.FcPMd60PZQJ8TBrkRZLf3gL76Vr1w9paSfgZPYZlDKNT0h74_zF8Wa8DwrB0Gciw-zneaBtUc1CBAa6yF2RmRQ","reqData": "cThk+vaRL/B41LkCUVrshuT0R8xfnKGh56D+NeqC6HC2PdfcWwFL5RU3gY/sjsc39e156rj29ryANn1LZ29pL5bfN6EIq2oVuqC34GQeu7g="
}

{result: 1, reqID: -1, respID: -1, sender: "", sendee: "", msg: "请求成功",…
}msg : "请求成功"
reqID : -1
respData:"nWOD3Sw2iLf1xevNYnHuHGvPFr/TAJpviyh/M22qLYuiaHoqvxhCn9OF2WGitiKr+LxTbEJvuBsbCmetAPCo7b48WG0PzsHz3fQxlOsQKeh629Z18gyBKho4zEAayHHjhP4rrHjSNNTwoZzAD0n6Cj2HTx+COXH9KtL905HZC3y0+mn1n72BCN2nxVExAu+8cBP+N66MJWVQLyj+7ENWJ0Euq22v3xWWoX2fFKe2XZr8Y7taMRkSNEyfYpgyq3Tl1az7A3I6+eYdpuYndBlxe0m7K6qOgckjni4l9ApIN16P7947Y5LXPY3wHsKVs1t0NKERnNzUTE/aD5einOv/pE3HdZAujtigOHwmihwrhHbTeumSYTC02kbtKUHNlR3lw+GsKR/727sCvkNhYnqz3uXjfJHYmghJJjL20XmPzpr5GJaohr3f4ZcoABv5G3dy"
respID : -1
result : 1
sendee : ""
sender: ""

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

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

相关文章

基于MATLAB仿真LFM线性调频信号

基于MATLAB仿真LFM线性调频信号 目录 前言 一、LFM信号简介 二、LFM信号基本原理 三、LFM信号仿真 四、代码 总结 前言 仿真中的接收信号&#xff0c;有时为了简单会直接用一个正弦波代替&#xff0c;但实际中接收到的信号极少是点频信号&#xff0c;一般都是PSK信号、OF…

MySQL事务、数据库的存储引擎

目录 1. 事务的概念 1.1事务的 ACID 2. 存储引擎 2.1 存储引擎 2.2 常用存储引擎 3.MyISAM 3.1 MyISAM 表支持 3 种不同的存储格式​编辑 3.2 适用的生产场景 4. InnoDB 4.1 InnoDB特点 4.2 适用生产场景分析 5. MyISAM 和 InnoDB 的区别 6. 命令操作 7. 死锁 7.…

计算机系统基础实训八—ProxyLab实验

实验目的与要求 1、让学生应用套接字接口实现网络编程&#xff1b; 2、让学生理解Web服务器开发的相关知识&#xff1b; 3、让学生应用并发编程技术进行并发服务器的开发&#xff1b; 实验原理与内容 Web代理是一种在Web浏览器和终端服务器之间充当中介角色的程序。在Web代…

堆排序的实现原理

一、什么是堆排序&#xff1f; 堆排序就是将待排序元素以一种特定树的结构组合在一起&#xff0c;这种结构被称为堆。 堆又分为大根堆和小根堆&#xff0c;所谓大根堆即为所有的父节点均大于子节点&#xff0c;但兄弟节点之间却没有什么严格的限制&#xff0c;小根堆恰恰相反&a…

高通安卓12-在源码中查找应用的方法

1.通过搜索命令查找app 一般情况下&#xff0c;UI上看到的APP名称会在xml文件里面定义出来&#xff0c;如 搜索名字为WiGig的一个APP 执行命令 sgrep "WiGig" 2>&1|tee 1.log 将所有的搜索到的内容打印到log里面 Log里面会有一段内容 在它的前面是这段内…

Stable Diffusion部署教程,开启你的AI绘图之路

本文环境 系统&#xff1a;Ubuntu 20.04 64位 内存&#xff1a;32G 环境安装 2.1 安装GPU驱动 在英伟达官网根据显卡型号、操作系统、CUDA等查询驱动版本。官网查询链接https://www.nvidia.com/Download/index.aspx?langen-us 注意这里的CUDA版本&#xff0c;如未安装CUD…

破碎的像素地牢探险:游戏分享

软件介绍 《破碎的像素地牢》是开源一款地牢冒险探索类的游戏&#xff0c;融合了日系RPG经典风格&#xff0c;玩家将控制主角进行未知场景的探索。除了经典地牢玩法外&#xff0c;游戏还添加了更多创意内容&#xff0c;如黑屏状态前的挑战性等&#xff0c;使得游戏更加富有挑战…

Vue78-缓存路由组件

一、需求 路由切走的时候&#xff0c;组件会被销毁&#xff0c;路由切回来&#xff0c;组件被挂载&#xff01; 需要&#xff1a;路由切走的时候&#xff0c;组件不会被销毁。 二、代码实现 若是不加include属性&#xff0c;则在<router-view>里面展示的路由&#xff0c…

高通安卓12-Input子系统

1.Input输入子系统架构 Input Driver(Input设备驱动层)->Input core(输入子系统核心层)->Event handler(事件处理层)->User space(用户空间) 2.getevent获取Input事件的用法 getevent 指令用于获取android系统中 input 输入事件&#xff0c;比如获取按键上报信息、获…

深入理解Python中的并发与异步的结合使用

​ 在上一篇文章中&#xff0c;我们讨论了异步编程中的性能优化技巧&#xff0c;并简单介绍了trio和curio库。今天&#xff0c;我们将深入探讨如何将并发编程与异步编程结合使用&#xff0c;并详细讲解如何利用trio和curio库优化异步编程中的性能。 文章目录 并发与异步编程的区…

【数据结构与算法】二叉树的性质 详解

在二叉树的第i层上至多有多少个结点。 在二叉树的第 i 层上至多有 2 i − 1 2^{i-1} 2i−1 个结点(i≥1)。 深度为 K的二叉树至多有多少个结点。 深度为 k 的二叉树上至多含 2 k − 1 2^k - 1 2k−1 个结点(k≥1)。 在一颗二叉树中, 其叶子结点数n0和度为二的结点数n2之间…

安装CDH时报错:Parcel 不可用于操作系统分配 RHEL7,原因与解决办法~

报错信息&#xff1a; 解决办法与思路&#xff1a; 1、检查CDH包的后缀名称&#xff0c;Redhat与Centos安装时不需要修改后缀名称&#xff0c;麒麟系统安装时才需要修改。 2、目录里面需要有xxx.parcel xxx.parcel.sha manifest.json 三个文件 缺一不可&#xff08;注&#x…

Transformer预测 | 基于Transformer的锂电池寿命预测(Pytorch,CALCE数据集)

文章目录 文章概述模型描述程序设计参考资料文章概述 Pytorch实现基于Transformer 的锂电池寿命预测,环境为pytorch 1.8.0,pandas 0.24.2 随着充放电次数的增加,锂电池的性能逐渐下降。电池的性能可以用容量来表示,故寿命预测 (RUL) 可以定义如下: SOH(t)=CtC0100%, 其中,…

HarmonyOS Next 系列之可移动悬浮按钮实现(六)

系列文章目录 HarmonyOS Next 系列之省市区弹窗选择器实现&#xff08;一&#xff09; HarmonyOS Next 系列之验证码输入组件实现&#xff08;二&#xff09; HarmonyOS Next 系列之底部标签栏TabBar实现&#xff08;三&#xff09; HarmonyOS Next 系列之HTTP请求封装和Token…

MQ~消息队列能力、AMQP协议、现有选择(Kafka、RabbitMQ、RocketMQ 、Pulsar)

消息队列 消息队列看作是一个存放消息的容器&#xff0c;当我们需要使用消息的时候&#xff0c;直接从容器中取出消息供自己使用即可。由于队列 Queue 是一种先进先出的数据结构&#xff0c;所以消费消息时也是按照顺序来消费的。 常⽤的消息队列主要这 五 种&#xff0c;分别…

使用 DISPATCHERS 进行 Blueprint 之间的通信

文章目录 初始准备DISPATCHERS 的创建和绑定实现效果 初始准备 首先 UE5 默认是不提供 静态网格体编辑器也就是 Modeling Mode 的&#xff0c;这里需要从插件中添加 Modeling Tools Editor Mode 进入 Modeling Mode 模式&#xff0c;创建一个正方体 然后利用 PolyGroup Edit 和…

Vue79-路由组件独有的2个新的生命周期钩子

一、需求 news.vue路由组件被缓存了&#xff08;因为想要保留里面的输入框的数据&#xff01;&#xff09;&#xff0c;导致&#xff0c;路由页面切走&#xff0c;组件也不会被销毁&#xff0c;所以&#xff0c;beforeDestroy()函数就不会被执行&#xff0c;所以&#xff0c;定…

React+TS前台项目实战(十二)-- 全局常用组件Toast封装,以及rxjs和useReducer的使用

文章目录 前言Toast组件1. 功能分析2. 代码详细注释&#xff08;1&#xff09;建立一个reducer.ts文件&#xff0c;用于管理状态数据&#xff08;2&#xff09;自定义一个清除定时器的hook&#xff08;3&#xff09;使用rxjs封装全局变量管理hook&#xff08;4&#xff09;在to…

在scrapy中使用Selector提取数据

经院吉吉&#xff1a; 首先说明一下&#xff0c;在scrapy中使用选择器是基于Selector这个对象滴&#xff0c;selector对象在scrapy中通过XPATH或是CSS来提取数据的&#xff0c;我们可以自己创建selector对象&#xff0c;但在实际开发中我们不需要这样做&#xff0c;因为respons…

御道源码(ruoyi-vue-pro)个人使用小结

御道源码&#xff08;ruoyi-vue-pro&#xff09;个人使用小结 一、Git地址 1、平台项目简介及地址 2、开发指南&#xff0c;如图所示&#xff0c;部分功能需要收费&#xff0c;可自行了解 二、项目文件夹结构示例&#xff1a; 三、技术介绍 1.基于 Spring Boot MyBatis P…