【个人博客系统网站】注册与登录 · 加盐加密验密算法 · 上传头像

news2024/11/27 16:47:50

【JavaEE】进阶 · 个人博客系统(3)

在这里插入图片描述

文章目录

  • 【JavaEE】进阶 · 个人博客系统(3)
    • 1. 加盐加密验密算法原理
      • 1.1 md5加密
      • 1.2 md5验密
      • 1.3 md5缺漏
      • 1.4 加盐加密
      • 1.5 后端的盐值拼接约定
      • 1.6 代码实现
        • 1.6.1 加密
        • 1.6.2 验密
        • 1.6.3 测试
    • 2. 博客注册页
      • 2.1 上传头像
        • 2.1.1 期待效果
        • 2.1.2 约定前后端交互接口
        • 2.1.3 后端代码
        • 2.1.4 前端代码
        • 2.1.5 测试
      • 2.2 注册
        • 2.2.1 期待效果
        • 2.2.2 约定前后端交互接口
        • 2.2.3 后端代码
        • 2.2.4 前端代码
        • 2.2.5 测试
    • 3. 博客登录页
      • 3.1 期待效果
      • 3.2 失焦更新头像
        • 3.2.1 约定前后端交互接口
        • 3.2.2 后端代码
        • 3.2.3 前端代码
        • 3.2.4 测试
      • 3.3 处理url 以及 注册页面跳转
        • 3.3.1 通过key,获取url中的value
        • 3.3.2 将username赋值给用户名输入框
        • 3.3.3 注册页面跳转
        • 3.3.4 测试
      • 3.4 登录功能
        • 3.4.1 约定前后端交互接口
        • 3.4.2 后端代码
        • 3.4.3 前端代码
        • 3.4.4 测试

【JavaEE】进阶 · 个人博客系统(3)

本文章正式进行前后端交互了!

还是一样的老套路:

  1. 根据期待效果约定前后端交互接口
  2. 后端代码
    • 三板斧:校验,处理请求,返回响应
  3. 前端代码
    • 三板斧:校验,发送请求,处理响应

先写后端还是先写前端,个人习惯问题~

大方向就是那三板斧,具体按具体改动~

1. 加盐加密验密算法原理

1.1 md5加密

我们原本通过md5进行加密,这是一个不可逆的加密:

在这里插入图片描述
在这里插入图片描述](https://img-blog.csdnimg.cn/dbcab6e1574148c0ab41849ba13fc77b.gif)

原理就是通过password生成一个 一一对应固定长度的加密密码

1.2 md5验密

为什么不说是解密的,因为这个是一个不可逆的过程,也就是说,如果后端用md5加密后,是无法获取到原密码的,除非你使用“逆天的暴力枚举”

而一个固定的password,生成的是一个固定的加密密码!

这个也是常识,因为我们几乎在任何场景下,都没有遇到过,找回密码是返回原密码的,一般都是通过一些手段验证你的信息,进行修改密码的操作~

所以,后端能做的就是“验证密码”

因为一个固定的password,生成的是一个固定的加密密码!

所以如果密码是正确的话,生成的加密密码也是正确对应的上的!

在这里插入图片描述

1.3 md5缺漏

没错,不良用户/黑客,可以通过“逆天的暴力枚举”,也就是他们总结出来的“彩虹表”:

  • 这个表,记录了很多很多字符串的加密密码,这样如果攻破了数据库的话,这些用户的密码就会被破解出来!

1.4 加盐加密

加盐,这里是比较形象的说法,也就是加点料,让加密密码无规律:

  1. 生成一个全球不重复的随机的盐值
  2. 盐值 + 原密码,进行md5加密,获取加密密码
  3. 将盐值 + 加密密码保存到数据库

而这个盐值,可见就是UUID!

  • 因此,同样的原密码,由于UUID不会重复和md5加密的一一对应,生成的加密密码是不一样的~
  • UUID和md5都是用不完的,底层不需要理解,坐享其成即可,不要杞人忧天~

在这里插入图片描述

这个算法逻辑上是破解的了的:

  1. 攻破数据库后,获取一个杂合密码
  2. 破解出盐值和加密密码(不良用户不知道这个盐值 + 加密码是咋组合的)
  3. 用彩虹表破解加密密码,获取原生组合,破解出原密码
    1. 不良用户不知道这个盐值 + 原密码是咋组合的
    2. 很难映射出这么“主观性这么强,随机性这么强”的原生组合

从逻辑分析上可以看出,破解难度和成本高出的倍数是不能计量的,“逆宇宙级枚举”

但是世界上没有完全的安全,只有你想不到的破解方法,和他们考虑成本是否要进行破解!

补充:加密过程后端是不会记录下来的,这里黑客破解的是持久化的数据

1.5 后端的盐值拼接约定

  1. 盐值跟原密码直接拼接,生成的加密密码
  2. 盐值跟加密密码以:[salt]$[plus password] 格式拼接
    • 这样方便获取盐值

1.6 代码实现

1.6.1 加密

创建一个用户相关的工具类:UserUtils

在这里插入图片描述

public class UserUtils {
    public static String encrypt(String password) {
        // 1. 获取盐值
        String salt = UUID.randomUUID().toString();
        // 2. md5加密
        String plusPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes(StandardCharsets.UTF_8));
        // 3. 将盐值和加密密码组合返回
        return salt + "$" + plusPassword;
    }
}

1.6.2 验密

根据加密原理推算:

验密就是根据数据库里的组合密码:

  1. 获取到[盐值]和[正确的加密密码]
  2. [盐值]拼接[待验证的密码],生成[待验证的加密密码]
  3. 对比[正确的加密密码]和[待验证的加密密码]

根据md5的一一对应,如果对应的上,那就是正确的密码

public static boolean confirm(String password, String dbPassword) {
    // 1. 获取到[盐值]和[正确的加密密码]
    String[] group = dbPassword.split("\\$"); 
    	// 在split函数的参数字符串里,这个$有特殊含义,需要转义一下
    // 2. md5加密
    String plusPassword = DigestUtils.md5DigestAsHex((group[0] + password).getBytes(StandardCharsets.UTF_8));
    // 3. 对比
    return group[1].equals(plusPassword);
}

参数校验调用这些方法之前就确认过了,不必重复~

  • dbPassword必然是正确样式的数据,否则之前就不会添加成功,获取不到也更不会调用这个方法~

1.6.3 测试

public static void main(String[] args) {
    String password = "abcd";
    String dbPassword1 = encrypt(password);
    boolean conf1 = confirm(password, dbPassword1);
    String dbPassword2 = encrypt(password);
    boolean conf2 = confirm(password, dbPassword2);
    System.out.println(password);
    System.out.println(dbPassword1);
    System.out.println(conf1);
    System.out.println(dbPassword2);
    System.out.println(conf2);
}

结果:

在这里插入图片描述

补充:UUID的-建议去除,我的数据库是65位的组合密码,所以得去掉:

  • 这样UUID就是十六进制的三十二位数了
public static String encrypt(String password) {
    // 1. 获取盐值
    String salt = UUID.randomUUID().toString().replace("-", "");
    // 2. md5加密
    String plusPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes(StandardCharsets.UTF_8));
    // 3. 将盐值和加密密码组合返回
    return salt + "$" + plusPassword;
}

2. 博客注册页

2.1 上传头像

2.1.1 期待效果

在这里插入图片描述

之前用的是form表单上传文件,现在我们用Ajax上传文件,这样我们就不会被强制跳转且可以获取传递回来的文件名,更新其显示!

在这里插入图片描述

2.1.2 约定前后端交互接口

后端:

  1. /user/picture
  2. 接受请求中的文件,项目外的D:/blog_userImage目录下
    • 不用导致项目空间占用太大
    • 应该是项目,映射指向机器的某一个位置的资源
  3. 返回文件名(包装成的CommonResult对象)

前端:

  1. /user/picture
  2. multipart/form-data
  3. post
  4. body:文件按钮上传的文件

2.1.3 后端代码

  1. 工具类ImageUtils,通过getImageUniquePath方法,可获取文件保存路径
public class ImageUtils {
    public static String getImageUniquePath(String originName) {
        String path = "blog_userImage/";
        // 获取唯一id
        String id = UUID.randomUUID().toString();
        //获取文件后缀
        String suffix = originName.substring(originName.lastIndexOf("."));
        //拼接
        path += id + suffix;
        return path;
    }
}
  1. controller层,处理请求,调用service层进行数据持久化
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;
    @RequestMapping("/picture")
    public CommonResult picture(@RequestPart("myfile")MultipartFile file) throws IOException {
        if(file == null) {
            return CommonResult.fail(-1, "上传文件失败");
        }
        //获取文件保存路径
        String path = ImageUtils.getImageUniquePath(file.getOriginalFilename());
        //通过文件保存路径,将文件进行保存
        userService.loadImage(file, path);
        //返回文件名(包装成统一对象)
        return CommonResult.success(path);
    }

}
  1. service层,进行数据持久化
@Service
@Slf4j
public class UserService {

    public void loadImage(MultipartFile file, String path) {
        log.info("保存成功:" + path);//保存成功日志
        //保存文件
        try {
            file.transferTo(new File("D:/" + path));//spring mvc是可以直接throws异常的,框架内部/异常处理器有处理,但是多级调用,耦合度有点高
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  1. 配置静态资源映射

对于新增的文件:

  1. 我们的网站能够访问到我们自己的静态资源是因为我们在运行的时候,将这些打包到target里面了,而新增的文件只是在我们开发的时候的路径下,并没有立即加载到target里
  2. 绝对路径也是一样,无论你保存到项目里,还是保存到项目外,都没有加载到target里面,我们也无法手动写入target

而我们的网站,浏览器考虑到安全性,是不能直接访问不在项目target里的静态资源的

而我们spring boot项目与普通maven项目不同,spring boot项目修改静态资源,例如html/css/js等等,必须保存并重启服务器才能更新~

所以我们需要进行,静态资源的映射!

@Configuration
public class MyWebMvcConfigurerAdapter implements WebMvcConfigurer {
    /**
     * 配置静态访问资源
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/blog_userImage/**").addResourceLocations("file:D:/blog_userImage/");
    }

}

含义就是,将“file:D:/blog_userImage/”路径下的静态资源,映射成“/blog_userImage/**”

  • 通过localhost:8080/blog_userImage/**,就可以访问~

并且,访问服务器的路由以这个为准,拦截器配置:

在这里插入图片描述

目录结构:

在这里插入图片描述

2.1.4 前端代码

<div id="fileImage">
    <input
           id="i"
           type="button"
           value="请上传头像"
           onclick="putImage();"
           />
    <input id="f_file" type="file" name="file" style="display: none" />
</div>

putImage(点击普通按钮触发file按钮):

function putImage() {
    javascript: jQuery("input[name='file']").click();
}

file按钮上传成功触发的事件(发送请求):

  • 固定搭配,无需多问~
jQuery("#f_file").change(function (e) {
    // 获取选中的文件
    var file = e.target.files[0];
    // 创建一个 FormData 对象
    var formData = new FormData();
    formData.append("myfile", file);
    // 用 Ajax 向服务器发送文件
    jQuery.ajax({
        url: "/user/picture",
        type: "POST",
        data: formData,
        processData: false, // 告诉 jQuery 不要处理发送的数据
        contentType: false, // 告诉 jQuery 不要设置 Content-Type 请求头
        success: function (res) {
            if (res.code === 200) {
                //修改图像
                var url = "url(" + res.data + ")";
                jQuery("#i").css("background-image", url);
                jQuery("#i").val("");
            } else {
                console.log("上传失败: " + res.msg);
            }
        },
        error: function () {
            console.log("上传失败,请重试!");
        },
    });
});

2.1.5 测试

在这里插入图片描述

2.2 注册

2.2.1 期待效果

输入必选项:昵称,密码,确认密码

  • 确认密码在发送之前进行验证,因为没有确认密码这个字段,也没有必要,这个不需要多说

代码仓库以及头像为非必选

  • 注册成功后,弹框提示自动生成的用户名,跳转到登录页面,并帮助用户填写用户名

而在后端:

  1. 生成一个加密密码
  2. 生成一个用户名

2.2.2 约定前后端交互接口

后端:

  1. /user/register
  2. 返回受影响行数,以及用户名

前端:

  1. body:image,name,password,git
  2. post,json,/user/register

2.2.3 后端代码

  1. UserUtils的获取用户名的方法
public static String getUsername() {
    // 获取当前时间戳
    long timestamp = System.currentTimeMillis();

    // 生成随机数
    Random random = new Random();
    int randomNumber = random.nextInt(100);

    // 结合时间戳和随机数生成唯一标识符
    String identifier = String.valueOf(timestamp) + String.valueOf(randomNumber);

    return identifier;
}

不适用UUID是因为UUID太长了,没啥规律,带字母,而这里我用的是当前时间戳 + 100以内的随机数组成的15位十进制数

如果恶意注册,用户名才可能重复,由于有unique约束,所以会添加失败,受影响行数返回0~

  1. controller层
@RequestMapping("register")
public CommonResult register(@RequestBody UserInfo userInfo) {
    // 1. 校验参数
    if(userInfo == null || !StringUtils.hasLength(userInfo.getName())
       || !StringUtils.hasLength(userInfo.getPassword())) {
        return CommonResult.fail(-1, "非法参数");
    }
    // 2. 生成一个用户名
    String username = UserUtils.getUsername();
    userInfo.setUsername(username);
    // 3. 加密
    userInfo.setPassword(UserUtils.encrypt(userInfo.getPassword()));
    // 4. 请求service的添加数据库操作
    int rows = userService.register(userInfo);
    // 5. 执行结果返回
    Map<String, Object> map = new HashMap<>();
    map.put("rows", rows);
    map.put("username", username);
    return CommonResult.success(map);
}

补充:

在这里插入图片描述

判断字符串为空字符串/null,是的话,返回false

  1. mapper层
    在这里插入图片描述

实现:

  • 由于我需要用到标签,所以注解的方式不太方便,我用xml去实现
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">

    
</mapper>

register实现(非必选项的判断):

<insert id="register">
    insert into userinfo (
        username,
        name,
        <if test="photo != null">
            photo,
        </if>
        <if test="git != null">
            git,
        </if>
        password
    ) values (
        #{username},
        #{name},
        <if test="photo != null">
            #{photo},
        </if>
        <if test="git != null">
            #{git},
        </if>
        #{password}
    )
</insert>
  1. service层
@Autowired
private UserMapper userMapper; 
public int register(UserInfo userInfo) {
    return userMapper.register(userInfo);
}
  1. 拦截器配置

在这里插入图片描述

2.2.4 前端代码

  1. 用户名不能全为空,并且上传时空白符会被去除掉
  2. 密码不能全为空,并且上传时空白符会被去除掉

在这里插入图片描述

实现:

在这里插入图片描述

function register() {
    var name = jQuery("#username");
    var password = jQuery("#password");
    var judge_password = jQuery("#judge_password");
    var photo = jQuery("#i")
    .css("background-image")
    .replace("url(", "")
    .replace(")", "")
    .replace("\"", "")
    .replace("\"", "");//去掉两个引号
    var git = jQuery("#git");

    // 1. 非空校验
    if (name.val().trim() == "") {
        alert("请输入昵称!");
        name.focus();
        return false;
    }
    if (password.val().trim() == "") {
        alert("请输入密码!");
        password.focus();
        return false;
    }
    if (judge_password.val().trim() == "") {
        alert("请输入密码!");
        judge_password.focus();
        return false;
    }
    if (password.val() != judge_password.val()) {
        alert("两次输入密码不一致!");
        return false;
    }
    // 2. 发送请求
    jQuery.ajax({
        url: "/user/register",
        method: "POST",
        contentType: "application/json; charset=utf8",
        data: JSON.stringify({
            name: name.val().trim(),
            password: password.val().trim(),
            photo: photo,
            git: git.val(),
        }),
        // 3. 处理响应
        success: function (body) {
            if (body.code == 200 && body.data.rows == 1) {
                alert("注册成功,请记住你的用户名:" + body.data.username + " !");
                location.href = "blog_login.html?username=" + body.data.username;
            } else {
                alert("注册失败:" + data.msg);
            }
        },
    });
}

2.2.5 测试

在这里插入图片描述

3. 博客登录页

3.1 期待效果

在这里插入图片描述

  1. 用户名输入框聚焦到失焦的时候发送请求给后端,尝试获取用户头像
  2. 根据querystring,如果有用户名将用户名进行赋值,两个输入框都输入值后才能发送登录请求,后端对数据进行校验
  3. 点击注册按钮,跳转到注册页

3.2 失焦更新头像

3.2.1 约定前后端交互接口

后端:

  1. /user/get_photo
  2. 返回头像名

前端:

  1. /user/get_photo
  2. 用户名
  3. json

3.2.2 后端代码

  1. controller层
@RequestMapping("/get_photo")
public CommonResult getPhoto(@RequestBody UserInfo user) {
    String username = user.getUsername();
    UserInfo userInfo = userService.getUserByUsername(username);
    return userInfo != null ? CommonResult.success(userInfo.getPhoto()) : CommonResult.fail(-1, "没有此用户");
}
  1. service层
public UserInfo getUserByUsername(String username) {
    return userMapper.selectByUsername(username);
}
  1. mapper层
@Select("select * from userinfo where username = #{username}")
UserInfo selectByUsername(@Param("username") String username);
  1. 拦截器配置

.excludePathPatterns("/user/get_photo")

3.2.3 前端代码

在这里插入图片描述

给用户名输入框一个失焦事件:

jQuery("#username").blur(function () {
    var username = jQuery("#username");
    if (username.val().trim() != "") {
        jQuery.ajax({
            url: "/user/get_photo",
            method: "post",
            contentType: "application/json; charset=utf8",
            data: JSON.stringify({
                username: username.val().trim(),
            }),
            success: function (body) {
                if (body.code == 200 && body.data != "") {
                    var img = "url(" + body.data + ")";
                    jQuery("#i").css("background-image", img);
                } else {
                    jQuery("#i").css(
                        "background-image",
                        "url(blog_userImage/avatar.png)"
                    );
                }
            },
        });
    }
});

3.2.4 测试

在这里插入图片描述

3.3 处理url 以及 注册页面跳转

3.3.1 通过key,获取url中的value

<script src="js/url_handler.js"></script>
// 根据 key 获取 url 中对应的 value
function getParamValue(key){
    // 1.得到当前url的参数部分
    var params = location.search;
    // 2.去除“?”
    if(params.indexOf("?")>=0){
        params = params.substring(1);
        // 3.根据“&”将参数分割成多个数组
        var paramArray = params.split("&");
        // 4.循环对比 key,并返回查询的 value
        if(paramArray.length>=1){
            for(var i=0;i<paramArray.length;i++){
                // key=value
                var item = paramArray[i].split("=");
                if(item[0]==key){
                    return item[1];
                }
            }
        }
    }
    return null;
}

3.3.2 将username赋值给用户名输入框

jQuery("#username").val(getParamValue("username"));
jQuery("#username").focus();

3.3.3 注册页面跳转

在这里插入图片描述

3.3.4 测试

在这里插入图片描述

3.4 登录功能

3.4.1 约定前后端交互接口

后端:

  1. /user/login
  2. 返回 true / false

前端:

  1. /user/login
  2. 用户名,密码,json,post
  3. true:跳转到所有人的博客列表页;false:弹窗提示

3.4.2 后端代码

  1. controller层

在这里插入图片描述

@RequestMapping("/login")
public CommonResult login(@RequestBody UserInfo userInfo, HttpServletRequest request) {
    //1. 参数校验
    if(userInfo.getUsername() == null || !StringUtils.hasLength(userInfo.getUsername())
       || userInfo.getPassword() == null || !StringUtils.hasLength(userInfo.getPassword())) {
        return CommonResult.fail(-1, "非法参数!");
    }
    //2. 根据用户名查询对象
    UserInfo user = userService.getUserByUsername(userInfo.getUsername());
    if(user == null || user.getId() == 0) {
        return CommonResult.fail(-2, "用户名或者密码错误!");
    }
    //3. 验证密码(左边待测,右边数据库查出来的)
    if(!UserUtils.confirm(userInfo.getPassword(), user.getPassword())) {
        return CommonResult.fail(-2, "用户名或者密码错误!");
    }
    //4. 比较成功,将对象存储到session中
    SessionUtils.setUser(request, user);
    //5. 返回结果
    return CommonResult.success("登录成功");
}
  1. 拦截器配置

.excludePathPatterns("/user/login")

3.4.3 前端代码

在这里插入图片描述

function login() {
    var username = jQuery("#username");
    var password = jQuery("#password");
    // 1. 非空校验
    if (username.val().trim() == "") {
        alert("请输入昵称!");
        username.focus();
        return false;
    }
    if (password.val().trim() == "") {
        alert("请输入密码!");
        password.focus();
        return false;
    }
    // 2. 发送请求
    jQuery.ajax({
        url: "/user/login",
        method: "POST",
        contentType: "application/json; charset=utf8",
        data: JSON.stringify({
            username: username.val().trim(),
            password: password.val().trim(),
        }),
        // 3. 处理响应
        success: function (body) {
            if (body.code == 200) {
                alert("登录成功!");
                location.href = "blog_lists.html";
            } else {
                alert("登录失败:" + body.msg);
            }
        },
    });
}

3.4.4 测试

在这里插入图片描述

可以访问需要登录校验的页面:

在这里插入图片描述


文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆

代码:myblog_system · 游离态/马拉圈2023年9月 - 码云 - 开源中国 (gitee.com)


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

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

相关文章

探究IP路由的工作原理与路由表查找规则

文章目录 一、定义二、IP连通的前提三、路由表1. 作用2. 路由表字段内容3. 路由表查表规则4. 路由信息的来源5. 路由表写表规则6. 路由优先级 四、常用命令 首先可以看下思维导图&#xff0c;以便更好的理解接下来的内容。 一、定义 路由器是网络中负责将数据报文在不同IP网段…

css 左右宽固定,中间自适应——双飞翼布局

最近面试的时候遇到一个提问说&#xff0c;如何做到一个左右宽度固定&#xff0c;中间自适应的布局&#xff0c;我的答案不重要&#xff0c;重要的是不是面试官想听到的答案&#xff0c;这样问大概率他想听到的答案一定是双飞翼布局&#xff0c;所以今天就手敲一个双飞翼布局让…

设计模式-原则篇-01.开闭原则

简介 ​ 可以把设计模式理解为一套比较成熟并且成体系的建筑图纸&#xff0c;经过多次编码检验目前看来使用效果还不错的软件设计方案。适用的场景也比较广泛&#xff0c;在使用具体的设计模式之前先要学习软件设计的基础 “软件设计原则”&#xff0c;后面的23个设计模式都是…

Mybatis学习|多对一、一对多

有多个学生&#xff0c;没个学生都对应&#xff08;关联&#xff09;了一个老师&#xff0c;这叫&#xff08;多对一&#xff09; 对于每个老师而言&#xff0c;每个老师都有N个学生&#xff08;学生集合&#xff09;&#xff0c;这叫&#xff08;一对多&#xff09; 测试环境…

《TCP/IP网络编程》阅读笔记--Socket类型及协议设置

目录 1--协议的定义 2--Socket的创建 2-1--协议族&#xff08;Protocol Family&#xff09; 2-2--Socket类型&#xff08;Type&#xff09; 3--Linux下实现TCP Socket 3-1--服务器端 3-2--客户端 3-3--编译运行 4--Windows下实现 TCP Socket 4-1--TCP服务端 4-2--TC…

B. Consecutive Points Segment - 思维

分析&#xff1a; 思维还是不够发散&#xff0c;太容易一种方法走到死了&#xff0c;一直在模拟一直WA&#xff0c;看完题解发现一个数组的整段所有数组共同移动的距离最多只能是2&#xff0c;那么a[0]到a[n - 1]就是之间应该有多少个数&#xff0c;然后本来需要n个连续的数&am…

【vue2第十一章】v-model的原理详解 与 如何使用v-model对父子组件的value绑定 和修饰符.sync

v-model的原理详解 v-model的本质就是一个语法糖&#xff0c;实际上就是 :value"msg" 与 input"msg $event.target.value" 的简写。 :value"msg" 从数据单向绑定到input框&#xff0c;当data数据中的msg内容一旦改变&#xff0c;而input框数据…

yum 、rpm、yumdownloader、repotrack 学习笔记

1 Linux 包管理器概述 rpm的使用&#xff1a; rpm -ivh filename.rpm#这列出该packageName&#xff08;包名&#xff09;安装的所有文件列表。 rpm -ql packageName #查询已安装的该packageName的详细信息&#xff0c;包括版本、发布日期等。 rpm -qi packageName #列出该pac…

剑指 Offer 62. 圆圈中最后剩下的数字(简单)

题目&#xff1a; class Solution { public:int lastRemaining(int n, int m) {int pos 0;for(int i2;i<n;i){pos (posm)%i;}return pos;} };作者&#xff1a;想吃火锅的木易 链接&#xff1a;详细题解 来源&#xff1a;力扣&#xff08;LeetCode&#xff09;

哪些存储设备的数据需要注意,防止误删除或者格式化丢失?

以下是一些存储设备的数据要注意&#xff0c;防止误删除或者格式化丢失&#xff1a; 1.硬盘&#xff1a;存储重要数据时要备份&#xff0c;避免硬盘故障、误格式化等情况导致数据丢失。 2.USB闪存驱动器&#xff1a;在拔出USB闪存驱动器前&#xff0c;应该先进行“安全删除”…

Stable Diffuse 之 本地环境部署 WebUI 进行汉化操作

Stable Diffuse 之 本地环境部署 WebUI 进行汉化操作 目录 Stable Diffuse 之 本地环境部署 WebUI 进行汉化操作 一、简单介绍 二、汉化操作 附录&#xff1a; 一、Install from URL 中出现 Failed to connect to 127.0.0.1 port 7890: Connection refused 错误&#xf…

Nodejs 第十四章(process)

process 是Nodejs操作当前进程和控制当前进程的API&#xff0c;并且是挂载到globalThis下面的全局API API 介绍 1. process.arch 返回操作系统 CPU 架构 跟我们之前讲的os.arch 一样 arm、arm64、ia32、mips、mipsel、ppc、ppc64、s390、s390x、以及 x64 2. process.cwd() …

docker 笔记5:redis 集群分布式存储案例

尚硅谷Docker实战教程&#xff08;docker教程天花板&#xff09;_哔哩哔哩_bilibili 目录 1.cluster(集群)模式-docker版哈希槽分区进行亿级数据存储 1.1面试题 1.1.1 方案1 哈希取余分区 1.1.2 方案2 一致性哈希算法分区 原理 优点 一致性哈希算法的容错性 一致性…

恢复数据的利器:易我数据恢复终身技术版v16.2.0.0

EaseUS Data Recovery Wizard为全球提供数据恢复方案,用于误删数据数据,电脑误删文件恢复,格式化硬盘数据恢复,手机U盘数据恢复等,RAID磁盘阵列数据恢复,分区丢失及其它未知原因丢失的数据恢复,简单易用轻松的搞定数据恢复。 特点描述 - 易我数据恢复中文便携版&#xff0c;无…

一笑的大型连续剧之第一集

自我介绍 哈喽&#xff0c;大家好。首先在开篇之前我想先自己介绍一下&#xff0c;我叫一笑&#xff0c;大家也可以叫我小舒。是一名又菜又爱写代码的Java程序员。当然这个也是我目前的一个想法&#xff0c;可以后期也可能想着去写一下其他的语言。介绍完成之后也就是单纯的想…

VPG算法

VPG算法 前言 首先来看经典的策略梯度REINFORCE算法&#xff1a; 在REINFORCE中&#xff0c;每次采集一个episode的轨迹&#xff0c;计算每一步动作的回报 G t G_t Gt​&#xff0c;与动作概率对数相乘&#xff0c;作为误差反向传播&#xff0c;有以下几个特点&#xff1a; …

Linux命令200例:Dump用来做文件系统备份

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈优质新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0…

C. Queries for the Array - 思维

分析&#xff1a; 分析出现矛盾的地方&#xff0c;也就是可能遇到0&#xff0c;并且已有字符串的长度小于等于1&#xff0c;另一种情况就是&#xff0c;遇到了1并且已有字符串不是排好序的&#xff0c;或者遇到了0已有字符串是排好序的&#xff0c;那么可以遍历字符串&#xff…

Arthas教程 - 安装篇 (一)

目录 一、在线安装 1.1 Windows安装 1.2 小结 1.3 linux安装 二、离线安装 一、在线安装 1.1 Windows安装 本地指定目录下&#xff08;例如我是&#xff1a;F:\测试\arthas&#xff09;&#xff0c;使用Windows的cmd窗口&#xff0c;执行如下命令则会将jar包下载下来。大…

STM32f103入门(10)ADC模数转换器

ADC模数转换器 ADC简介AD单通道初始化代码编写第一步开启时钟第二步 RCCCLK分频 6分频 72M/612M第三步 配置GPIO 配置为AIN状态第四步&#xff0c;选择规则组的输入通道第五步 用结构体 初始化ADC第六步 对ADC进行校准编写获取电压函数初始化代码如下 Main函数编写 ADC简介 ADC…