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

news2024/11/28 4:53:06

目录

一、加密知识回顾 

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-tabs
          size="large"
          :tabBarStyle="{ textAlign: 'center' }"
          style="padding: 0 2px"
          @change="userLoginTypeChange"
        >
          <a-tab-pane tab="账户密码登录" key="1">
            <a-alert
              type="error"
              :closable="true"
              v-show="error"
              :message="error"
              showIcon
              style="margin-bottom: 24px"
            />
            <a-form-item>
              <a-input
                ref="userNameRef"
                autocomplete="autocomplete"
                size="large"
                allowClear
                placeholder="登录用户名"
                v-decorator="[
                  'name',
                  {
                    rules: [
                      {
                        required: true,
                        message: '请输入账户名',
                        whitespace: true,
                      },
                    ],
                  },
                ]"
              >
                <a-icon slot="prefix" type="user" />
              </a-input>
            </a-form-item>
            <a-form-item>
              <a-input
                size="large"
                placeholder="登录密码"
                autocomplete="autocomplete"
                type="password"
                allowClear
                v-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-input
                size="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-button
                    style="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.coloradmin.cn/o/1851544.html

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

相关文章

JDK17特性

JDK17特性 一、JAVA17概述 JDK 16 刚发布半年(2021/03/16),JDK 17 又如期而至(2021/09/14),这个时间点特殊,蹭苹果发布会的热度?记得当年 JDK 15 的发布也是同天。 Oracle 宣布,从 JDK 17 开始,后面的 JDK 带附加条款免费提供!!!Java 17+ 可以免费使用了,包括商…

当Windows台式电脑或笔记本电脑随机关机时,请先从这8个方面检查

序言 你的Windows笔记本电脑或PC是否意外关闭?笔记本电脑电池故障、电源线松动、过热、电源设置错误、驱动程序过时或电脑组件故障等问题都可能是罪魁祸首。如果你对这个问题感到沮丧,试试这些解决方案。 进行一些初步检查 与从电池中获取电力的笔记本电脑不同,台式电脑依…

Vue3 + TS + Antd + Pinia 从零搭建后台系统(四) ant-design-vue Layout布局,导航栏,标签页

书接上回本篇主要介绍&#xff1a; Layout布局&#xff0c;导航栏&#xff0c;标签页继续填充目录 按需引入组件Layout布局&#xff0c;导航栏&#xff0c;标签页css样式 按需引入组件 使用unplugin-vue-components插件完成ant-design-vue组件的按需加载。 前文中已处理过&…

调试器烧录失败的几种常见解决办法

目录 1. 检查接线、Keil配置是否正确 2. 降低下载速度 3. SWD引脚被禁用或被复用为其他功能 4. 使用CubeMX生成的工程&#xff0c;无法调试&#xff1f; 5. 能识别到芯片但是下载时弹出报错对话框&#xff08;Command not supported&#xff09; 6. 内部flash锁死&#x…

媒体邀约中媒体采访应该如何做?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体宣传加速季&#xff0c;100万补贴享不停&#xff0c;一手媒体资源&#xff0c;全国100城线下落地执行。详情请联系胡老师。 在媒体邀约中&#xff0c;媒体采访应该遵循以下几个步骤和…

用户态协议栈05—架构优化

优化部分 添加了in和out两个环形缓冲区&#xff0c;收到数据包后添加到in队列&#xff1b;经过消费者线程处理之后&#xff0c;将需要发送的数据包添加到out队列。添加数据包解析线程&#xff08;消费者线程&#xff09;&#xff0c;架构分层 #include <rte_eal.h> #inc…

ionic7 从安装 到 项目启动最后打包成 apk

报错处理 在打包的时候遇到过几个问题&#xff0c;这里记录下来两个 Visual Studio Code运行ionic build出错显示ionic : 无法加载文件 ionic 项目通过 android studio 打开报错 capacitor.settings.gradle 文件不存在 说明 由于之前使用的是 ionic 3&#xff0c;当时打包的…

模式分解的概念(上)-分解、无损连接性、保持函数依赖特性

一、分解的概念 1、分解的定义 2、判断一个关系模式的集合P是否为关系模式R的一个分解 只要满足以下三个条件&#xff0c;P就是R的一个分解 &#xff08;1&#xff09;P中所有关系模式属性集的并集是R的属性集 &#xff08;2&#xff09;P中所有不同的关系模式的属性集之间…

【计算机网络体系结构】计算机网络体系结构实验-DHCP实验

服务器ip地址 2. 服务器地址池 3. 客户端ip 4. ping Ipconfig

星闪指向遥控,做家电交互的破壁人

“面壁者罗辑&#xff0c;我是你的破壁人。” 科幻小说《三体》中&#xff0c;当人类的基础科学被三体人封锁&#xff0c;变得停步不前&#xff0c;人类启动了自救的面壁计划&#xff0c;通过一次又一次破壁&#xff0c;找到战胜三体人的办法。 现实中&#xff0c;有一点已经成…

教师数字素养标准

老师们有没有想过如何让自己的课堂更加生动、互动&#xff0c;甚至超越传统的教学模式&#xff1f;是否思考过&#xff0c;数字技术如何成为我们教学的得力助手&#xff0c;让我们的课堂焕发出新的活力&#xff1f; 数字素养&#xff0c;这个听起来充满科技感的词汇&#xff0c…

Github上传大于100M的文件(ubuntu教程)

安装Git-lfs Git Large File Storage (LFS) 使用 Git 内部的文本指针替换音频样本、视频、数据集和图形等大文件&#xff0c;同时将文件内容存储在 GitHub.com 或 GitHub Enterprise 等远程服务器上。官网下载&#xff1a;https://git-lfs.github.com/ ./install.sh上传 比如…

软银CEO孙正义:10年内将出现比人类聪明1万倍的人工智能|TodayAI

2024年6月20日&#xff0c;软银集团公司&#xff08;SoftBank&#xff09;董事长兼首席执行官孙正义在日本东京举行的公司年度股东大会上发表讲话&#xff0c;表示比人类聪明1万倍的人工智能将在10年内出现。这是他近年来一次罕见的公开露面&#xff0c;在会上他质疑了自己的人…

57.SAP MII产品介绍(07)功能详解(06)Workbench-SQLQuery

1.SQLQuery概念 您可以使用SAP Manufacturing Integration and Intelligence&#xff08;SAP MII&#xff09;Workbench中的SQLQuery来创建访问面向SQL的连接器&#xff08;如IDBC连接器&#xff09;的模板。此查询的扩展名为tqsq。 简而言之&#xff0c;SQLQuery就是一段…

Github 2024-06-22 开源项目日报 Top10

根据Github Trendings的统计,今日(2024-06-22统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量TypeScript项目3JavaScript项目2Python项目2HTML项目1Rust项目1Dart项目1Dockerfile项目1Shell项目1C++项目1Swift项目1RustDesk: 用Rust编写的…

数据结构与算法-B(B-)树的简单实现

B(B-)树定义 B树&#xff08;或B-tree&#xff09;是一个在计算机科学中广泛使用的数据结构&#xff0c;它是一种自平衡的树&#xff0c;能够保持数据有序。 以下是B树的特性 每个节点最多右m个孩子&#xff0c;二叉树是B-树的特例&#xff0c;其有2个孩子。除了叶节点和根节点…

探索 Kubernetes v1.30:与 MinIO 部署相关的增强功能

Kubernetes v1.30 的发布带来了一系列更新&#xff0c;其中一些更新对于高性能 Kubernetes 原生对象存储 MinIO 的用户来说可能意义重大。随着组织继续利用这两种技术来提供可扩展且安全的存储解决方案&#xff0c;了解这些新 Kubernetes 功能的影响非常重要。以下是 Kubernete…

华为开发者大会:全场景智能操作系统HarmonyOS NEXT

文章目录 一、全场景智能操作系统 - HarmonyOS NEXT1.1 系统特性1.2 关于架构、体验和生态 二、应用案例2.1 蚂蚁mpaas平台的性能表现 三、新版本应用框架发布3.1 新语言发布3.2 新数据库发布3.3 新版本编译器的发布 四、CodeArts和DataArts4.1 CodeArts4.2 DataArts 五、总结 …

网络富集显著性检验NEST(?)

https://doi.org/10.1002/hbm.26714 背景 一般情况下&#xff0c;研究者通过评估统计量较大的脑区与功能网络重叠的情况&#xff0c;或者计算网络的体素占比&#xff0c;来确定行为和功能网络的相关性。NEST能检测行为表型和大脑表型的相关性是否富集在特定的功能网络中。例如下…