系列二、RuoYi前后端分离(登录密码加密去除公钥)

news2024/11/20 1:50:01

一、问题描述

系列一虽然实现了登录时密码加密,但是/getPublicKey返回的结果中,把私钥也返回了,这样显然是不合理的,如下:

 二、后端代码修改

2.1、RSAUtil

package com.tssl.business.utils;

import org.apache.commons.codec.binary.Base64;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

@Component
public class RSAUtil {
    // Rsa 私钥 也可固定秘钥对 若依原写法(不安全)
    public static String privateKeys = "";
    private static String publicKeyStr = "";
    private static String privateKeyStr = "";
    private static String myPublicKeyStr = "";
    private static String myPrivateKeyStr = "";
    private static final RSAKeyPair rsaKeyPair = new RSAKeyPair();
    private static final MyRSAPublicKey myRSAPublicKey = new MyRSAPublicKey();
    private static final MyRSAPrivateKey myRSAPrivateKey = new MyRSAPrivateKey();


    /**
     * 私钥解密
     *
     * @param text 待解密的文本
     * @return 解密后的文本
     */
    public static String decryptByPrivateKey(String text) throws Exception {
        return decryptByPrivateKey(rsaKeyPair.getPrivateKey(), text);
    }

    /**
     * 私钥解密
     *
     * @param privateKeyString 私钥
     * @param text             待解密的文本
     * @return 解密后的文本
     */
    public static String decryptByPrivateKey(String privateKeyString, String text) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] result = cipher.doFinal(Base64.decodeBase64(text));
        return new String(result);
    }

    /**
     * 私钥解密
     *
     * @param text 待解密的文本
     * @return 解密后的文本
     */
    public static String myDecryptByPrivateKey(String text) throws Exception {
        return myDecryptByPrivateKey(myRSAPrivateKey.getPrivateKey(), text);
    }

    /**
     * 私钥解密
     *
     * @param privateKeyString 私钥
     * @param text             待解密的文本
     * @return 解密后的文本
     */
    private static String myDecryptByPrivateKey(String privateKeyString, String text) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] result = cipher.doFinal(Base64.decodeBase64(text));
        return new String(result);
    }

    /**
     * 公钥解密
     *
     * @param publicKeyString 公钥
     * @param text            待解密的信息
     * @return 解密后的文本
     */
    public static String decryptByPublicKey(String publicKeyString, String text) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(Base64.decodeBase64(text));
        return new String(result);
    }

    /**
     * 私钥加密
     *
     * @param privateKeyString 私钥
     * @param text             待加密的信息
     * @return 加密后的文本
     */
    public static String encryptByPrivateKey(String privateKeyString, String text) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] result = cipher.doFinal(text.getBytes());
        return Base64.encodeBase64String(result);
    }

    /**
     * 公钥加密
     *
     * @param publicKeyString 公钥
     * @param text            待加密的文本
     * @return 加密后的文本
     */
    public static String encryptByPublicKey(String publicKeyString, String text) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(text.getBytes());
        return Base64.encodeBase64String(result);
    }

    public static MyRSAPublicKey generatePublicKey() {
        return myRSAPublicKey;
    }

    /**
     * 构建RSA密钥对
     *
     * @return 生成后的公私钥信息
     */
    @Bean
    public void generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
        String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
        rsaKeyPair.setPrivateKey(privateKeyString);
        rsaKeyPair.setPublicKey(publicKeyString);
        myRSAPublicKey.setPublicKey(publicKeyString);
        myRSAPrivateKey.setPrivateKey(privateKeyString);
        publicKeyStr = publicKeyString;
        privateKeyStr = privateKeyString;
        myPublicKeyStr = publicKeyString;
        myPrivateKeyStr = privateKeyString;
    }


    public static String getPublicKey() {
        return publicKeyStr;
    }

    public static String getPrivateKey() {
        return privateKeyStr;
    }

    public static String getMyPublicKeyStr() {
        return myPublicKeyStr;
    }

    public static String getMyPrivateKeyStr() {
        return myPrivateKeyStr;
    }

    public static RSAKeyPair rsaKeyPair() {
        return rsaKeyPair;
    }

    /**
     * RSA密钥-私钥对象
     */
    public static class RSAKeyPair {
        private String publicKey;
        private String privateKey;

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

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

        public RSAKeyPair() {

        }

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

        public String getPublicKey() {
            return publicKey;
        }

        public String getPrivateKey() {
            return privateKey;
        }
    }

    /**
     * RSA密钥-公钥对象
     */
    public static class MyRSAPrivateKey {
        private String privateKey;

        public MyRSAPrivateKey() {
        }

        public MyRSAPrivateKey(String privateKey) {
            this.privateKey = privateKey;
        }

        public String getPrivateKey() {
            return privateKey;
        }

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

        @Override
        public String toString() {
            return "MyRSAPrivateKey{" +
                    "privateKey='" + privateKey + '\'' +
                    '}';
        }
    }

    public static class MyRSAPublicKey {
        private String publicKey;

        public MyRSAPublicKey() {
        }

        public MyRSAPublicKey(String publicKey) {
            this.publicKey = publicKey;
        }

        public String getPublicKey() {
            return publicKey;
        }

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

        @Override
        public String toString() {
            return "MyRSAPublicKey{" +
                    "publicKey='" + publicKey + '\'' +
                    '}';
        }
    }
}

说明:

        RSAUtil中添加了@Component注解,generateKeyPair()构建秘钥对添加了@Bean注解,在项目启动时通过@Bean的方式将普通类实例化到Spring容器中,所以当系统启动后,每次调用接口得到的公钥&私钥是一样的,服务重启后,公钥&私钥重新生成。

2.2、SysLoginController提供获取公钥接口

/**
 * 获取公钥:前端用来密码加密
 * @return
 */
@GetMapping("/getPublicKey")
public RSAUtil.MyRSAPublicKey getPublicKey() {
    return RSAUtil.generatePublicKey();
}

2.3、SecurityConfig配置白名单

.antMatchers("/getPublicKey").permitAll()

2.4、Postman调用接口获取publicKey

 2.5、SysLoginService.java#login

/**
 * 登录验证
 *
 * @param username 用户名
 * @param password 密码
 * @param code 验证码
 * @param uuid 唯一标识
 * @return 结果
 */
public String login(String username, String password, String code, String uuid)
{
	// 验证码校验
//        validateCaptcha(username, code, uuid);
	// 登录前置校验
	loginPreCheck(username, password);
	// 用户验证
	Authentication authentication = null;
	try
	{
		UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, RSAUtil.decryptByPrivateKey(password));
		AuthenticationContextHolder.setContext(authenticationToken);
		// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
		authentication = authenticationManager.authenticate(authenticationToken);
	}
	catch (Exception e)
	{
		if (e instanceof BadCredentialsException)
		{
			AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
			throw new UserPasswordNotMatchException();
		}
		else
		{
			AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
			throw new ServiceException(e.getMessage());
		}
	}
	finally
	{
		AuthenticationContextHolder.clearContext();
	}
	AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
	LoginUser loginUser = (LoginUser) authentication.getPrincipal();
	recordLoginInfo(loginUser.getUserId());
	// 生成token
	return tokenService.createToken(loginUser);
}

 

 2.6、SysProfileController重置密码接口修改

@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
public AjaxResult updatePwd(String oldPassword, String newPassword) throws Exception {
	LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
	String userName = loginUser.getUsername();
	//加密后的
	String password = loginUser.getPassword();
	//解密
	oldPassword = RSAUtil.decryptByPrivateKey(oldPassword);
	newPassword = RSAUtil.decryptByPrivateKey(newPassword);
	//拿原密码和加密后的解密
	if (!SecurityUtils.matchesPassword(oldPassword, password)) {
		return AjaxResult.error("修改密码失败,旧密码错误");
	}
	if (SecurityUtils.matchesPassword(newPassword, password)) {
		return AjaxResult.error("新密码不能与旧密码相同");
	}
	if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) {
		// 更新缓存用户密码
		loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
		tokenService.setLoginUser(loginUser);
		return AjaxResult.success();
	}
	return AjaxResult.error("修改密码异常,请联系管理员");
}

 

 ==========================O(∩_∩)O 后端修改over O(∩_∩)O==========================

三、前端代码修改

3.1、login.js添加获取公钥的接口

import request from '@/utils/request'
 
// 获取公钥
export function getPublicKey() {
  return request({
    url: '/getPublicKey',
    method: 'get',
  })
}
 
// 登录方法
export function login(username, password, code, uuid) {
  const data = {
    username,
    password,
    code,
    uuid
  }
  return request({
    url: '/login',
    headers: {
      isToken: false
    },
    method: 'post',
    data: data
  })
}
 
// 注册方法
export function register(data) {
  return request({
    url: '/register',
    headers: {
      isToken: false
    },
    method: 'post',
    data: data
  })
}
 
// 获取用户详细信息
export function getInfo() {
  return request({
    url: '/getInfo',
    method: 'get'
  })
}
 
// 退出方法
export function logout() {
  return request({
    url: '/logout',
    method: 'post'
  })
}
 
// 获取验证码
export function getCodeImg() {
  return request({
    url: '/captchaImage',
    headers: {
      isToken: false
    },
    method: 'get',
    timeout: 20000
  })
}

3.2、 user.js修改登录方法

import { getToken, setToken, removeToken } from '@/utils/auth'
import { login, logout, getInfo, getPublicKey } from '@/api/login'
import {encrypt} from "../../utils/jsencrypt";
 
const user = {
  state: {
    token: getToken(),
    name: '',
    avatar: '',
    roles: [],
    permissions: []
  },
 
  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },
    SET_NAME: (state, name) => {
      state.name = name
    },
    SET_AVATAR: (state, avatar) => {
      state.avatar = avatar
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles
    },
    SET_PERMISSIONS: (state, permissions) => {
      state.permissions = permissions
    }
  },
 
  actions: {
    getPublicKey() {
      return new Promise((resolve, reject) => {
        getPublicKey()
          .then(res => {
            resolve(res)
          })
          .catch(error => {
            reject(error)
          })
      })
    },
    // 登录
    Login({ commit, dispatch }, userInfo) {
      return new Promise((resolve, reject) => {
        dispatch('getPublicKey').then(res => {
          let publicKey = res.publicKey
          const username = userInfo.username.trim()
          //调用加密方法(传密码和公钥)
          const password = encrypt(userInfo.password, publicKey)
          const code = userInfo.code
          const uuid = userInfo.uuid
          login(username, password, code, uuid)
            .then(res => {
              setToken(res.token)
              commit('SET_TOKEN', res.token)
              resolve()
            })
            .catch(error => {
              reject(error)
            })
        })
      })
    },
 
    // 获取用户信息
    GetInfo({ commit, state }) {
      return new Promise((resolve, reject) => {
        getInfo().then(res => {
          const user = res.user
          const avatar = (user.avatar == "" || user.avatar == null) ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
          if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
            commit('SET_ROLES', res.roles)
            commit('SET_PERMISSIONS', res.permissions)
          } else {
            commit('SET_ROLES', ['ROLE_DEFAULT'])
          }
          commit('SET_NAME', user.userName)
          commit('SET_AVATAR', avatar)
          resolve(res)
        }).catch(error => {
          reject(error)
        })
      })
    },
 
    // 退出系统
    LogOut({ commit, state }) {
      return new Promise((resolve, reject) => {
        logout(state.token).then(() => {
          commit('SET_TOKEN', '')
          commit('SET_ROLES', [])
          commit('SET_PERMISSIONS', [])
          removeToken()
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
 
    // 前端 登出
    FedLogOut({ commit }) {
      return new Promise(resolve => {
        commit('SET_TOKEN', '')
        removeToken()
        resolve()
      })
    }
  }
}
 
export default user

 

 注意事项:注意上方的导包

3.3、修改resetPwd.vue

<template>
  <el-form ref="form" :model="user" :rules="rules" label-width="80px">
    <el-form-item label="旧密码" prop="oldPassword">
      <el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password/>
    </el-form-item>
    <el-form-item label="新密码" prop="newPassword">
      <el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password/>
    </el-form-item>
    <el-form-item label="确认密码" prop="confirmPassword">
      <el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password/>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" size="mini" @click="submit">保存</el-button>
      <el-button type="danger" size="mini" @click="close">关闭</el-button>
    </el-form-item>
  </el-form>
</template>
 
<script>
import { updateUserPwd } from "@/api/system/user";
import { getPublicKey } from '@/api/login';
import { encrypt } from '@/utils/jsencrypt';
 
export default {
  data() {
    const equalToPassword = (rule, value, callback) => {
      if (this.user.newPassword !== value) {
        callback(new Error("两次输入的密码不一致"));
      } else {
        callback();
      }
    };
    return {
      user: {
        oldPassword: undefined,
        newPassword: undefined,
        confirmPassword: undefined
      },
      // 表单校验
      rules: {
        oldPassword: [
          { required: true, message: "旧密码不能为空", trigger: "blur" }
        ],
        newPassword: [
          { required: true, message: "新密码不能为空", trigger: "blur" },
          { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }
        ],
        confirmPassword: [
          { required: true, message: "确认密码不能为空", trigger: "blur" },
          { required: true, validator: equalToPassword, trigger: "blur" }
        ]
      }
    };
  },
  methods: {
    getPublicKey() {
      return new Promise((resolve, reject) => {
        getPublicKey()
          .then(res => {
            resolve(res)
          })
          .catch(error => {
            reject(error)
          })
      })
    },
    submit() {
      this.$refs["form"].validate(valid => {
        if (valid) {
          this.getPublicKey().then(res=>{
            let publicKey = res.publicKey
            console.log("res.publicKey",res.publicKey)
            const oldPassword = encrypt(this.user.oldPassword, publicKey)
            const newPassword = encrypt(this.user.newPassword, publicKey)
            updateUserPwd(oldPassword, newPassword).then(
              response => {
                this.msgSuccess("修改成功");
              }
            );
          })
 
        }
      });
    },
    close() {
      this.$tab.closePage();
    }
  }
};
</script>

 3.4、jsencrypt.js更改加密方法

前置条件:npm install jsencrypt

import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
 
// 密钥对生成 http://web.chacuo.net/netrsakeypair
 
//这里注掉了原来固定的公私钥
//const publicKey = ''
//const privateKey = ''
 
// 加密
export function encrypt(txt, publicKey) {
  const encryptor = new JSEncrypt()
  encryptor.setPublicKey(publicKey) // 设置公钥
  return encryptor.encrypt(txt) // 对数据
}
 
// 解密(暂无使用)
export function decrypt(txt) {
  const encryptor = new JSEncrypt()
  encryptor.setPrivateKey(privateKey) // 设置私钥
  return encryptor.decrypt(txt) // 对数据进行解密
}

四、启动后端&前端服务,验证登录密码是否加密

 

 五、完整代码

链接:https://pan.baidu.com/s/1zPDvu9o3qUMS6Qq0DJDchw?pwd=yyds 
提取码:yyds 

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

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

相关文章

STM32单片机蓝牙APP LORA无线远程火灾报警温度烟雾监控系统

实践制作DIY- GC0145蓝牙APP LORA无线远程火灾报警 基于STM32单片机设计---蓝牙APP LORA无线远程火灾报警 二、功能介绍&#xff1a; 1个主机&#xff1a;STM32F103C系列单片机LCD1602显示器蜂鸣器 LORA无线模块3个按键&#xff08;设置、加、减&#xff09;HC-05蓝牙模块&…

Node包管理工具

包管理工具 package代表了一组特定功能的源码集合。 管理包的应用软件&#xff0c;可以对包进行下载安装&#xff0c;更新&#xff0c;删除&#xff0c;上传等操作 借助包管理工具&#xff0c;可以快速开发项目&#xff0c;提高开发效率 前端常用包管理工具 npm Node Pack…

【算法系列 | 3】深入解析排序算法之——选择排序

序言 你只管努力&#xff0c;其他交给时间&#xff0c;时间会证明一切。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用来标记二级论点 决定开一个算法专栏&#xff0c;希望能帮助大…

基于Hexo和Butterfly创建个人技术博客,(3) 创建博客文章及文章模板配置

Hexo官司网查看 这里 笔者个人站查看 这里 特别说明&#xff1a; hexo博客站点发布的文件全是静态文件&#xff0c;没有任何后台服务。博文的发布过程是&#xff1a;1、在本地用hexo new命令创建.md文件----2、经hexo g命令生成.html文件-----3、再通过hexo d命令发布到远程主机…

知网CN期刊《才智》简介及投稿邮箱

《才智》杂志成立于2001年&#xff0c;隶属吉林省人事厅&#xff0c;是经国家新闻出版总署批准的&#xff0c;吉林省人事系统唯一一本面向全国公开发行的杂志。是一本专业发表各类论文评定职称的省级理论性杂志。以挖掘各行各业拔尖人才、促进科教兴国、振兴人才市场为己任&…

python套接字(二):实现一个服务器和多客户端连接

文章目录 前言一、问题二、实现一个服务器连接多个客户端1、问题分析2、代码实现a、服务器端b、客户端 3、运行 前言 在上一篇博客python套接字(一)&#xff1a;socket的使用简单说明了一下套接字的使用&#xff0c;也实现了使用套接字来传输消息&#xff0c;但是也有一个问题…

深度学习应用篇-推荐系统[12]:经典模型-DeepFM模型、DSSM模型召回排序策略以及和其他模型对比

【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍&#xff1a;【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化…

JavaWeb笔记(五)

JavaWeb后端 经过前面的学习&#xff0c;现在终于可以正式进入到后端的学习当中&#xff0c;不过&#xff0c;我们还是需要再系统地讲解一下HTTP通信基础知识&#xff0c;它是我们学习JavaWeb的基础知识&#xff0c;我们之前已经学习过TCP通信&#xff0c;而HTTP实际上是基于T…

使用SonarLint在开发阶段提高代码质量

使用SonarLint在开发阶段提高代码质量 SonarLint是什么 SonarLint是一个免费的IDE插件&#xff0c;是一个代码质量工具。 它可以在我们编写代码的时候&#xff0c;就帮我我们发现问题并提醒我们。可以帮助我们养成良好的代码习惯。 它支持5000条规则&#xff0c;可以帮助我…

如何在Microsoft Excel中使用MATCH查找值的位置

当你需要在电子表格中查找值的确切位置时,可以使用 Excel 中的 MATCH 函数。这样可以避免你手动搜索可能需要参考的位置或其他公式。 MATCH 函数通常与 INDEX 函数一起用作高级查找。但在这里,我们将介绍如何单独使用 MATCH 来找到价值所在。 一、Excel中的MATCH函数是什么 …

11. Synchronized与锁升级

11.1 面试题 ● 谈谈你对Synchronized的理解 ● Sychronized的锁升级你聊聊 ● Synchronized实现原理&#xff0c;monitor对象什么时候生成的&#xff1f;知道monitor的monitorenter和monitorexit这两个是怎么保证同步的嘛&#xff1f;或者说这两个操作计算机底层是如何执行的 …

【企业业务架构】LEANIX : 业务能力

业务能力是组织执行核心功能所需的能力、材料和专业知识的表达或发声。企业架构师使用业务能力来说明业务的总体需求&#xff0c;以便更好地制定满足这些业务需求的 IT 解决方案。 目录 介绍业务能力建模您可以通过业务能力映射实现什么&#xff1f;并购管理IT风险管理创新管理…

手把手教你入门 Docker

博主介绍&#xff1a; ✌博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家✌ Java知识图谱点击链接&#xff1a;体系化学习Java&#xff08;Java面试专题&#xff09; &#x1f495;&#x1f495; 感兴趣的同学可以收…

DAY 76 分布式监控平台:zabbix

市场上常用的监控软件&#xff1a; 传统运维&#xff1a;zabbix、 Nagios云原生环境&#xff1a; Prometheus &#xff08;go语言开发的&#xff09; zabbix概述 作为一个运维&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数据去…

国内云服务器全面对比

想要领取优惠券购买云服务可以前往我的云服务器领券购买。 经过疫情三年&#xff0c;大多行业开始复苏&#xff0c;企业开始布局以后得发展&#xff0c;云服务器作为企业发展几乎是必须的&#xff0c;一个企业从无到有&#xff0c;要经历很多&#xff0c;比如企业官网搭建&…

GaussDB云数据库SQL应用系列—索引管理

目录 一、前言 二、注意事项 三、索引创建 1、创建普通索引 2、创建唯一索引 3、创建多字段索引 4、创建部分索引 5、创建表达式索引 四、索引管理 1、查看索引信息 2、删除索引 总结 一、前言 随着互联网的快速发展&#xff0c;数据量呈现爆炸式增长。如何高效地…

PLC现场安装时需要注意的几个关键点

PLC适用于大多数工业现场&#xff0c;但它对使用场合、环境温度等还是有一定要求。控制PLC的工作环境&#xff0c;可以有效地提高它的工作效率和寿命。 在安装PLC时&#xff0c;要避开下列场所&#xff1a; 1.环境温度超过0 ~ 50℃的范围&#xff1b; 2.相对湿度超过85%或者…

Coggle 30 Days of ML 打卡任务二:苹果病害数据加载与数据增强

Coggle 30 Days of ML 打卡任务二&#xff1a;苹果病害数据加载与数据增强 任务二&#xff1a;苹果病害数据加载与数据增强 难度/分值&#xff1a;中/2 打卡内容&#xff1a; 参赛选手名称&#xff1a;AppleDoctor完成日期&#xff1a;2023.6.9任务完成情况&#xff1a; 使…

第四章 完型填空

第四章 完型填空 第一节 真题 2020-完型填空- Section I Use of English Directions&#xff1a; Read the following text. Choose the best word (s) for each numbered blank and mark A, B, C or D on the ANSWER SHEET. (10 points) Being a good parent is, of cour…

Vue中使用editor.md(1):简单使用

0. 背景 在Vue项目中添加一个markdown编辑器&#xff0c;选择使用editor.md&#xff0c;记录在Vue项目中的简单使用。 1. 环境配置 1.1 下载editor.md 官网地址&#xff1a;http://pandao.github.io/editor.md/ 项目文件解压后放入&#xff1a;public/static/内 1.2 下…