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

【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/";// 获取唯一idString id = UUID.randomUUID().toString();//获取文件后缀String suffix = originName.substring(originName.lastIndexOf("."));//拼接path += id + suffix;return path;}
}
  1. controller层,处理请求,调用service层进行数据持久化
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate 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*/@Overridepublic 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"><inputid="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,并返回查询的 valueif(paramArray.length>=1){for(var i=0;i<paramArray.length;i++){// key=valuevar 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.rhkb.cn/news/128781.html

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

相关文章

MySQL的常用术语

目录 1.关系 2.元组 3.属性 MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 1.关系 前面的博客有说到,MySQL是一款关系型数据库管理软件,一个关系就是 一张二维表(表) 我想大家都知道表格怎么…

sqli-labs闯关

目录 less-01: less-08: less-19: less-20: 项目地址—Github 使用HackBar插件 less-01: Sqli-labs前20关均为数字型注入 Sqli-labs前四关较为类似以less-01为模板 将网址导入HackBar中&#xff1a; 1.根据提示&#xff0c;输入http://127.0.0.1/sqli/Less-1/?id1查看…

算法[动态规划]---买卖股票最佳时机

1、题目&#xff1a; 给你一个整数数组 prices&#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候最多只能持一股股票。你也可以先购买&#xff0c;然后在同一天出售。 返回你能获得的最大利润 。 2…

9. xaml ComboBox控件

1.运行图像 2.运行源码 a.Xaml源码 <Grid Name="Grid1"><!--IsDropDownOpen="True" 默认就是打开的--><ComboBox x:Name="co

完成Centos上使用SSH公钥进行免密上传文件到gitee的步骤后,测试免密推送到gitee的时候还是需要输入邮箱和密码

如果你已经按照正确的步骤设置了SSH公钥并进行了免密测试&#xff0c;但仍然需要输入邮箱地址和密码才能推送到gitee&#xff0c;那么可能有以下几种原因&#xff1a; 您可能没有使用SSH URL来推送代码。请确保您使用的是SSH URL而不是HTTPS URL来推送代码。您可以使用命令 gi…

电商3D资产优化管线的自动化

如果你曾经尝试将从 CAD 程序导出的 3D 模型上传到 WebGL 或 AR 服务&#xff0c;那么可能会遇到最大文件大小、永无休止的进度条和糟糕的帧速率等问题。 为了创作良好的在线交互体验&#xff0c;优化 3D 数据的大小和性能至关重要。 这也有利于你的盈利&#xff0c;因为较小的…

GDB用法(三)

预备 测试代码参照GDB用法(二) 命令历史 可以将命令历史保存到文件中 (show history) 展示当前gdb中history的设置信息 设置expansion (set history expansion) 打开历史扩展 能使用历史处理命令对历史数据进行处理, 暂不细究 (show history expansion) 展示历史扩展配置…

什么是原生IP?原生IP与住宅IP有何区别?

相信许多做跨境的都会接触到IP代理&#xff0c;比如电商平台、社媒平台、收款平台等等&#xff0c;都会检测IP。那也会经常听到一些词汇&#xff1a;原生IP、住宅IP&#xff0c;这两者之间有什么区别呢&#xff1f;什么业务需要用到呢&#xff1f;接下来带大家具体了解一下。 什…

软件架构设计(十三) 构件与中间件技术

中间件的定义 其实中间件是属于构件的一种。是一种独立的系统软件或服务程序,可以帮助分布式应用软件在不同技术之间共享资源。 我们把它定性为一类系统软件,比如我们常说的消息中间件,数据库中间件等等都是中间件的一种体现。一般情况都是给应用系统提供服务,而不是直接…

Jenkins介绍

Jenkins介绍 持续集成、持续部署的工具很多&#xff0c;其中Jenkins是一个开源的持续集成平台。 Jenkins涉及到将编写完毕的代码发布到测试环境和生产环境的任务&#xff0c;并且还涉及到了构建项目等任务。 Jenkins需要大量的插件保证工作&#xff0c;安装成本较高&#xff0…

MyBatis框架中各种参数类型绑定的方式

MyBatis框架中各种参数类型绑定的方式 一、MyBatis参数绑定 MyBatis框架中&#xff0c;通过Mapper接口和Mapper映射文件的方式来操作数据库的时候&#xff0c;可能需要通过Mapper接口中的方法传递相应的参数拼接到SQL语句上面&#xff0c;那么Mybatis将传递的参数映射到对应S…

Debian11安装MySQL8.0,链接Navicat

图文小白教程 1 下载安装MySQL1.1 从MySQL官网下载安装文件1.2 安装MySQL1.3 登录MySQL 2 配置Navicat远程访问2.1 修改配置2.2 Navicat 连接 end: 卸载 MySQL 记录于2023年9月&#xff0c;Debian11 、 MySQL 8.0.34 1 下载安装MySQL 1.1 从MySQL官网下载安装文件 打开 MySQ…

FFmpeg入门之FFmpeg源码编译

1.源码下载: git clone https://github.com/FFmpeg/FFmpeg.git windows : macos: ubuntu: 2.编译FFmpeg CompilationGuide – FFmpeg windows: 1.下载yasm并安装 : Download - The Yasm Modular Assembler Project 下载后复制到c:/windows 2.下载SDL 3.下载H264/265源码 git…

stringBuffer.append(“字符串参数“);这个在字符串参数后添加空格怎么写

stringBuffer.append(“字符串参数”);这个在字符串参数后添加空格怎么写&#xff1f; 要在字符串参数后添加空格&#xff0c;可以直接在字符串参数的末尾使用空格字符&#xff0c;像这样&#xff1a; stringBuffer.append("字符串参数 ");这样就在字符串参数后添加…

【HTML5高级第三篇】drag拖拽、音频视频、defer/async属性、dialog应用

文章目录 一、拖拽事件1.1 拖拽事件1.2 案例&#xff1a;拖拽丢弃图片 二、音频和视频三、defer 与 async 属性3.1 概述3.2 示例一&#xff1a;3.3 示例二&#xff1a; 四、dialog 元素 一、拖拽事件 原生JavaScipt案例合集 JavaScript DOM基础 JavaScript 基础到高级 Canvas…

637. 二叉树的层平均值

637. 二叉树的层平均值 题目-简单难度示例1. bfs 题目-简单难度 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 示例 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&…

爬虫逆向实战(29)-某蜂窝详情页(cookie、混淆、MD5、SHA)

一、数据接口分析 主页地址&#xff1a;某蜂窝 1、抓包 通过抓包可以发现数据是静态的&#xff0c;在html中。 2、判断是否有加密参数 请求参数是否加密&#xff1f; 无请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无cookie是否加密&#xff1f; 通过查看“c…

Windows wsl2安装Ubuntu

wsl&#xff08;Windows Subsystem for Linux&#xff09;即适用于Windows的Linux子系统&#xff0c;是一个实现在Windows 10 / 11上运行原生Linux的技术。 wsl2 为其迭代版本&#xff0c;可以更好的在Windows上运行Linux子系统。 这里以 Windows 11 安装Ubuntu作为示例。 开启…

kafka学习-消费者

目录 1、消费者、消费组 2、心跳机制 3、消费者常见参数配置 4、订阅 5、反序列化 基本概念 自定义反序列化器 6、位移提交 6.1、自动提交 6.2、手动提交 同步提交 异步提交 7、再均衡 7.1、定义与基本概念 7.2、缺陷 7.3、如何避免再均衡 7.4、如何进行组内分…

电脑同时连接有线和无线网络怎么设置网络的优先级

电脑同时连接有线和无线网络怎么设置网络的优先级&#xff1a; 我们知道在 笔记本电脑系统 中&#xff0c;可以通过有线或无线网络进行联网。如果电脑在有线网络和无线网络同时存在的情况&#xff0c;应该怎么设置有线网络优先连接呢?对此我们提供下面的方法可以让电脑在有Wi…